seifertd-cache-money 0.2.5.2
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 +205 -0
- data/TODO +17 -0
- data/UNSUPPORTED_FEATURES +14 -0
- data/config/environment.rb +6 -0
- data/config/memcache.yml +6 -0
- data/db/schema.rb +12 -0
- data/lib/cache_money.rb +54 -0
- data/lib/cash/accessor.rb +79 -0
- data/lib/cash/buffered.rb +126 -0
- data/lib/cash/config.rb +71 -0
- data/lib/cash/finders.rb +38 -0
- data/lib/cash/index.rb +207 -0
- data/lib/cash/local.rb +59 -0
- data/lib/cash/lock.rb +53 -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/request.rb +3 -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 +159 -0
- data/spec/cash/active_record_spec.rb +199 -0
- data/spec/cash/calculations_spec.rb +67 -0
- data/spec/cash/finders_spec.rb +355 -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 +56 -0
- metadata +113 -0
@@ -0,0 +1,355 @@
|
|
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
|
+
|
298
|
+
describe '#without_cache' do
|
299
|
+
describe 'when finders are called within the provided block' do
|
300
|
+
it 'uses the database not the cache' do
|
301
|
+
story = Story.create!
|
302
|
+
mock(Story).get.never
|
303
|
+
Story.without_cache do
|
304
|
+
Story.find(story.id).should == story
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
describe 'when the cache is not populated' do
|
312
|
+
before do
|
313
|
+
@story = Story.create!(:title => 'title')
|
314
|
+
$memcache.flush_all
|
315
|
+
end
|
316
|
+
|
317
|
+
describe '#find(:first, ...)' do
|
318
|
+
it 'populates the cache' do
|
319
|
+
Story.find(:first, :conditions => { :title => @story.title })
|
320
|
+
Story.fetch("title/#{@story.title}").should == [@story.id]
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
describe '#find_by_attr' do
|
325
|
+
it 'populates the cache' do
|
326
|
+
Story.find_by_title(@story.title)
|
327
|
+
Story.fetch("title/#{@story.title}").should == [@story.id]
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
describe '#find(:all, :conditions => ...)' do
|
332
|
+
it 'populates the cache' do
|
333
|
+
Story.find(:all, :conditions => { :title => @story.title })
|
334
|
+
Story.fetch("title/#{@story.title}").should == [@story.id]
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
describe '#find(1)' do
|
339
|
+
it 'populates the cache' do
|
340
|
+
Story.find(@story.id)
|
341
|
+
Story.fetch("id/#{@story.id}").should == [@story]
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
describe 'when there is a with_scope' do
|
346
|
+
it "uses the database, not the cache" do
|
347
|
+
Story.send :with_scope, :find => { :conditions => { :title => @story.title }} do
|
348
|
+
Story.find(:first, :conditions => { :id => @story.id }).should == @story
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
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
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
|
3
|
+
module Cash
|
4
|
+
describe 'Ordering' do
|
5
|
+
before :suite do
|
6
|
+
FairyTale = Class.new(Story)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#create!' do
|
10
|
+
describe 'the records are written-through in sorted order', :shared => true do
|
11
|
+
describe 'when there are not already records matching the index' do
|
12
|
+
it 'initializes the index' do
|
13
|
+
fairy_tale = FairyTale.create!(:title => 'title')
|
14
|
+
FairyTale.get("title/#{fairy_tale.title}").should == [fairy_tale.id]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'when there are already records matching the index' do
|
19
|
+
before do
|
20
|
+
@fairy_tale1 = FairyTale.create!(:title => 'title')
|
21
|
+
FairyTale.get("title/#{@fairy_tale1.title}").should == sorted_and_serialized_records(@fairy_tale1)
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'when the index is populated' do
|
25
|
+
it 'appends to the index' do
|
26
|
+
fairy_tale2 = FairyTale.create!(:title => @fairy_tale1.title)
|
27
|
+
FairyTale.get("title/#{@fairy_tale1.title}").should == sorted_and_serialized_records(@fairy_tale1, fairy_tale2)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'when the index is not populated' do
|
32
|
+
before do
|
33
|
+
$memcache.flush_all
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'initializes the index' do
|
37
|
+
fairy_tale2 = FairyTale.create!(:title => @fairy_tale1.title)
|
38
|
+
FairyTale.get("title/#{@fairy_tale1.title}").should == sorted_and_serialized_records(@fairy_tale1, fairy_tale2)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe 'when the order is ascending' do
|
45
|
+
it_should_behave_like 'the records are written-through in sorted order'
|
46
|
+
|
47
|
+
before :all do
|
48
|
+
FairyTale.index :title, :order => :asc
|
49
|
+
end
|
50
|
+
|
51
|
+
def sorted_and_serialized_records(*records)
|
52
|
+
records.collect(&:id).sort
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'when the order is descending' do
|
57
|
+
it_should_behave_like 'the records are written-through in sorted order'
|
58
|
+
|
59
|
+
before :all do
|
60
|
+
FairyTale.index :title, :order => :desc
|
61
|
+
end
|
62
|
+
|
63
|
+
def sorted_and_serialized_records(*records)
|
64
|
+
records.collect(&:id).sort.reverse
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "#find(..., :order => ...)" do
|
70
|
+
before :each do
|
71
|
+
@fairy_tales = [FairyTale.create!(:title => @title = 'title'), FairyTale.create!(:title => @title)]
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'when the order is ascending' do
|
75
|
+
before :all do
|
76
|
+
FairyTale.index :title, :order => :asc
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#find(..., :order => 'id ASC')" do
|
80
|
+
describe 'when the cache is populated' do
|
81
|
+
it 'does not use the database' do
|
82
|
+
mock(FairyTale.connection).execute.never
|
83
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id ASC').should == @fairy_tales
|
84
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id').should == @fairy_tales
|
85
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => '`id`').should == @fairy_tales
|
86
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'stories.id').should == @fairy_tales
|
87
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => '`stories`.id').should == @fairy_tales
|
88
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => '`stories`.`id`').should == @fairy_tales
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe 'when the cache is not populated' do
|
93
|
+
it 'populates the cache' do
|
94
|
+
$memcache.flush_all
|
95
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id ASC').should == @fairy_tales
|
96
|
+
FairyTale.get("title/#{@title}").should == @fairy_tales.collect(&:id)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "#find(..., :order => 'id DESC')" do
|
102
|
+
describe 'when the cache is populated' do
|
103
|
+
it 'uses the database, not the cache' do
|
104
|
+
mock(FairyTale).get.never
|
105
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id DESC').should == @fairy_tales.reverse
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'when the cache is not populated' do
|
110
|
+
it 'does not populate the cache' do
|
111
|
+
$memcache.flush_all
|
112
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id DESC').should == @fairy_tales.reverse
|
113
|
+
FairyTale.get("title/#{@title}").should be_nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe 'when the order is descending' do
|
120
|
+
before :all do
|
121
|
+
FairyTale.index :title, :order => :desc
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "#find(..., :order => 'id DESC')" do
|
125
|
+
describe 'when the cache is populated' do
|
126
|
+
it 'does not use the database' do
|
127
|
+
mock(FairyTale.connection).execute.never
|
128
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id DESC').should == @fairy_tales.reverse
|
129
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id DESC').should == @fairy_tales.reverse
|
130
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => '`id` DESC').should == @fairy_tales.reverse
|
131
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'stories.id DESC').should == @fairy_tales.reverse
|
132
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => '`stories`.id DESC').should == @fairy_tales.reverse
|
133
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => '`stories`.`id` DESC').should == @fairy_tales.reverse
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe 'when the cache is not populated' do
|
138
|
+
it 'populates the cache' do
|
139
|
+
$memcache.flush_all
|
140
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id DESC')
|
141
|
+
FairyTale.get("title/#{@title}").should == @fairy_tales.collect(&:id).reverse
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe "#find(..., :order => 'id ASC')" do
|
147
|
+
describe 'when the cache is populated' do
|
148
|
+
it 'uses the database, not the cache' do
|
149
|
+
mock(FairyTale).get.never
|
150
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id ASC').should == @fairy_tales
|
151
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id').should == @fairy_tales
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe 'when the cache is not populated' do
|
156
|
+
it 'does not populate the cache' do
|
157
|
+
$memcache.flush_all
|
158
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id ASC').should == @fairy_tales
|
159
|
+
FairyTale.get("title/#{@title}").should be_nil
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|