fat_cache 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/VERSION +1 -1
  2. data/lib/fat_cache.rb +56 -39
  3. data/spec/fat_cache_spec.rb +63 -50
  4. metadata +2 -2
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.5
1
+ 0.0.6
data/lib/fat_cache.rb CHANGED
@@ -4,29 +4,22 @@ class FatCache
4
4
 
5
5
  class << self
6
6
  @initted = false
7
- attr_accessor :fetchers, :fatcache, :indexed_fatcache
8
-
9
- def store(*key_and_maybe_data, &fetcher)
10
- init unless initted? # for first time store
11
-
12
- if key_and_maybe_data.length == 2 && fetcher.nil?
13
- key, data = key_and_maybe_data
14
- fatcache[key] = data
15
- elsif key_and_maybe_data.length == 1 && fetcher
16
- key = key_and_maybe_data.first
17
- fetchers[key] = fetcher
18
- fatcache[key] = fetcher.call
19
- else
20
- argstr = "#{key_and_maybe_data.length} arguments"
21
- blockstr = (block_given?) ? 'a block' : 'no block'
22
- raise "Got #{argstr} and #{blockstr}, expected (key, data) or (key) { fetcher block }"
23
- end
7
+ attr_accessor :fatcache, :indexed_fatcache
8
+ attr_accessor :fetchers, :index_fetchers
9
+
10
+ # Simply store value as key
11
+ def set(key, &fetcher)
12
+ init unless initted? # for first time set
13
+
14
+ fetchers[key] = fetcher
24
15
  end
25
16
 
17
+ # Gets from cache or pays fetch cost if necessary to get, raises if none
18
+ # available
26
19
  def get(key)
27
20
  unless cached?(key)
28
21
  if fetchable?(key)
29
- fatcache[key] = fetchers[key].call
22
+ fetch!(key)
30
23
  else
31
24
  raise "no data for #{key}"
32
25
  end
@@ -39,23 +32,17 @@ class FatCache
39
32
  by = [*options.delete(:by)]
40
33
  using = [*options.delete(:using)]
41
34
 
42
- # create index if it doesn't exist
43
- index(key, by) unless indexed?(key, by)
44
-
45
- return indexed_fatcache[key][by][using]
35
+ fetch_index!(key, by) unless indexed?(key, by)
36
+
37
+ indexed_fatcache[key][by][using]
46
38
  end
47
39
 
48
40
  def index(key, on, &block)
49
- # must have cache data to work with
50
- ensure_cached(key)
51
-
52
- # ensure we're dealing with an array, we're such a friendly API!
53
- on = [*on]
41
+ on = [*on] # ensure we're dealing with an array, we're such a friendly API!
54
42
 
55
- # init hash if we've never indexed for this key before
56
- indexed_fatcache[key] = {} unless indexed_fatcache.has_key?(key)
43
+ ensure_fetchable(key)
57
44
 
58
- raw_data = get(key)
45
+ index_fetchers[key] = {} unless index_fetchers.has_key?(key)
59
46
 
60
47
  if block
61
48
  # make the cache available to the passed block, and ensure that
@@ -65,14 +52,39 @@ class FatCache
65
52
 
66
53
  # pass each element of the raw data set into the block, which will
67
54
  # compute the key for us
68
- indexed_fatcache[key][on] = raw_data.group_by(&wrapped_block)
55
+ index_fetchers[key][on] = lambda { |data| data.group_by(&wrapped_block) }
69
56
  else
70
57
  # call each method specified in the `on` array once on each element in
71
58
  # the raw dataset, and use the results of those calls to key this index
72
- indexed_fatcache[key][on] = raw_data.group_by { |x| on.map { |b| x.send(b) } }
59
+ index_fetchers[key][on] = lambda { |data| data.group_by { |x| on.map { |b| x.send(b) } } }
73
60
  end
74
61
  end
75
62
 
