Class: FatCache

Inherits:
Object
  • Object
show all
Defined in:
lib/fat_cache.rb,
lib/fat_cache/shortcuts.rb

Defined Under Namespace

Modules: Shortcuts Classes: AmbiguousHit, CacheMiss

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.fatcacheObject

Returns the value of attribute fatcache.



10
11
12
# File 'lib/fat_cache.rb', line 10

def fatcache
  @fatcache
end

.fetchersObject

Returns the value of attribute fetchers.



11
12
13
# File 'lib/fat_cache.rb', line 11

def fetchers
  @fetchers
end

.index_fetchersObject

Returns the value of attribute index_fetchers.



11
12
13
# File 'lib/fat_cache.rb', line 11

def index_fetchers
  @index_fetchers
end

.indexed_fatcacheObject

Returns the value of attribute indexed_fatcache.



10
11
12
# File 'lib/fat_cache.rb', line 10

def indexed_fatcache
  @indexed_fatcache
end

.loggerObject

Returns the value of attribute logger.



13
14
15
# File 'lib/fat_cache.rb', line 13

def logger
  @logger
end

Class Method Details

.cached?(key) ⇒ Boolean

Returns:

  • (Boolean)


161
162
163
# File 'lib/fat_cache.rb', line 161

def cached?(key)
  fatcache && fatcache.has_key?(key)
end

.dumpObject



123
124
125
126
127
128
# File 'lib/fat_cache.rb', line 123

def dump
  Marshal.dump({
    :fatcache => self.fatcache,
    :indexed_fatcache => self.indexed_fatcache
  })
end

.ensure_cached(key) ⇒ Object



149
150
151
# File 'lib/fat_cache.rb', line 149

def ensure_cached(key)
  raise "no data in cache for #{key}" unless cached?(key)
end

.ensure_data_available_for(key) ⇒ Object



145
146
147
# File 'lib/fat_cache.rb', line 145

def ensure_data_available_for(key)
  raise "no data available for #{key}" unless cached?(key) || fetchable?(key)
end

.ensure_fetchable(key) ⇒ Object



157
158
159
# File 'lib/fat_cache.rb', line 157

def ensure_fetchable(key)
  raise "cannot fetch for #{key}" unless fetchable?(key)
end

.ensure_indexed(key, on) ⇒ Object



153
154
155
# File 'lib/fat_cache.rb', line 153

def ensure_indexed(key, on)
  raise "no index for #{key} on #{on.inspect}" unless indexed?(key, on)
end

.fetch!(key) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/fat_cache.rb', line 89

def fetch!(key)
  ensure_fetchable(key)
  if logger
    start_time = Time.now
    logger.info "[fatcache] <#{key}> fetching ..."
  end
  fatcache[key] = fetchers[key].call(self)
  if logger
    took = Time.now - start_time
    logger.info "[fatcache] <#{key}> done! #{(took / 60).floor} minutes, #{(took % 60).floor} seconds"
  end
end

.fetch_index!(key, on) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/fat_cache.rb', line 102

def fetch_index!(key, on)
  on = [*on]

  index(key, on) unless index_defined?(key, on)

  # init hash if we've never indexed for this key before
  indexed_fatcache[key] = {} unless indexed_fatcache.has_key?(key)

  raw_data = get(key)

  if logger
    start_time = Time.now
    logger.info "[fatcache] <#{key}> indexing on #{on.inspect} ..."
  end
  indexed_fatcache[key][on] = index_fetchers[key][on].call(raw_data)
  if logger
    took = Time.now - start_time
    logger.info "[fatcache] <#{key}> indexing on #{on.inspect} done! #{(took / 60).floor} minutes, #{(took % 60).floor} seconds"
  end
end

.fetchable?(key) ⇒ Boolean

Returns:

  • (Boolean)


171
172
173
# File 'lib/fat_cache.rb', line 171

def fetchable?(key)
  fetchers && fetchers.has_key?(key)
end

.get(key) ⇒ Object

Gets from cache or pays fetch cost if necessary to get, raises if none available



24
25
26
27
28
29
30
31
32
33
# File 'lib/fat_cache.rb', line 24

