revo-cache_fu 1.0.0
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/CHANGELOG +0 -0
- data/LICENSE +18 -0
- data/README +17 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/cache_fu.gemspec +77 -0
- data/defaults/extensions.rb.default +40 -0
- data/defaults/memcached.yml.default +34 -0
- data/defaults/memcached_ctl.default +81 -0
- data/init.rb +34 -0
- data/install.rb +58 -0
- data/lib/acts_as_cached/benchmarking.rb +87 -0
- data/lib/acts_as_cached/cache_methods.rb +305 -0
- data/lib/acts_as_cached/config.rb +97 -0
- data/lib/acts_as_cached/disabled.rb +30 -0
- data/lib/acts_as_cached/fragment_cache.rb +124 -0
- data/lib/acts_as_cached/local_cache.rb +44 -0
- data/lib/acts_as_cached/memcached_rails.rb +17 -0
- data/lib/acts_as_cached/recipes.rb +8 -0
- data/lib/acts_as_cached.rb +51 -0
- data/tasks/memcached.rake +43 -0
- data/test/benchmarking_test.rb +36 -0
- data/test/cache_test.rb +281 -0
- data/test/config_test.rb +128 -0
- data/test/disabled_test.rb +40 -0
- data/test/extensions_test.rb +33 -0
- data/test/fragment_cache_test.rb +250 -0
- data/test/helper.rb +215 -0
- data/test/local_cache_test.rb +43 -0
- data/test/sti_test.rb +36 -0
- metadata +92 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
namespace :memcached do
|
5
|
+
desc "Start memcached locally"
|
6
|
+
task :start do
|
7
|
+
memcached config_args
|
8
|
+
puts "memcached started"
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "Restart memcached locally"
|
12
|
+
task :restart do
|
13
|
+
Rake::Task['memcached:stop'].invoke
|
14
|
+
Rake::Task['memcached:start'].invoke
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Stop memcached locally"
|
18
|
+
task :stop do
|
19
|
+
`killall memcached`
|
20
|
+
puts "memcached killed"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def config
|
25
|
+
return @config if @config
|
26
|
+
config = YAML.load(ERB.new(IO.read(File.dirname(__FILE__) + '/../../../../config/memcached.yml')).result)
|
27
|
+
@config = config['defaults'].merge(config['development'])
|
28
|
+
end
|
29
|
+
|
30
|
+
def config_args
|
31
|
+
args = {
|
32
|
+
'-p' => Array(config['servers']).first.split(':').last,
|
33
|
+
'-c' => config['c_threshold'],
|
34
|
+
'-m' => config['memory'],
|
35
|
+
'-d' => ''
|
36
|
+
}
|
37
|
+
|
38
|
+
args.to_a * ' '
|
39
|
+
end
|
40
|
+
|
41
|
+
def memcached(*args)
|
42
|
+
`/usr/bin/env memcached #{args * ' '}`
|
43
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helper')
|
2
|
+
|
3
|
+
ActsAsCached.config.clear
|
4
|
+
config = YAML.load_file(File.join(File.dirname(__FILE__), '../defaults/memcached.yml.default'))
|
5
|
+
config['test'] = config['development']
|
6
|
+
ActsAsCached.config = config
|
7
|
+
Story.send :acts_as_cached
|
8
|
+
|
9
|
+
context "When benchmarking is enabled" do
|
10
|
+
specify "ActionController::Base should respond to rendering_runtime_with_memcache" do
|
11
|
+
ActionController::Base.new.should.respond_to :rendering_runtime_with_memcache
|
12
|
+
end
|
13
|
+
|
14
|
+
specify "cachable Ruby classes should be respond to :logger" do
|
15
|
+
Story.should.respond_to :logger
|
16
|
+
end
|
17
|
+
|
18
|
+
specify "a cached object should gain a fetch_cache with and without benchmarking methods" do
|
19
|
+
Story.should.respond_to :fetch_cache_with_benchmarking
|
20
|
+
Story.should.respond_to :fetch_cache_without_benchmarking
|
21
|
+
end
|
22
|
+
|
23
|
+
specify "cache_benchmark should yield and time any action" do
|
24
|
+
ActsAsCached::Benchmarking.cache_runtime.should.equal 0.0
|
25
|
+
|
26
|
+
level = Class.new { |k| def k.method_missing(*args) true end }
|
27
|
+
Story.stubs(:logger).returns(level)
|
28
|
+
|
29
|
+
Story.cache_benchmark("Seriously, nothing.", true) {
|
30
|
+
sleep 0.01
|
31
|
+
"Nothing."
|
32
|
+
}.should.equal "Nothing."
|
33
|
+
|
34
|
+
ActsAsCached::Benchmarking.cache_runtime.should.be > 0.0
|
35
|
+
end
|
36
|
+
end
|
data/test/cache_test.rb
ADDED
@@ -0,0 +1,281 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helper')
|
2
|
+
|
3
|
+
context "A Ruby class acting as cached (in general)" do
|
4
|
+
include StoryCacheSpecSetup
|
5
|
+
|
6
|
+
specify "should be able to retrieve a cached instance from the cache" do
|
7
|
+
Story.get_cache(1).should.equal Story.find(1)
|
8
|
+
end
|
9
|
+
|
10
|
+
specify "should set to the cache if its not already set when getting" do
|
11
|
+
Story.should.not.have.cached 1
|
12
|
+
Story.get_cache(1).should.equal Story.find(1)
|
13
|
+
Story.should.have.cached 1
|
14
|
+
end
|
15
|
+
|
16
|
+
specify "should not set to the cache if is already set when getting" do
|
17
|
+
Story.expects(:set_cache).never
|
18
|
+
Story.should.have.cached 2
|
19
|
+
Story.get_cache(2).should.equal Story.find(2)
|
20
|
+
Story.should.have.cached 2
|
21
|
+
end
|
22
|
+
|
23
|
+
specify "should be able to tell if a key is cached" do
|
24
|
+
Story.is_cached?(1).should.equal false
|
25
|
+
Story.should.not.have.cached 1
|
26
|
+
Story.should.have.cached 2
|
27
|
+
end
|
28
|
+
|
29
|
+
specify "should be able to cache arbitrary methods using #caches" do
|
30
|
+
Story.cache_store.expects(:get).returns(nil)
|
31
|
+
Story.cache_store.expects(:set).with('Story:something_cool', :redbull, 1500)
|
32
|
+
Story.caches(:something_cool).should.equal :redbull
|
33
|
+
|
34
|
+
Story.cache_store.expects(:get).returns(:redbull)
|
35
|
+
Story.cache_store.expects(:set).never
|
36
|
+
Story.caches(:something_cool).should.equal :redbull
|
37
|
+
end
|
38
|
+
|
39
|
+
specify "should be able to cache arbitrary methods with arguments using #caches and :with" do
|
40
|
+
with = :mongrel
|
41
|
+
|
42
|
+
Story.cache_store.expects(:get).returns(nil)
|
43
|
+
Story.cache_store.expects(:set).with("Story:block_on:#{with}", with, 1500)
|
44
|
+
Story.caches(:block_on, :with => with).should.equal with
|
45
|
+
|
46
|
+
Story.cache_store.expects(:get).with("Story:block_on:#{with}").returns(:okay)
|
47
|
+
Story.cache_store.expects(:set).never
|
48
|
+
Story.caches(:block_on, :with => with).should.equal :okay
|
49
|
+
end
|
50
|
+
|
51
|
+
specify "should be able to cache arbitrary methods with a nil argument using #caches and :with" do
|
52
|
+
with = nil
|
53
|
+
|
54
|
+
Story.cache_store.expects(:get).returns(nil)
|
55
|
+
Story.cache_store.expects(:set).with("Story:pass_through:#{with}", :_nil, 1500)
|
56
|
+
Story.caches(:pass_through, :with => with).should.equal with
|
57
|
+
end
|
58
|
+
|
59
|
+
specify "should be able to cache arbitrary methods with arguments using #caches and :withs" do
|
60
|
+
withs = [ :first, :second ]
|
61
|
+
|
62
|
+
cached_string = "first: #{withs.first} | second: #{withs.last}"
|
63
|
+
|
64
|
+
Story.cache_store.expects(:get).returns(nil)
|
65
|
+
Story.cache_store.expects(:set).with("Story:two_params:#{withs}", cached_string, 1500)
|
66
|
+
Story.caches(:two_params, :withs => withs).should.equal cached_string
|
67
|
+
|
68
|
+
Story.cache_store.expects(:get).with("Story:two_params:#{withs}").returns(:okay)
|
69
|
+
Story.cache_store.expects(:set).never
|
70
|
+
Story.caches(:two_params, :withs => withs).should.equal :okay
|
71
|
+
end
|
72
|
+
|
73
|
+
specify "should set nil when trying to set nil" do
|
74
|
+
Story.set_cache(3, nil).should.equal nil
|
75
|
+
Story.get_cache(3).should.equal nil
|
76
|
+
end
|
77
|
+
|
78
|
+
specify "should set false when trying to set false" do
|
79
|
+
Story.set_cache(3, false).should.equal false
|
80
|
+
Story.get_cache(3).should.equal false
|
81
|
+
end
|
82
|
+
|
83
|
+
specify "should be able to expire a cache key" do
|
84
|
+
Story.should.have.cached 2
|
85
|
+
Story.expire_cache(2).should.equal true
|
86
|
+
Story.should.not.have.cached 2
|
87
|
+
end
|
88
|
+
|
89
|
+
specify "should return true when trying to expire the cache" do
|
90
|
+
Story.should.not.have.cached 1
|
91
|
+
Story.expire_cache(1).should.equal true
|
92
|
+
Story.should.have.cached 2
|
93
|
+
Story.expire_cache(2).should.equal true
|
94
|
+
end
|
95
|
+
|
96
|
+
specify "should be able to reset a cache key, returning the cached object if successful" do
|
97
|
+
Story.expects(:find).with(2).returns(@story2)
|
98
|
+
Story.should.have.cached 2
|
99
|
+
Story.reset_cache(2).should.equal @story2
|
100
|
+
Story.should.have.cached 2
|
101
|
+
end
|
102
|
+
|
103
|
+
specify "should be able to cache the value of a block" do
|
104
|
+
Story.should.not.have.cached :block
|
105
|
+
Story.get_cache(:block) { "this is a block" }
|
106
|
+
Story.should.have.cached :block
|
107
|
+
Story.get_cache(:block).should.equal "this is a block"
|
108
|
+
end
|
109
|
+
|
110
|
+
specify "should be able to define a class level ttl" do
|
111
|
+
ttl = 1124
|
112
|
+
Story.cache_config[:ttl] = ttl
|
113
|
+
Story.cache_config[:store].expects(:set).with(Story.cache_key(1), @story, ttl)
|
114
|
+
Story.get_cache(1)
|
115
|
+
end
|
116
|
+
|
117
|
+
specify "should be able to define a per-key ttl" do
|
118
|
+
ttl = 3262
|
119
|
+
Story.cache_config[:store].expects(:set).with(Story.cache_key(1), @story, ttl)
|
120
|
+
Story.get_cache(1, :ttl => ttl)
|
121
|
+
end
|
122
|
+
|
123
|
+
specify "should be able to skip cache gets" do
|
124
|
+
Story.should.have.cached 2
|
125
|
+
ActsAsCached.skip_cache_gets = true
|
126
|
+
Story.expects(:find).at_least_once
|
127
|
+
Story.get_cache(2)
|
128
|
+
ActsAsCached.skip_cache_gets = false
|
129
|
+
end
|
130
|
+
|
131
|
+
specify "should be able to use an arbitrary finder method via :finder" do
|
132
|
+
Story.expire_cache(4)
|
133
|
+
Story.cache_config[:finder] = :find_live
|
134
|
+
Story.expects(:find_live).with(4).returns(false)
|
135
|
+
Story.get_cache(4)
|
136
|
+
end
|
137
|
+
|
138
|
+
specify "should raise an exception if no finder method is found" do
|
139
|
+
Story.cache_config[:finder] = :find_penguins
|
140
|
+
proc { Story.get_cache(1) }.should.raise(NoMethodError)
|
141
|
+
end
|
142
|
+
|
143
|
+
specify "should be able to use an abitrary cache_id method via :cache_id" do
|
144
|
+
Story.expire_cache(4)
|
145
|
+
Story.cache_config[:cache_id] = :title
|
146
|
+
story = Story.get_cache(1)
|
147
|
+
story.cache_id.should.equal story.title
|
148
|
+
end
|
149
|
+
|
150
|
+
specify "should modify its cache key to reflect a :version option" do
|
151
|
+
Story.cache_config[:version] = 'new'
|
152
|
+
Story.cache_key(1).should.equal 'Story:new:1'
|
153
|
+
end
|
154
|
+
|
155
|
+
specify "should truncate the key normally if we dont have a namespace" do
|
156
|
+
Story.stubs(:cache_namespace).returns(nil)
|
157
|
+
key = "a" * 260
|
158
|
+
Story.cache_key(key).length.should == 250
|
159
|
+
end
|
160
|
+
|
161
|
+
specify "should truncate key with length over 250, including namespace if set" do
|
162
|
+
Story.stubs(:cache_namespace).returns("37-power-moves-app" )
|
163
|
+
key = "a" * 260
|
164
|
+
(Story.cache_namespace + Story.cache_key(key)).length.should == (250 - 1)
|
165
|
+
end
|
166
|
+
|
167
|
+
specify "should raise an informative error message when trying to set_cache with a proc" do
|
168
|
+
Story.cache_config[:store].expects(:set).raises(TypeError.new("Can't marshal Proc"))
|
169
|
+
proc { Story.set_cache('proc:d', proc { nil }) }.should.raise(ActsAsCached::MarshalError)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
context "Passing an array of ids to get_cache" do
|
174
|
+
include StoryCacheSpecSetup
|
175
|
+
|
176
|
+
setup do
|
177
|
+
@grab_stories = proc do
|
178
|
+
@stories = Story.get_cache(1, 2, 3)
|
179
|
+
end
|
180
|
+
|
181
|
+
@keys = 'Story:1', 'Story:2', 'Story:3'
|
182
|
+
@hash = {
|
183
|
+
'Story:1' => nil,
|
184
|
+
'Story:2' => $stories[2],
|
185
|
+
'Story:3' => nil
|
186
|
+
}
|
187
|
+
|
188
|
+
# TODO: doh, probably need to clean this up...
|
189
|
+
@cache = $with_memcache ? CACHE : $cache
|
190
|
+
|
191
|
+
@cache.expects(:get_multi).with(*@keys).returns(@hash)
|
192
|
+
end
|
193
|
+
|
194
|
+
specify "should try to fetch those ids using get_multi" do
|
195
|
+
@grab_stories.call
|
196
|
+
|
197
|
+
@stories.size.should.equal 3
|
198
|
+
@stories.should.be.an.instance_of Hash
|
199
|
+
@stories.each { |id, story| story.should.be.an.instance_of Story }
|
200
|
+
end
|
201
|
+
|
202
|
+
specify "should pass the cache miss ids to #find" do
|
203
|
+
Story.expects(:find).with(%w(1 3)).returns($stories[1], $stories[3])
|
204
|
+
@grab_stories.call
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context "Passing an array of ids to get_cache using a cache which doesn't support get_multi" do
|
209
|
+
include StoryCacheSpecSetup
|
210
|
+
|
211
|
+
setup do
|
212
|
+
@grab_stories = proc do
|
213
|
+
@stories = Story.get_cache(1, 2, 3)
|
214
|
+
end
|
215
|
+
|
216
|
+
# TODO: doh, probably need to clean this up...
|
217
|
+
@cache = $with_memcache ? CACHE : $cache
|
218
|
+
end
|
219
|
+
|
220
|
+
specify "should raise an exception" do
|
221
|
+
class << @cache; undef :get_multi end
|
222
|
+
proc { @grab_stories.call }.should.raise(ActsAsCached::NoGetMulti)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
context "A Ruby object acting as cached" do
|
227
|
+
include StoryCacheSpecSetup
|
228
|
+
|
229
|
+
specify "should be able to retrieve a cached version of itself" do
|
230
|
+
Story.expects(:get_cache).with(1, {}).at_least_once
|
231
|
+
@story.get_cache
|
232
|
+
end
|
233
|
+
|
234
|
+
specify "should be able to set itself to the cache" do
|
235
|
+
Story.expects(:set_cache).with(1, @story, nil).at_least_once
|
236
|
+
@story.set_cache
|
237
|
+
end
|
238
|
+
|
239
|
+
specify "should cache the value of a passed block" do
|
240
|
+
@story.should.not.have.cached :block
|
241
|
+
@story.get_cache(:block) { "this is a block" }
|
242
|
+
@story.should.have.cached :block
|
243
|
+
@story.get_cache(:block).should.equal "this is a block"
|
244
|
+
end
|
245
|
+
|
246
|
+
specify "should allow setting custom options by passing them to get_cache" do
|
247
|
+
Story.expects(:set_cache).with('1:options', 'cached value', 1.hour)
|
248
|
+
@story.get_cache(:options, :ttl => 1.hour) { 'cached value' }
|
249
|
+
end
|
250
|
+
|
251
|
+
specify "should be able to expire its cache" do
|
252
|
+
Story.expects(:expire_cache).with(2)
|
253
|
+
@story2.expire_cache
|
254
|
+
end
|
255
|
+
|
256
|
+
specify "should be able to reset its cache" do
|
257
|
+
Story.expects(:reset_cache).with(2)
|
258
|
+
@story2.reset_cache
|
259
|
+
end
|
260
|
+
|
261
|
+
specify "should be able to tell if it is cached" do
|
262
|
+
@story.should.not.be.cached
|
263
|
+
@story2.should.be.cached
|
264
|
+
end
|
265
|
+
|
266
|
+
specify "should be able to set itself to the cache with an arbitrary ttl" do
|
267
|
+
ttl = 1500
|
268
|
+
Story.expects(:set_cache).with(1, @story, ttl)
|
269
|
+
@story.set_cache(ttl)
|
270
|
+
end
|
271
|
+
|
272
|
+
specify "should be able to cache arbitrary instance methods using caches" do
|
273
|
+
Story.cache_store.expects(:get).returns(nil)
|
274
|
+
Story.cache_store.expects(:set).with('Story:1:something_flashy', :molassy, 1500)
|
275
|
+
@story.caches(:something_flashy).should.equal :molassy
|
276
|
+
|
277
|
+
Story.cache_store.expects(:get).returns(:molassy)
|
278
|
+
Story.cache_store.expects(:set).never
|
279
|
+
@story.caches(:something_flashy).should.equal :molassy
|
280
|
+
end
|
281
|
+
end
|
data/test/config_test.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helper')
|
2
|
+
|
3
|
+
context "The global cache configuration" do
|
4
|
+
# Pass in a hash to update the config.
|
5
|
+
# If the first arg is a symbol, an expectation will be set.
|
6
|
+
def setup_config(*args)
|
7
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
8
|
+
@config[RAILS_ENV].update options.stringify_keys
|
9
|
+
ActsAsCached::Config.expects(args.first) if args.first.is_a? Symbol
|
10
|
+
ActsAsCached.config = @config
|
11
|
+
end
|
12
|
+
|
13
|
+
setup do
|
14
|
+
ActsAsCached.config.clear
|
15
|
+
@config = YAML.load_file('defaults/memcached.yml.default')
|
16
|
+
@config['test'] = @config['development'].merge('benchmarking' => false, 'disabled' => false)
|
17
|
+
end
|
18
|
+
|
19
|
+
specify "should be able to set itself as the session store" do
|
20
|
+
setup_config :setup_session_store, :sessions => true
|
21
|
+
end
|
22
|
+
|
23
|
+
specify "should be able to set itself as the fragment store" do
|
24
|
+
setup_config :setup_fragment_store!, :fragments => true
|
25
|
+
end
|
26
|
+
|
27
|
+
specify "should construct a namespace from the environment and a config value" do
|
28
|
+
setup_config
|
29
|
+
ActsAsCached.config[:namespace].should.equal "app-#{RAILS_ENV}"
|
30
|
+
end
|
31
|
+
|
32
|
+
specify "should be able to set a global default ttl" do
|
33
|
+
setup_config
|
34
|
+
Story.send :acts_as_cached
|
35
|
+
ActsAsCached.config[:ttl].should.not.be.nil
|
36
|
+
Story.cache_config[:ttl].should.equal ActsAsCached.config[:ttl]
|
37
|
+
end
|
38
|
+
|
39
|
+
specify "should be able to swallow errors" do
|
40
|
+
setup_config :raise_errors => false
|
41
|
+
Story.send :acts_as_cached
|
42
|
+
Story.stubs(:find).returns(Story.new)
|
43
|
+
Story.cache_config[:store].expects(:get).raises(MemCache::MemCacheError)
|
44
|
+
Story.cache_config[:store].expects(:set).returns(true)
|
45
|
+
proc { Story.get_cache(1) }.should.not.raise(MemCache::MemCacheError)
|
46
|
+
end
|
47
|
+
|
48
|
+
specify "should not swallow marshal errors" do
|
49
|
+
setup_config :raise_errors => false
|
50
|
+
Story.send :acts_as_cached
|
51
|
+
Story.stubs(:find).returns(Story.new)
|
52
|
+
Story.cache_config[:store].expects(:get).returns(nil)
|
53
|
+
Story.cache_config[:store].expects(:set).raises(TypeError.new("Some kind of Proc error"))
|
54
|
+
proc { Story.get_cache(1) }.should.raise(ActsAsCached::MarshalError)
|
55
|
+
end
|
56
|
+
|
57
|
+
specify "should be able to re-raise errors" do
|
58
|
+
setup_config :raise_errors => true
|
59
|
+
Story.send :acts_as_cached
|
60
|
+
Story.cache_config[:store].expects(:get).raises(MemCache::MemCacheError)
|
61
|
+
proc { Story.get_cache(1) }.should.raise(MemCache::MemCacheError)
|
62
|
+
end
|
63
|
+
|
64
|
+
specify "should be able to enable benchmarking" do
|
65
|
+
setup_config :benchmarking => true
|
66
|
+
ActsAsCached.config[:benchmarking].should.equal true
|
67
|
+
Story.send :acts_as_cached
|
68
|
+
Story.methods.should.include 'fetch_cache_with_benchmarking'
|
69
|
+
end
|
70
|
+
|
71
|
+
specify "should be able to disable all caching" do
|
72
|
+
setup_config :disabled => true
|
73
|
+
Story.send :acts_as_cached
|
74
|
+
Story.should.respond_to :fetch_cache_with_disabled
|
75
|
+
ActsAsCached.config[:disabled].should.equal true
|
76
|
+
end
|
77
|
+
|
78
|
+
specify "should be able to use a global store other than memcache" do
|
79
|
+
setup_config :store => 'HashStore'
|
80
|
+
ActsAsCached.config[:store].should.equal HashStore.new
|
81
|
+
Story.send :acts_as_cached
|
82
|
+
Story.cache_config[:store].should.be ActsAsCached.config[:store]
|
83
|
+
end
|
84
|
+
|
85
|
+
specify "should be able to override the memcache-client hashing algorithm" do
|
86
|
+
setup_config :fast_hash => true
|
87
|
+
ActsAsCached.config[:fast_hash].should.equal true
|
88
|
+
CACHE.hash_for('eatingsnotcheating').should.equal 1919
|
89
|
+
end
|
90
|
+
|
91
|
+
specify "should be able to override the memcache-client hashing algorithm" do
|
92
|
+
setup_config :fastest_hash => true
|
93
|
+
ActsAsCached.config[:fastest_hash].should.equal true
|
94
|
+
CACHE.hash_for(string = 'eatingsnotcheating').should.equal string.hash
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
context "The class configuration" do
|
101
|
+
# Setups up the Story class with acts_as_cached using options
|
102
|
+
def setup_cached(options = {})
|
103
|
+
Story.send :acts_as_cached, options
|
104
|
+
end
|
105
|
+
|
106
|
+
specify "should save unknown keys as options and not config" do
|
107
|
+
setup_cached :pengiuns => true
|
108
|
+
Story.cache_options.should.include :pengiuns
|
109
|
+
Story.cache_config.should.not.include :pengiuns
|
110
|
+
end
|
111
|
+
|
112
|
+
specify "should be able to override the default finder" do
|
113
|
+
setup_cached :finder => :find_by_title
|
114
|
+
Story.cache_config[:finder].should.equal :find_by_title
|
115
|
+
end
|
116
|
+
|
117
|
+
specify "should be able to override the default cache_id" do
|
118
|
+
setup_cached :cache_id => :title
|
119
|
+
Story.cache_config[:cache_id].should.equal :title
|
120
|
+
end
|
121
|
+
|
122
|
+
specify "should be able to override the default finder and the cache_id using find_by" do
|
123
|
+
setup_cached :find_by => :title
|
124
|
+
Story.cache_config.should.not.include :find
|
125
|
+
Story.cache_config[:finder].should.equal :find_by_title
|
126
|
+
Story.cache_config[:cache_id].should.equal :title
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helper')
|
2
|
+
|
3
|
+
context "When the cache is disabled" do
|
4
|
+
setup do
|
5
|
+
@story = Story.new(:id => 1, :title => "acts_as_cached 2 released!")
|
6
|
+
@story2 = Story.new(:id => 2, :title => "BDD is something you can use")
|
7
|
+
$stories = { 1 => @story, 2 => @story2 }
|
8
|
+
|
9
|
+
config = YAML.load_file('defaults/memcached.yml.default')
|
10
|
+
config['test'] = config['development'].merge('disabled' => true, 'benchmarking' => false)
|
11
|
+
ActsAsCached.config = config
|
12
|
+
Story.send :acts_as_cached
|
13
|
+
end
|
14
|
+
|
15
|
+
specify "get_cache should call through to the finder" do
|
16
|
+
Story.expects(:find).at_least_once.returns(@story2)
|
17
|
+
@story2.get_cache.should.equal @story2
|
18
|
+
end
|
19
|
+
|
20
|
+
specify "expire_cache should return true" do
|
21
|
+
$cache.expects(:delete).never
|
22
|
+
@story2.expire_cache.should.equal true
|
23
|
+
end
|
24
|
+
|
25
|
+
specify "reset_cache should return the object" do
|
26
|
+
$cache.expects(:set).never
|
27
|
+
Story.expects(:find).at_least_once.returns(@story2)
|
28
|
+
@story2.reset_cache.should.equal @story2
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "set_cache should just return the object" do
|
32
|
+
$cache.expects(:set).never
|
33
|
+
@story2.set_cache.should.equal @story2
|
34
|
+
end
|
35
|
+
|
36
|
+
specify "cached? should return false" do
|
37
|
+
$cache.expects(:get).never
|
38
|
+
@story2.should.not.be.cached
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helper')
|
2
|
+
|
3
|
+
module ActsAsCached
|
4
|
+
module Extensions
|
5
|
+
module ClassMethods
|
6
|
+
def user_defined_class_method
|
7
|
+
true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
def user_defined_instance_method
|
13
|
+
true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "When Extensions::ClassMethods exists" do
|
20
|
+
include StoryCacheSpecSetup
|
21
|
+
|
22
|
+
specify "caching classes should extend it" do
|
23
|
+
Story.singleton_methods.should.include 'user_defined_class_method'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "When Extensions::InstanceMethods exists" do
|
28
|
+
include StoryCacheSpecSetup
|
29
|
+
|
30
|
+
specify "caching classes should include it" do
|
31
|
+
Story.instance_methods.should.include 'user_defined_instance_method'
|
32
|
+
end
|
33
|
+
end
|