63
+ def invalidate!(key)
64
+ return unless cached?(key)
65
+
66
+ indexed_fatcache.delete(key)
67
+ fatcache.delete(key)
68
+ end
69
+
70
+ def fetch!(key)
71
+ ensure_fetchable(key)
72
+ fatcache[key] = fetchers[key].call(self)
73
+ end
74
+
75
+ def fetch_index!(key, on)
76
+ on = [*on]
77
+
78
+ index(key, on) unless index_defined?(key, on)
79
+
80
+ # init hash if we've never indexed for this key before
81
+ indexed_fatcache[key] = {} unless indexed_fatcache.has_key?(key)
82
+
83
+ raw_data = get(key)
84
+
85
+ indexed_fatcache[key][on] = index_fetchers[key][on].call(raw_data)
86
+ end
87
+
76
88
  def get_index(key, on)
77
89
  on = [*on]
78
90
 
@@ -82,14 +94,17 @@ class FatCache
82
94
  end
83
95
 
84
96
  def ensure_cached(key)
85
- raise "no data for #{key}" unless cached?(key)
97
+ raise "no data in cache for #{key}" unless cached?(key)
86
98
  end
87
99
 
88
100
  def ensure_indexed(key, on)
89
- ensure_cached(key)
90
101
  raise "no index for #{key} on #{on.inspect}" unless indexed?(key, on)
91
102
  end
92
103
 
104
+ def ensure_fetchable(key)
105
+ raise "cannot fetch for #{key}" unless fetchable?(key)
106
+ end
107
+
93
108
  def cached?(key)
94
109
  fatcache && fatcache.has_key?(key)
95
110
  end
@@ -103,15 +118,16 @@ class FatCache
103
118
  def fetchable?(key)
104
119
  fetchers && fetchers.has_key?(key)
105
120
  end
106
-
107
- def invalidate(key)
108
- init unless initted?
109
-
110
- fatcache.delete(key)
121
+
122
+ def index_defined?(key, on)
123
+ index_fetchers &&
124
+ index_fetchers.has_key?(key) &&
125
+ index_fetchers[key].has_key?(on)
111
126
  end
112
127
 
113
128
  def reset!
114
129
  self.fetchers = nil
130
+ self.index_fetchers = nil
115
131
  self.fatcache = nil
116
132
  self.indexed_fatcache = nil
117
133
  @initted = nil
@@ -121,6 +137,7 @@ class FatCache
121
137
 
122
138
  def init
123
139
  self.fetchers = {}
140
+ self.index_fetchers = {}
124
141
  self.fatcache = {}
125
142
  self.indexed_fatcache = {}
126
143
  @initted = true
@@ -5,63 +5,71 @@ describe FatCache do
5
5
  after { FatCache.reset! }
6
6
 
7
7
  describe 'get(key)' do
8
- it 'raises an exception if no data is stored for this key' do
8
+ it 'raises an exception if no data is set for this key' do
9
9
  lambda {
10
10
  FatCache.get(:not_there)
11
11
  }.should raise_error(/not_there/)
12
12
  end
13
13
 
14
- describe 'when data has been invalidated for a key with a fetcher' do
15
- it 'uses a fetcher to get new data' do
16
- i = 0
17
- FatCache.store(:increment) { i += 1 }
18
- FatCache.invalidate(:increment)
19
- FatCache.get(:increment).should == 2
20
- end
14
+ it 'uses specified fetcher to get new data' do
15
+ needy = mock('needy')
16
+ needy.should_receive(:called).once
17
+ FatCache.set(:call_me) { needy.called }
18
+ FatCache.get(:call_me)
21
19
  end
22
- end
23
20
 
24
- describe 'store(key[, data])' do
25
- describe 'when called with key and data arguments' do
26
- it 'stores data for specified key' do
27
- FatCache.store(:five_alive, 5)
28
- FatCache.get(:five_alive).should == 5
29
- end
21
+ it 'calls the block only once, caching the data returned' do
22
+ i = 0
23
+ FatCache.set(:increment) { i += 1 }
30
24
 