def get(key)
  unless cached?(key)
    if fetchable?(key)
      fetch!(key)
    else
      raise CacheMiss, "no data for #{key}" 
    end
  end
  fatcache[key]
end

.get_index(key, on) ⇒ Object



137
138
139
140
141
142
143
# File 'lib/fat_cache.rb', line 137

def get_index(key, on)
  on = [*on]

  ensure_indexed(key, on)

  indexed_fatcache[key][on]
end

.index(key, on, &block) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/fat_cache.rb', line 59

def index(key, on, &block)
  on = [*on] # ensure we're dealing with an array, we're such a friendly API!

  ensure_data_available_for(key)

  index_fetchers[key] = {} unless index_fetchers.has_key?(key)

  if block
    # make the cache available to the passed block, and ensure that
    # the returned value is wrapped in an array (to keep these keys in line
    # with the method sending strategy
    wrapped_block = lambda { |item| [*block.call(self, item)] }

    # pass each element of the raw data set into the block, which will
    # compute the key for us
    index_fetchers[key][on] = lambda { |data| data.group_by(&wrapped_block) }
  else
    # call each method specified in the `on` array once on each element in
    # the raw dataset, and use the results of those calls to key this index
    index_fetchers[key][on] = lambda { |data| data.group_by { |x| on.map { |b| x.send(b) } } }
  end
end

.index_defined?(key, on) ⇒ Boolean

Returns:

  • (Boolean)


175
176
177
178
179
# File 'lib/fat_cache.rb', line 175

def index_defined?(key, on)
  index_fetchers                   && 
  index_fetchers.has_key?(key)     &&
  index_fetchers[key].has_key?(on)
end

.indexed?(key, on) ⇒ Boolean

Returns:

  • (Boolean)


165
166
167
168
169
# File 'lib/fat_cache.rb', line 165

def indexed?(key, on)
  indexed_fatcache                   && 
  indexed_fatcache.has_key?(key)     &&
  indexed_fatcache[key].has_key?(on)
end

.invalidate!(key) ⇒ Object



82
83
84
85
86
87
# File 'lib/fat_cache.rb', line 82

def invalidate!(key)
  return unless cached?(key)
  
  indexed_fatcache.delete(key)
  fatcache.delete(key)
end

.load!(datastring) ⇒ Object



130
131
132
133
134
135
# File 'lib/fat_cache.rb', line 130

def load!(datastring)
  init unless initted?
  data = Marshal.load(datastring)
  self.fatcache         = data[:fatcache]
  self.indexed_fatcache = data[:indexed_fatcache]
end

.lookup(key, options = {}) ⇒ Object



35
36
37
38
39
40
41
42
43
# File 'lib/fat_cache.rb', line 35

def lookup(key, options={})
  options = options.dup
  by      = [*options.delete(:by)]
  using   = [*options.delete(:using)]
  
  fetch_index!(key, by) unless indexed?(key, by)

  indexed_fatcache[key][by][using]
end

.one(key, spec = {}) ⇒ Object



45
46
47
48
49
50
51
# File 'lib/fat_cache.rb', line 45

def one(key, spec={})
  result = lookup(key, :by => spec.keys, :using => spec.values)
  if result && result.length > 1
    raise AmbiguousHit, "expected one record for #{key} with #{spec.inspect}, got #{result.inspect}" 
  end
  result.first if result # makes us return nil if result is nil
end

.one!(key, spec = {}) ⇒ Object

Raises:



53
54
55
56
57
# File 'lib/fat_cache.rb', line 53

def one!(key, spec={})
  result = one(key, spec)
  raise CacheMiss, "could not find #{key} with #{spec.inspect}" unless result
  result
end

.reset!Object



181
182
183
184
185
186
187
# File 'lib/fat_cache.rb', line 181

def reset!
  self.fetchers         = nil
  self.index_fetchers   = nil
  self.fatcache         = nil
  self.indexed_fatcache = nil
  @initted              = nil
end

.set(key, &fetcher) ⇒ Object

Simply store value as key



16
17
18
19
20
# File 'lib/fat_cache.rb', line 16

def set(key, &fetcher)
  init unless initted? # for first time set

  fetchers[key] = fetcher
end