cache_fu 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +18 -0
- data/README +18 -0
- data/defaults/extensions.rb.default +40 -0
- data/defaults/memcached.yml.default +34 -0
- data/defaults/memcached_ctl.default +81 -0
- data/lib/acts_as_cached/benchmarking.rb +82 -0
- data/lib/acts_as_cached/cache_methods.rb +307 -0
- data/lib/acts_as_cached/config.rb +111 -0
- data/lib/acts_as_cached/disabled.rb +30 -0
- data/lib/acts_as_cached/fragment_cache.rb +102 -0
- data/lib/acts_as_cached/local_cache.rb +44 -0
- data/lib/acts_as_cached/railtie.rb +13 -0
- data/lib/acts_as_cached/recipes.rb +8 -0
- data/lib/cache_fu.rb +62 -0
- data/lib/cache_fu.rb~ +61 -0
- data/lib/cache_fu.rb~5453d1247ce57bb1ea5ad70936b97707d5dbdab0 +1 -0
- data/lib/cache_fu.rb~5453d1247ce57bb1ea5ad70936b97707d5dbdab0_0 +1 -0
- data/lib/tasks/memcached.rake +59 -0
- data/rails/init.rb +34 -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 +103 -0
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
|