31
- it 'properly stores nil for key if explicitly specified' do
32
- FatCache.store(:empty_inside, nil)
33
- FatCache.get(:empty_inside).should be_nil
34
- end
25
+ first_result = FatCache.get(:increment)
26
+ second_result = FatCache.get(:increment)
27
+
28
+ first_result.should == 1
29
+ second_result.should == 1
35
30
  end
31
+ end
36
32
 
37
- describe 'when called with key and fetcher block' do
38
- it 'uses block as fetcher to retrieve data to store' do
39
- FatCache.store(:fetched_from_block) { 'cheese sandwich' }
40
- FatCache.get(:fetched_from_block).should == 'cheese sandwich'
41
- end
33
+ describe 'set(key) { fetcher_block }' do
34
+ it 'does not evaluate fetcher_block' do
35
+ independent = mock('independent')
36
+ independent.should_not_receive(:called)
37
+ FatCache.set(:dont_need_you) { independent.called }
38
+ end
42
39
 
43
- it 'calls the block only once, caching the data returned' do
44
- i = 0
45
- FatCache.store(:increment) { i += 1 }
40
+ it 'makes data available on get' do
41
+ FatCache.set(:fetched_from_block) { 'cheese sandwich' }
42
+ FatCache.get(:fetched_from_block).should == 'cheese sandwich'
43
+ end
44
+ end
46
45
 
47
- first_result = FatCache.get(:increment)
48
- second_result = FatCache.get(:increment)
46
+ describe 'fetch!(key)' do
47
+ it 'activates evaluation of a fetcher block' do
48
+ needy = mock('needy')
49
+ needy.should_receive(:called)
50
+ FatCache.set(:call_me) { needy.called }
51
+ FatCache.fetch!(:call_me)
52
+ end
49
53
 
50
- first_result.should == 1
51
- second_result.should == 1
52
- end
54
+ it 'prevents evaluation fetcher block in future gets' do
55
+ fool_me_once = stub('fool_me_once')
56
+ fool_me_once.should_receive(:fooled!).once
57
+
58
+ FatCache.set(:dont_fool_me_twice) { fool_me_once.fooled! }
59
+ FatCache.fetch!(:dont_fool_me_twice)
60
+ FatCache.get(:dont_fool_me_twice)
53
61
  end
54
62
  end
55
63
 
56
64
  describe 'lookup(key, :by => [:method_names], :using => [:index_key])' do
57
- it 'returns a records stored in the dataset specified by key, indexed by the specified methods, and with the following key to the index' do
58
- FatCache.store(:a_set, [0,1,2,3,4,5])
65
+ it 'returns a records set in the dataset specified by key, indexed by the specified methods, and with the following key to the index' do
66
+ FatCache.set(:a_set) { [0,1,2,3,4,5] }
59
67
  result = FatCache.lookup(:a_set, :by => :odd?, :using => true)
60
68
  result.should == [1,3,5]
61
69
  end
62
70
 
63
71
  it 'works with multi-element index keys' do
64
- FatCache.store(:a_set, [0,1,2,3,4,5])
72
+ FatCache.set(:a_set) { [0,1,2,3,4,5] }
65
73
  result = FatCache.lookup(:a_set, :by => [:even?, :zero?], :using => [true, true])
66
74
  result.should == [0]
67
75
  end
@@ -69,13 +77,14 @@ describe FatCache do
69
77
 
70
78
  describe 'get_index(key, on)' do
71
79
  it 'returns the given index for a key' do
72
- FatCache.store(:numbers, [0,1,2,3,4])
80
+ FatCache.set(:numbers) { [0,1,2,3,4] }
73
81
  FatCache.index(:numbers, :odd?)
82
+ FatCache.fetch_index!(:numbers, :odd?)
74
83
  FatCache.get_index(:numbers, :odd?).should be_a Hash
75
84
  end
76
85
 
77
86
  it 'raises an error if no index exists for on specified key' do
