nkallen-cache-money 0.2.1
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/README +1 -0
- data/TODO +20 -0
- data/UNSUPPORTED_FEATURES +14 -0
- data/config/environment.rb +6 -0
- data/config/memcache.yml +6 -0
- data/db/schema.rb +11 -0
- data/lib/cash.rb +53 -0
- data/lib/cash/accessor.rb +78 -0
- data/lib/cash/buffered.rb +126 -0
- data/lib/cash/config.rb +64 -0
- data/lib/cash/finders.rb +40 -0
- data/lib/cash/index.rb +207 -0
- data/lib/cash/local.rb +59 -0
- data/lib/cash/lock.rb +52 -0
- data/lib/cash/mock.rb +86 -0
- data/lib/cash/query/abstract.rb +162 -0
- data/lib/cash/query/calculation.rb +45 -0
- data/lib/cash/query/primary_key.rb +51 -0
- data/lib/cash/query/select.rb +16 -0
- data/lib/cash/transactional.rb +42 -0
- data/lib/cash/util/array.rb +9 -0
- data/lib/cash/write_through.rb +72 -0
- data/spec/cash/accessor_spec.rb +133 -0
- data/spec/cash/active_record_spec.rb +190 -0
- data/spec/cash/calculations_spec.rb +67 -0
- data/spec/cash/finders_spec.rb +343 -0
- data/spec/cash/lock_spec.rb +87 -0
- data/spec/cash/order_spec.rb +166 -0
- data/spec/cash/transactional_spec.rb +574 -0
- data/spec/cash/window_spec.rb +195 -0
- data/spec/cash/write_through_spec.rb +223 -0
- data/spec/spec_helper.rb +55 -0
- metadata +100 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
|
3
|
+
module Cash
|
4
|
+
describe Finders do
|
5
|
+
describe 'Calculations' do
|
6
|
+
describe 'when the cache is populated' do
|
7
|
+
before do
|
8
|
+
@stories = [Story.create!(:title => @title = 'asdf'), Story.create!(:title => @title)]
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#count(:all, :conditions => ...)' do
|
12
|
+
it "does not use the database" do
|
13
|
+
Story.count(:all, :conditions => { :title => @title }).should == @stories.size
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#count(:column, :conditions => ...)' do
|
18
|
+
it "uses the database, not the cache" do
|
19
|
+
mock(Story).get.never
|
20
|
+
Story.count(:title, :conditions => { :title => @title }).should == @stories.size
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#count(:all, :distinct => ..., :select => ...)' do
|
25
|
+
it 'uses the database, not the cache' do
|
26
|
+
mock(Story).get.never
|
27
|
+
Story.count(:all, :distinct => true, :select => :title, :conditions => { :title => @title }).should == @stories.collect(&:title).uniq.size
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'association proxies' do
|
32
|
+
describe '#count(:all, :conditions => ...)' do
|
33
|
+
it 'does not use the database' do
|
34
|
+
story = Story.create!
|
35
|
+
characters = [story.characters.create!(:name => name = 'name'), story.characters.create!(:name => name)]
|
36
|
+
mock(Story.connection).execute.never
|
37
|
+
story.characters.count(:all, :conditions => { :name => name }).should == characters.size
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'when the cache is not populated' do
|
44
|
+
describe '#count(:all, ...)' do
|
45
|
+
describe '#count(:all)' do
|
46
|
+
it 'uses the database, not the cache' do
|
47
|
+
mock(Story).get.never
|
48
|
+
Story.count
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#count(:all, :conditions => ...)' do
|
53
|
+
before do
|
54
|
+
Story.create!(:title => @title = 'title')
|
55
|
+
$memcache.flush_all
|
56
|
+
end
|
57
|
+
|
58
|
+
it "populates the count correctly" do
|
59
|
+
Story.count(:all, :conditions => { :title => @title }).should == 1
|
60
|
+
Story.fetch("title/#{@title}/count", :raw => true).should =~ /\s*1\s*/
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,343 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
|
3
|
+
module Cash
|
4
|
+
describe Finders do
|
5
|
+
describe 'Cache Usage' do
|
6
|
+
describe 'when the cache is populated' do
|
7
|
+
describe '#find' do
|
8
|
+
describe '#find(1)' do
|
9
|
+
it 'does not use the database' do
|
10
|
+
story = Story.create!
|
11
|
+
mock(Story.connection).execute.never
|
12
|
+
Story.find(story.id).should == story
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#find(object)' do
|
17
|
+
it 'uses the objects quoted id' do
|
18
|
+
story = Story.create!
|
19
|
+
mock(Story.connection).execute.never
|
20
|
+
Story.find(story).should == story
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#find(:first, ...)' do
|
25
|
+
describe '#find(:first, :conditions => { :id => ?})' do
|
26
|
+
it "does not use the database" do
|
27
|
+
story = Story.create!
|
28
|
+
mock(Story.connection).execute.never
|
29
|
+
Story.find(:first, :conditions => { :id => story.id }).should == story
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#find(:first, :conditions => 'id = ?')" do
|
34
|
+
it "does not use the database" do
|
35
|
+
story = Story.create!
|
36
|
+
mock(Story.connection).execute.never
|
37
|
+
Story.find(:first, :conditions => "id = #{story.id}").should == story
|
38
|
+
Story.find(:first, :conditions => "`stories`.id = #{story.id}").should == story
|
39
|
+
Story.find(:first, :conditions => "`stories`.`id` = #{story.id}").should == story
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#find(:first, :readonly => false) and any other options other than conditions are nil' do
|
44
|
+
it "does not use the database" do
|
45
|
+
story = Story.create!
|
46
|
+
mock(Story.connection).execute.never
|
47
|
+
Story.find(:first, :conditions => { :id => story.id }, :readonly => false, :limit => nil, :offset => nil, :joins => nil, :include => nil).should == story
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#find(:first, :readonly => true)' do
|
52
|
+
it "uses the database, not the cache" do
|
53
|
+
story = Story.create!
|
54
|
+
mock(Story).get.never
|
55
|
+
Story.find(:first, :conditions => { :id => story.id }, :readonly => true).should == story
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#find(:first, :join => ...) or find(..., :include => ...)' do
|
60
|
+
it "uses the database, not the cache" do
|
61
|
+
story = Story.create!
|
62
|
+
mock(Story).get.never
|
63
|
+
Story.find(:first, :conditions => { :id => story.id }, :joins => 'AS stories').should == story
|
64
|
+
Story.find(:first, :conditions => { :id => story.id }, :include => :characters).should == story
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '#find(:first)' do
|
69
|
+
it 'uses the database, not the cache' do
|
70
|
+
mock(Story).get.never
|
71
|
+
Story.find(:first)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#find(:first, :conditions => "...")' do
|
76
|
+
describe 'on unindexed attributes' do
|
77
|
+
it 'uses the database, not the cache' do
|
78
|
+
story = Story.create!
|
79
|
+
mock(Story).get.never
|
80
|
+
Story.find(:first, :conditions => "type IS NULL")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe 'on indexed attributes' do
|
85
|
+
describe 'when the attributes are integers' do
|
86
|
+
it 'does not use the database' do
|
87
|
+
story = Story.create!
|
88
|
+
mock(Story.connection).execute.never
|
89
|
+
Story.find(:first, :conditions => "`stories`.id = #{story.id}") \
|
90
|
+
.should == story
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe 'when the attributes are non-integers' do
|
95
|
+
it 'uses the database, not the cache' do
|
96
|
+
story = Story.create!(:title => "title")
|
97
|
+
mock(Story.connection).execute.never
|
98
|
+
Story.find(:first, :conditions => "`stories`.title = '#{story.title }'") \
|
99
|
+
.should == story
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe '#find(:first, :conditions => [...])' do
|
105
|
+
describe 'with one indexed attribute' do
|
106
|
+
it 'does not use the database' do
|
107
|
+
story = Story.create!
|
108
|
+
mock(Story.connection).execute.never
|
109
|
+
Story.find(:first, :conditions => ['id = ?', story.id]).should == story
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe 'with two attributes that match a combo-index' do
|
114
|
+
it 'does not use the database' do
|
115
|
+
story = Story.create!(:title => 'title')
|
116
|
+
mock(Story.connection).execute.never
|
117
|
+
Story.find(:first, :conditions => ['id = ? AND title = ?', story.id, story.title]).should == story
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe '#find(:first, :conditions => {...})' do
|
124
|
+
it "does not use the database" do
|
125
|
+
story = Story.create!(:title => "Sam")
|
126
|
+
mock(Story.connection).execute.never
|
127
|
+
Story.find(:first, :conditions => { :id => story.id, :title => story.title }).should == story
|
128
|
+
end
|
129
|
+
|
130
|
+
describe 'regardless of hash order' do
|
131
|
+
it 'does not use the database' do
|
132
|
+
story = Story.create!(:title => "Sam")
|
133
|
+
mock(Story.connection).execute.never
|
134
|
+
Story.find(:first, :conditions => { :id => story.id, :title => story.title }).should == story
|
135
|
+
Story.find(:first, :conditions => { :title => story.title, :id => story.id }).should == story
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe 'on unindexed attribtes' do
|
140
|
+
it 'uses the database, not the cache' do
|
141
|
+
story = Story.create!
|
142
|
+
mock(Story).get.never
|
143
|
+
Story.find(:first, :conditions => { :id => story.id, :type => story.type }).should == story
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe 'when there is a with_scope' do
|
150
|
+
describe 'when the with_scope has conditions' do
|
151
|
+
describe 'when the scope conditions is a string' do
|
152
|
+
it "uses the database, not the cache" do
|
153
|
+
story = Story.create!(:title => title = 'title')
|
154
|
+
mock(Story.connection).execute.never
|
155
|
+
Story.send :with_scope, :find => { :conditions => "title = '#{title}'"} do
|
156
|
+
Story.find(:first, :conditions => { :id => story.id }).should == story
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe 'when the find conditions is a string' do
|
162
|
+
it "does not use the database" do
|
163
|
+
story = Story.create!(:title => title = 'title')
|
164
|
+
mock(Story.connection).execute.never
|
165
|
+
Story.send :with_scope, :find => { :conditions => { :id => story.id }} do
|
166
|
+
Story.find(:first, :conditions => "title = '#{title}'").should == story
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe '#find(1, :conditions => ...)' do
|
172
|
+
it "does not use the database" do
|
173
|
+
story = Story.create!
|
174
|
+
character = Character.create!(:name => name = 'barbara', :story_id => story)
|
175
|
+
mock(Character.connection).execute.never
|
176
|
+
Character.send :with_scope, :find => { :conditions => { :story_id => story.id } } do
|
177
|
+
Character.find(character.id, :conditions => { :name => name }).should == character
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe 'has_many associations' do
|
184
|
+
describe '#find(1)' do
|
185
|
+
it "does not use the database" do
|
186
|
+
story = Story.create!
|
187
|
+
character = story.characters.create!
|
188
|
+
mock(Character.connection).execute.never
|
189
|
+
story.characters.find(character.id).should == character
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe '#find(1, 2, ...)' do
|
194
|
+
it "does not use the database" do
|
195
|
+
story = Story.create!
|
196
|
+
character1 = story.characters.create!
|
197
|
+
character2 = story.characters.create!
|
198
|
+
mock(Character.connection).execute.never
|
199
|
+
story.characters.find(character1.id, character2.id).should == [character1, character2]
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe '#find_by_attr' do
|
204
|
+
it "does not use the database" do
|
205
|
+
story = Story.create!
|
206
|
+
character = story.characters.create!
|
207
|
+
mock(Character.connection).execute.never
|
208
|
+
story.characters.find_by_id(character.id).should == character
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
describe '#find(:all)' do
|
215
|
+
it "uses the database, not the cache" do
|
216
|
+
character = Character.create!
|
217
|
+
mock(Character).get.never
|
218
|
+
Character.find(:all).should == [character]
|
219
|
+
end
|
220
|
+
|
221
|
+
describe '#find(:all, :conditions => {...})' do
|
222
|
+
describe 'when the index is not empty' do
|
223
|
+
it 'does not use the database' do
|
224
|
+
story1 = Story.create!(:title => title = "title")
|
225
|
+
story2 = Story.create!(:title => title)
|
226
|
+
mock(Story.connection).execute.never
|
227
|
+
Story.find(:all, :conditions => { :title => story1.title }).should == [story1, story2]
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
describe '#find(:all, :limit => ..., :offset => ...)' do
|
233
|
+
it "cached attributes should support limits and offsets" do
|
234
|
+
character1 = Character.create!(:name => "Sam", :story_id => 1)
|
235
|
+
character2 = Character.create!(:name => "Sam", :story_id => 1)
|
236
|
+
character3 = Character.create!(:name => "Sam", :story_id => 1)
|
237
|
+
mock(Character.connection).execute.never
|
238
|
+
|
239
|
+
Character.find(:all, :conditions => { :name => character1.name, :story_id => character1.story_id }, :limit => 1).should == [character1]
|
240
|
+
Character.find(:all, :conditions => { :name => character1.name, :story_id => character1.story_id }, :offset => 1).should == [character2, character3]
|
241
|
+
Character.find(:all, :conditions => { :name => character1.name, :story_id => character1.story_id }, :limit => 1, :offset => 1).should == [character2]
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
describe '#find([...])' do
|
247
|
+
describe '#find([1, 2, ...], :conditions => ...)' do
|
248
|
+
it "uses the database, not the cache" do
|
249
|
+
story1, story2 = Story.create!, Story.create!
|
250
|
+
mock(Story).get.never
|
251
|
+
Story.find([story1.id, story2.id], :conditions => "type IS NULL").should == [story1, story2]
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
describe '#find([1], :conditions => ...)' do
|
256
|
+
it "uses the database, not the cache" do
|
257
|
+
story1, story2 = Story.create!, Story.create!
|
258
|
+
mock(Story).get.never
|
259
|
+
Story.find([story1.id], :conditions => "type IS NULL").should == [story1]
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe '#find_by_attr' do
|
265
|
+
describe 'on indexed attributes' do
|
266
|
+
describe '#find_by_id(id)' do
|
267
|
+
it "does not use the database" do
|
268
|
+
story = Story.create!
|
269
|
+
mock(Story.connection).execute.never
|
270
|
+
Story.find_by_id(story.id).should == story
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
describe '#find_by_title(title)' do
|
275
|
+
it "does not use the database" do
|
276
|
+
story1 = Story.create!(:title => 'title1')
|
277
|
+
story2 = Story.create!(:title => 'title2')
|
278
|
+
mock(Story.connection).execute.never
|
279
|
+
Story.find_by_title('title1').should == story1
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
describe "Single Table Inheritence" do
|
286
|
+
describe '#find(:all, ...)' do
|
287
|
+
it "does not use the database" do
|
288
|
+
story, epic, oral = Story.create!(:title => title = 'foo'), Epic.create!(:title => title), Oral.create!(:title => title)
|
289
|
+
mock(Story.connection).execute.never
|
290
|
+
Story.find(:all, :conditions => { :title => title }).should == [story, epic, oral]
|
291
|
+
Epic.find(:all, :conditions => { :title => title }).should == [epic, oral]
|
292
|
+
Oral.find(:all, :conditions => { :title => title }).should == [oral]
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
describe 'when the cache is not populated' do
|
300
|
+
before do
|
301
|
+
@story = Story.create!(:title => 'title')
|
302
|
+
$memcache.flush_all
|
303
|
+
end
|
304
|
+
|
305
|
+
describe '#find(:first, ...)' do
|
306
|
+
it 'populates the cache' do
|
307
|
+
Story.find(:first, :conditions => { :title => @story.title })
|
308
|
+
Story.fetch("title/#{@story.title}").should == [@story.id]
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
describe '#find_by_attr' do
|
313
|
+
it 'populates the cache' do
|
314
|
+
Story.find_by_title(@story.title)
|
315
|
+
Story.fetch("title/#{@story.title}").should == [@story.id]
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
describe '#find(:all, :conditions => ...)' do
|
320
|
+
it 'populates the cache' do
|
321
|
+
Story.find(:all, :conditions => { :title => @story.title })
|
322
|
+
Story.fetch("title/#{@story.title}").should == [@story.id]
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
describe '#find(1)' do
|
327
|
+
it 'populates the cache' do
|
328
|
+
Story.find(@story.id)
|
329
|
+
Story.fetch("id/#{@story.id}").should == [@story]
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
describe 'when there is a with_scope' do
|
334
|
+
it "uses the database, not the cache" do
|
335
|
+
Story.send :with_scope, :find => { :conditions => { :title => @story.title }} do
|
336
|
+
Story.find(:first, :conditions => { :id => @story.id }).should == @story
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
|
3
|
+
module Cash
|
4
|
+
describe Lock do
|
5
|
+
describe '#synchronize' do
|
6
|
+
it "yields the block" do
|
7
|
+
block_was_called = false
|
8
|
+
$lock.synchronize('lock_key') do
|
9
|
+
block_was_called = true
|
10
|
+
end
|
11
|
+
block_was_called.should == true
|
12
|
+
end
|
13
|
+
|
14
|
+
it "acquires the specified lock before the block is run" do
|
15
|
+
$memcache.get("lock/lock_key").should == nil
|
16
|
+
$lock.synchronize('lock_key') do
|
17
|
+
$memcache.get("lock/lock_key").should_not == nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "releases the lock after the block is run" do
|
22
|
+
$memcache.get("lock/lock_key").should == nil
|
23
|
+
$lock.synchronize('lock_key') {}
|
24
|
+
$memcache.get("lock/lock_key").should == nil
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
it "releases the lock even if the block raises" do
|
29
|
+
$memcache.get("lock/lock_key").should == nil
|
30
|
+
$lock.synchronize('lock_key') { raise } rescue nil
|
31
|
+
$memcache.get("lock/lock_key").should == nil
|
32
|
+
end
|
33
|
+
|
34
|
+
specify "does not block on recursive lock acquisition" do
|
35
|
+
$lock.synchronize('lock_key') do
|
36
|
+
lambda { $lock.synchronize('lock_key') {} }.should_not raise_error
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#acquire_lock' do
|
42
|
+
specify "creates a lock at a given cache key" do
|
43
|
+
$memcache.get("lock/lock_key").should == nil
|
44
|
+
$lock.acquire_lock("lock_key")
|
45
|
+
$memcache.get("lock/lock_key").should_not == nil
|
46
|
+
end
|
47
|
+
|
48
|
+
specify "retries specified number of times" do
|
49
|
+
$lock.acquire_lock('lock_key')
|
50
|
+
as_another_process do
|
51
|
+
mock($memcache).add("lock/lock_key", Process.pid, timeout = 10) { "NOT_STORED\r\n" }.times(3)
|
52
|
+
stub($lock).exponential_sleep
|
53
|
+
lambda { $lock.acquire_lock('lock_key', timeout, 3) }.should raise_error
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
specify "correctly sets timeout on memcache entries" do
|
58
|
+
mock($memcache).add('lock/lock_key', Process.pid, timeout = 10) { "STORED\r\n" }
|
59
|
+
$lock.acquire_lock('lock_key', timeout)
|
60
|
+
end
|
61
|
+
|
62
|
+
specify "prevents two processes from acquiring the same lock at the same time" do
|
63
|
+
$lock.acquire_lock('lock_key')
|
64
|
+
as_another_process do
|
65
|
+
lambda { $lock.acquire_lock('lock_key') }.should raise_error
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def as_another_process
|
70
|
+
current_pid = Process.pid
|
71
|
+
stub(Process).pid { current_pid + 1 }
|
72
|
+
yield
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '#release_lock' do
|
78
|
+
specify "deletes the lock for a given cache key" do
|
79
|
+
$memcache.get("lock/lock_key").should == nil
|
80
|
+
$lock.acquire_lock("lock_key")
|
81
|
+
$memcache.get("lock/lock_key").should_not == nil
|
82
|
+
$lock.release_lock("lock_key")
|
83
|
+
$memcache.get("lock/lock_key").should == nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|