fat_cache 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/VERSION +1 -1
- data/lib/fat_cache.rb +56 -39
- data/spec/fat_cache_spec.rb +63 -50
- metadata +2 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
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 :
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
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
|
-
|
43
|
-
|
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
|
-
#
|
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
|
-
|
56
|
-
indexed_fatcache[key] = {} unless indexed_fatcache.has_key?(key)
|
43
|
+
ensure_fetchable(key)
|
57
44
|
|
58
|
-
|
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
|
-
|
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
|
-
|
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
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
data/spec/fat_cache_spec.rb
CHANGED
@@ -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
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
48
|
-
|
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
|
-
|
51
|
-
|
52
|
-
|
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
|
58
|
-
FatCache.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
147
|
-
|
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 '
|
152
|
-
|
153
|
-
FatCache.
|
154
|
-
|
155
|
-
|
156
|
-
|
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.
|
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.
|
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-
|
12
|
+
date: 2010-03-05 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|