78
- FatCache.store(:indexed_one_way, [123])
87
+ FatCache.set(:indexed_one_way) { [123] }
79
88
  FatCache.index(:indexed_one_way, :zero?)
80
89
  lambda {
81
90
  FatCache.get_index(:indexed_one_way, :odd?)
@@ -91,9 +100,10 @@ describe FatCache do
91
100
  end
92
101
 
93
102
  it 'raises an error if the elements of the dataset do not respond to the index key methods' do
94
- FatCache.store(:numbers, [1,2,3,4,5,6])
103
+ FatCache.set(:numbers) { [1,2,3,4,5,6] }
95
104
  lambda {
96
105
  FatCache.index(:numbers, :millionaire?)
106
+ FatCache.fetch_index!(:numbers, :millionaire?)
97
107
  }.should raise_error(/millionaire?/)
98
108
  end
99
109
 
@@ -104,11 +114,12 @@ describe FatCache do
104
114
  stub(:banana, :grams_of_awesome => 10),
105
115
  stub(:apple, :grams_of_awesome => 3)
106
116
  ]
107
- FatCache.store(:fruit, @fruit)
117
+ FatCache.set(:fruit) { @fruit }
108
118
  end
109
119
 
110
120
  it 'calls each method specified in `on` array and uses the results as the index key' do
111
121
  FatCache.index(:fruit, [:grams_of_awesome])
122
+ FatCache.fetch_index!(:fruit, [:grams_of_awesome])
112
123
  index = FatCache.get_index(:fruit, :grams_of_awesome)
113
124
  index.keys.should =~ [[3],[10]]
114
125
  end
@@ -134,33 +145,35 @@ describe FatCache do
134
145
  it 'passes each item into the block as the second argument' do
135
146
  FatCache.get(:fruit).each { |f| f.should_receive(:seen) }
136
147
  FatCache.index(:fruit, :grams_of_awesome) { |_, item| item.seen }
148
+ FatCache.fetch_index!(:fruit, :grams_of_awesome)
137
149
  end
138
150
  end
139
151
  end
140
152
 
141
153
  end
142
154
 
143
- describe 'invalidate(key)' do
155
+ describe 'invalidate!(key)' do
144
156
  describe 'when no fetcher has been specified for key' do
145
157
  it 'returns the last value the cache had for the key' do
146
- FatCache.store(:once_upon_a_time, 33)
147
- retval = FatCache.invalidate(:once_upon_a_time)
158
+ FatCache.set(:once_upon_a_time) { 33 }
159
+ FatCache.get(:once_upon_a_time)
160
+ retval = FatCache.invalidate!(:once_upon_a_time)
148
161
  retval.should == 33
149
162
  end
150
163
 
151
- it 'removes data stored for a given key' do
152
- FatCache.store(:there_and_gone, 100)
153
- FatCache.invalidate(:there_and_gone)
154
- lambda {
155
- FatCache.get(:there_and_gone)
156
- }.should raise_error(/there_and_gone/)
164
+ it 'causes data to be retreived from the fetcher again' do
165
+ i = 0
166
+ FatCache.set(:increment) { i += 1 }
167
+ FatCache.get(:increment).should == 1
168
+ FatCache.invalidate!(:increment)
169
+ FatCache.get(:increment).should == 2
157
170
  end
158
171
  end
159
172
 
160
173
  describe 'when a fetcher has been specified for a key' do
161
174
  it 'does not clear out the fetcher, which can be used in the next lookup' do
162
- FatCache.store(:fetch_me) { "I've been fetched" }
163
- FatCache.invalidate(:fetch_me)
175
+ FatCache.set(:fetch_me) { "I've been fetched" }
176
+ FatCache.invalidate!(:fetch_me)
164
177
  FatCache.get(:fetch_me).should == "I've been fetched"
165
178
  end
166
179
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fat_cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - phinze
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-03-03 00:00:00 -06:00
12
+ date: 2010-03-05 00:00:00 -06:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency