cached_resource 7.2.0 → 9.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.
@@ -1,594 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe CachedResource do
4
-
5
- def read_from_cache(key)
6
- Thing.send(:cache_read, key)
7
- end
8
-
9
- before(:each) do
10
- class Thing < ActiveResource::Base
11
- self.site = "http://api.thing.com"
12
- cached_resource
13
- end
14
-
15
- class NotTheThing < ActiveResource::Base
16
- self.site = "http://api.notthething.com"
17
- cached_resource
18
- end
19
-
20
- @thing = {:thing => {:id => 1, :name => "Ada"}}
21
- @thing_collection = [{:id => 1, :name => "Ada"}, {:id => 2, :name => "Ada", :major => 'CS'}]
22
- @thing_collection2 = [{:id => 2, :name => "Ada", :major => 'CS'}]
23
- @other_thing = {:thing => {:id => 1, :name => "Ari"}}
24
- @thing2 = {:thing => {:id => 2, :name => "Joe"}}
25
- @other_thing2 = {:thing => {:id => 2, :name => "Jeb"}}
26
- @thing3 = {:thing => {:id => 3, :name => "Stu"}}
27
- @string_thing = {:thing => {:id => "fded", :name => "Lev"}}
28
- @other_string_thing = {:thing => {:id => "fded", :name => "Lon"}}
29
- @date_thing = {:thing => {:id => 4, :created_at => DateTime.new(2020)}}
30
- @thing_json = @thing.to_json
31
- @other_thing_json = @other_thing.to_json
32
- @string_thing_json = @string_thing.to_json
33
- @other_string_thing_json = @other_string_thing.to_json
34
- @date_thing_json = @date_thing.to_json
35
- @nil_thing = nil.to_json
36
- @empty_array_thing = [].to_json
37
- @not_the_thing = {:not_the_thing => {:id => 1, :name => "Not"}}
38
- @not_the_thing_json = @not_the_thing.to_json
39
- end
40
-
41
- after(:each) do
42
- Thing.cached_resource.cache.clear
43
- Object.send(:remove_const, :Thing)
44
- NotTheThing.cached_resource.cache.clear
45
- Object.send(:remove_const, :NotTheThing)
46
- end
47
-
48
- describe "when enabled" do
49
- before(:each) do
50
- # it's on by default, but lets call the method
51
- # to make sure it works
52
- Thing.cached_resource.cache.clear
53
- Thing.cached_resource.on!
54
- NotTheThing.cached_resource.cache.clear
55
- NotTheThing.cached_resource.on!
56
-
57
- ActiveResource::HttpMock.reset!
58
- ActiveResource::HttpMock.respond_to do |mock|
59
- mock.get "/things/1.json", {}, @thing_json
60
- mock.get "/things/1.json?foo=bar", {}, @thing_json
61
- mock.get "/things/fded.json", {}, @string_thing_json
62
- mock.get "/things.json?name=42", {}, @nil_thing, 404
63
- mock.get "/things.json?name=43", {}, @empty_array_thing
64
- mock.get "/things/4.json", {}, @date_thing_json
65
- mock.get "/not_the_things/1.json", {}, @not_the_thing_json
66
- end
67
- end
68
-
69
- it "should cache a response" do
70
- result = Thing.find(1)
71
- read_from_cache("thing/1").should == result
72
- end
73
-
74
- it "shouldn't cache nil response" do
75
- Thing.find(:all, :params => { :name => '42' })
76
- read_from_cache("thing/all/name/42").should == nil
77
- end
78
-
79
- it "shouldn't cache [] response" do
80
- Thing.find(:all, :params => { :name => '43' })
81
- read_from_cache("thing/all/name/43").should == nil
82
- end
83
-
84
- it "should cache a response for a string primary key" do
85
- result = Thing.find("fded")
86
- read_from_cache("thing/fded").should == result
87
- end
88
-
89
- it "should cache without whitespace in keys" do
90
- result = Thing.find(1, :from => 'path', :params => { :foo => 'bar' })
91
- read_from_cache('thing/1/{:from=>"path",:params=>{:foo=>"bar"}}').should == result
92
- end
93
-
94
- it "should empty the cache when clear_cache is called" do
95
- result = Thing.find(1)
96
- Thing.clear_cache
97
- read_from_cache("thing/1").should == nil
98
- end
99
-
100
- it "should not empty the cache of NotTheThing when clear_cache is called on the Thing" do
101
- result1 = Thing.find(1)
102
- result2 = NotTheThing.find(1)
103
- Thing.clear_cache
104
- NotTheThing.send(:cache_read, 'notthething/1').should == result2
105
- end
106
-
107
- it "should empty all the cache when clear_cache is called on the Thing with :all option set" do
108
- result1 = Thing.find(1)
109
- result2 = NotTheThing.find(1)
110
- Thing.clear_cache(all: true)
111
- NotTheThing.send(:cache_read, 'notthething/1').should == nil
112
- end
113
-
114
- it "should cache a response with the same persistence" do
115
- result1 = Thing.find(1)
116
- result1.persisted?.should be true
117
- result2 = Thing.find(1)
118
- result1.persisted?.should == result2.persisted?
119
- end
120
-
121
- it "should read a response when the request is made again" do
122
- # make a request
123
- Thing.find(1)
124
- # make the same request
125
- Thing.find(1)
126
- # only one request should have happened
127
- ActiveResource::HttpMock.requests.length.should == 1
128
- end
129
-
130
- it "should read a response when the request is made again for a string primary key" do
131
- # make a request
132
- Thing.find("fded")
133
- # make the same request
134
- Thing.find("fded")
135
- # only one request should have happened
136
- ActiveResource::HttpMock.requests.length.should == 1
137
- end
138
-
139
- it "should remake a request when reloaded" do
140
- # make a request
141
- Thing.find(1)
142
- # make the same request, but reload it
143
- Thing.find(1, :reload => true)
144
- # we should get two requests
145
- ActiveResource::HttpMock.requests.length.should == 2
146
- end
147
-
148
- it "should remake a request when reloaded for a string primary key" do
149
- # make a request
150
- Thing.find("fded")
151
- # make the same request, but reload it
152
- Thing.find("fded", :reload => true)
153
- # we should get two requests
154
- ActiveResource::HttpMock.requests.length.should == 2
155
- end
156
-
157
- it "should rewrite the cache when the request is reloaded" do
158
- # make a request
159
- Thing.find(1)
160
- # get the cached result of the request
161
- old_result = read_from_cache("thing/1")
162
-
163
- # change the response
164
- ActiveResource::HttpMock.reset!
165
- ActiveResource::HttpMock.respond_to do |mock|
166
- mock.get "/things/1.json", {}, @other_thing_json
167
- end
168
-
169
- Thing.find(1, :reload => true)
170
- new_result = read_from_cache("thing/1")
171
- # since active resources are equal if and only if they
172
- # are the same object or an instance of the same class,
173
- # not new?, and have the same id.
174
- new_result.name.should_not == old_result.name
175
- end
176
-
177
- it "should remake the request when the ttl expires" do
178
- # set cache time to live to 1 second
179
- Thing.cached_resource.ttl = 1
180
- # make a request
181
- Thing.find(1)
182
- # wait for the cache to expire
183
- sleep(1.5)
184
- # make the same request
185
- Thing.find(1)
186
- ActiveResource::HttpMock.requests.length.should == 2
187
- end
188
-
189
- it "should not return a frozen object on first request" do
190
- result1 = Thing.find(1)
191
- result1.should_not be_frozen
192
- end
193
-
194
- it "should not return frozen object on a subsequent request" do
195
- result1 = Thing.find(1)
196
- result2 = Thing.find(1)
197
- result2.should_not be_frozen
198
- end
199
-
200
- it "should not freeze first requested object on a subsequent request" do
201
- result1 = Thing.find(1)
202
- result2 = Thing.find(1)
203
- result1.should_not be_frozen
204
- end
205
-
206
- describe "when ActiveSupport.parse_json_times is enabled" do
207
- before(:all) do
208
- Time.zone = 'UTC'
209
- ActiveSupport.parse_json_times = true
210
- end
211
-
212
- it "should convert date times to objects when reading from cache" do
213
- Thing.find(4)
214
-
215
- read_from_cache("thing/4").created_at.should == @date_thing[:thing][:created_at]
216
- end
217
- end
218
-
219
- shared_examples "collection_return_type" do
220
- if ActiveResource::VERSION::MAJOR >= 4
221
- it "should return an ActiveResource::Collection" do
222
- cached = read_from_cache("thing/all")
223
- cached.should be_instance_of(ActiveResource::Collection)
224
- end
225
-
226
- it "should return a chainable instance of the collection_parser" do
227
- Thing.cached_resource.cache.clear
228
- class CustomCollection < ActiveResource::Collection; end
229
- Thing.collection_parser = CustomCollection
230
-
231
- ActiveResource::HttpMock.respond_to do |mock|
232
- mock.get "/things.json?name=ada", {}, @thing_collection.to_json
233
- mock.get "/things.json?major=CS&name=ada", {}, @thing_collection2.to_json
234
- end
235
-
236
- non_cached = Thing.where(name: 'ada')
237
- non_cached.original_params.should == { 'name' => 'ada' }
238
- non_cached.map(&:id).should == @thing_collection.map { |h| h[:id]}
239
-
240
- cached = read_from_cache('thing/all/{:params=>{:name=>"ada"}}')
241
- cached.should be_instance_of(CustomCollection)
242
- cached.original_params.should == { 'name' => 'ada' }
243
- cached.resource_class.should == Thing
244
- cached.map(&:id).should == @thing_collection.map { |h| h[:id]}
245
-
246
- if ActiveResource::VERSION::MAJOR < 5
247
- non_cached = cached.resource_class.where(cached.original_params.merge(major: 'CS'))
248
- else
249
- non_cached = cached.where(major: 'CS')
250
- end
251
-
252
- non_cached.original_params.should == { 'name' => 'ada', 'major' => 'CS' }
253
- non_cached.resource_class.should == Thing
254
- non_cached.map(&:id).should == @thing_collection2.map { |h| h[:id]}
255
- cached = read_from_cache('thing/all/{:params=>{"name"=>"ada",:major=>"cs"}}')
256
- cached.original_params.should == { 'name' => 'ada', 'major' => 'CS' }
257
- cached.resource_class.should == Thing
258
- cached.map(&:id).should == @thing_collection2.map { |h| h[:id]}
259
- end
260
- else
261
- it "should return an Array" do
262
- cached = read_from_cache("thing/all")
263
- cached.should be_instance_of(Array)
264
- end
265
- end
266
- end
267
-
268
- shared_examples "collection_freezing" do
269
- it "should not return a frozen collection on first request" do
270
- Thing.cached_resource.cache.clear
271
- collection1 = Thing.all
272
- collection1.should_not be_frozen
273
- end
274
-
275
- it "should not return a frozen collection on a subsequent request" do
276
- Thing.cached_resource.cache.clear
277
- collection1 = Thing.all
278
- collection2 = Thing.all
279
- collection2.should_not be_frozen
280
- end
281
-
282
- it "should not freeze first requested collection on a subsequent request" do
283
- Thing.cached_resource.cache.clear
284
- result1 = Thing.all
285
- result2 = Thing.all
286
- result1.should_not be_frozen
287
- end
288
-
289
- it "should not return frozen members on first request" do
290
- Thing.cached_resource.cache.clear
291
- collection1 = Thing.all
292
- collection1.first.should_not be_frozen
293
- end
294
-
295
- it "should not return frozen members on a subsequent request" do
296
- Thing.cached_resource.cache.clear
297
- collection1 = Thing.all
298
- collection2 = Thing.all
299
- collection2.first.should_not be_frozen
300
- end
301
-
302
- it "should not freeze members on a subsequent request" do
303
- Thing.cached_resource.cache.clear
304
- collection1 = Thing.all
305
- member1 = Thing.find(1)
306
- collection1.first.should_not be_frozen
307
- end
308
-
309
- end
310
-
311
- shared_examples "collection_cache_clearing" do
312
- it "should empty the cache when clear_cache is called" do
313
- Thing.clear_cache
314
- read_from_cache("thing/all").should == nil
315
- read_from_cache("thing/1").should == nil
316
- end
317
-
318
- end
319
-
320
- describe "when collection synchronize is enabled" do
321
- before(:each) do
322
- Thing.cached_resource.cache.clear
323
- Thing.cached_resource.collection_synchronize = true
324
-
325
- ActiveResource::HttpMock.reset!
326
- ActiveResource::HttpMock.respond_to do |mock|
327
- mock.get "/things/1.json", {}, @thing_json
328
- mock.get "/things/fded.json", {}, @string_thing_json
329
- mock.get "/things.json", {}, [@thing[:thing], @string_thing[:thing]].to_json(:root => :thing)
330
- end
331
-
332
- # make a request for all things
333
- Thing.all
334
- end
335
-
336
- include_examples "collection_return_type"
337
-
338
- include_examples "collection_freezing"
339
-
340
- it "should write cache entries for its members" do
341
- result = Thing.find(1)
342
- string_result = Thing.find("fded")
343
- # only the all request should have been made
344
- ActiveResource::HttpMock.requests.length.should == 1
345
- # the result should be cached with the appropriate key
346
- read_from_cache("thing/1").should == result
347
- read_from_cache("thing/fded").should == string_result
348
- end
349
-
350
- include_examples "collection_cache_clearing"
351
-
352
- it "should rewrite cache entries for its members when reloaded" do
353
- # get the soon to be stale result so that we have a cache entry
354
- old_result = Thing.find(1)
355
- old_string_result = Thing.find("fded")
356
- # change the server
357
- ActiveResource::HttpMock.respond_to do |mock|
358
- mock.get "/things/1.json", {}, @other_thing_json
359
- mock.get "/things/fded.json", {}, @other_string_thing_json
360
- mock.get "/things.json", {}, [@other_thing[:thing], @other_string_thing[:thing]].to_json(:root => :thing)
361
- end
362
- # reload the collection
363
- Thing.all(:reload => true)
364
- # get the updated result, read from the cache
365
- result = Thing.find(1)
366
- read_from_cache("thing/all")[0].should == result
367
- read_from_cache("thing/all")[0].name.should == result.name
368
- string_result = Thing.find("fded")
369
- read_from_cache("thing/all")[1].should == string_result
370
- read_from_cache("thing/all")[1].name.should == string_result.name
371
- end
372
-
373
- it "should update the collection when an individual request is reloaded" do
374
- # change the server
375
- ActiveResource::HttpMock.respond_to do |mock|
376
- mock.get "/things/1.json", {}, @other_thing_json
377
- mock.get "/things/fded.json", {}, @other_string_thing_json
378
- mock.get "/things.json", {}, [@other_thing[:thing], @other_string_thing[:thing]].to_json(:root => :thing)
379
- end
380
-
381
- # reload the individual
382
- result = Thing.find(1, :reload => true)
383
- read_from_cache("thing/all")[0].should == result
384
- read_from_cache("thing/all")[0].name.should == result.name
385
- string_result = Thing.find("fded", :reload => true)
386
- read_from_cache("thing/all")[1].should == string_result
387
- read_from_cache("thing/all")[1].name.should == string_result.name
388
- end
389
-
390
- it "should update both the collection and the member cache entries when a subset of the collection is retrieved" do
391
- # create cache entries for 1 and all
392
- old_individual = Thing.find(1)
393
- old_collection = Thing.all
394
-
395
- # change the server
396
- ActiveResource::HttpMock.respond_to do |mock|
397
- mock.get "/things.json?name=Ari", {}, [@other_thing[:thing]].to_json(:root => :thing)
398
- mock.get "/things.json?name=Lon", {}, [@other_string_thing[:thing]].to_json(:root => :thing)
399
- end
400
-
401
- # make a request for a subset of the "mother" collection
402
- result = Thing.find(:all, :params => {:name => "Ari"})
403
- # the collection should be updated to reflect the server change
404
- read_from_cache("thing/all")[0].should == result[0]
405
- read_from_cache("thing/all")[0].name.should == result[0].name
406
- # the individual should be updated to reflect the server change
407
- read_from_cache("thing/1").should == result[0]
408
- read_from_cache("thing/1").name.should == result[0].name
409
-
410
- # make a request for a subset of the "mother" collection
411
- result = Thing.find(:all, :params => {:name => "Lon"})
412
- # the collection should be updated to reflect the server change
413
- read_from_cache("thing/all")[1].should == result[0]
414
- read_from_cache("thing/all")[1].name.should == result[0].name
415
- # the individual should be updated to reflect the server change
416
- read_from_cache("thing/fded").should == result[0]
417
- read_from_cache("thing/fded").name.should == result[0].name
418
- end
419
-
420
- it "should maintain the order of the collection when updating it" do
421
- # change the server to return a longer collection
422
- ActiveResource::HttpMock.respond_to do |mock|
423
- mock.get "/things.json", {}, [@thing[:thing], @thing3[:thing], @thing2[:thing], @string_thing[:thing]].to_json(:root => :thing)
424
- end
425
-
426
- # create cache entry for the collection (we reload because in before block we make an all request)
427
- old_collection = Thing.all(:reload => true)
428
-
429
- # change the server's response for the thing with id 2
430
- ActiveResource::HttpMock.respond_to do |mock|
431
- mock.get "/things/2.json", {}, @other_thing2.to_json(:root => :thing)
432
- mock.get "/things/fded.json", {}, @other_string_thing.to_json(:root => :thing)
433
- end
434
-
435
- # get thing 2, thereby updating the collection
436
- result = Thing.find(2, :reload => true)
437
- # get the updated collection from the cache
438
- updated_collection = Thing.all
439
- # name should have changed to "Jeb"
440
- updated_collection[2].name.should == result.name
441
- # the updated collection should have the elements in the same order
442
- old_collection.each_with_index do |thing, i|
443
- updated_collection[i].id.should == thing.id
444
- end
445
-
446
- # get string thing, thereby updating the collection
447
- string_result = Thing.find("fded", :reload => true)
448
- # get the updated collection from the cache
449
- updated_collection = Thing.all
450
- # name should have changed to "Lon"
451
- updated_collection[3].name.should == string_result.name
452
- # the updated collection should have the elements in the same order
453
- old_collection.each_with_index do |thing, i|
454
- updated_collection[i].id.should == thing.id
455
- end
456
- end
457
- end
458
-
459
- describe "when collection synchronize is disabled" do
460
- before(:each) do
461
- Thing.cached_resource.cache.clear
462
- Thing.cached_resource.collection_synchronize = false
463
-
464
- ActiveResource::HttpMock.reset!
465
- ActiveResource::HttpMock.respond_to do |mock|
466
- mock.get "/things/1.json", {}, @thing_json
467
- mock.get "/things/fded.json", {}, @string_thing_json
468
- mock.get "/things.json", {}, [@thing[:thing], @string_thing[:thing]].to_json(:root => :thing)
469
- end
470
-
471
- # make a request for all things
472
- Thing.all
473
- end
474
-
475
- include_examples "collection_return_type"
476
-
477
- include_examples "collection_freezing"
478
-
479
- it "should not write cache entries for its members" do
480
- result = Thing.find(1)
481
- result = Thing.find("fded")
482
- # both the all in the before each and this request should have been made
483
- ActiveResource::HttpMock.requests.length.should == 3
484
- end
485
-
486
- include_examples "collection_cache_clearing"
487
-
488
- it "should not update the collection when an individual request is reloaded" do
489
- # change the server
490
- ActiveResource::HttpMock.respond_to do |mock|
491
- mock.get "/things/1.json", {}, @other_thing_json
492
- mock.get "/things/fded.json", {}, @other_string_thing_json
493
- mock.get "/things.json", {}, [@other_thing[:thing], @other_string_thing[:thing]].to_json(:root => :thing)
494
- end
495
-
496
- # reload the individual
497
- result = Thing.find(1, :reload => true)
498
- # the ids are the same, but the names should be different
499
- read_from_cache("thing/all")[0].name.should_not == result.name
500
-
501
- # reload the individual
502
- string_result = Thing.find("fded", :reload => true)
503
- # the ids are the same, but the names should be different
504
- read_from_cache("thing/all")[1].name.should_not == string_result.name
505
- end
506
- end
507
-
508
- describe "when ttl randomization is enabled" do
509
- before(:each) do
510
- @ttl = 1
511
- Thing.cached_resource.ttl = @ttl
512
- Thing.cached_resource.ttl_randomization = true
513
- Thing.cached_resource.send(:sample_range, 1..2, @ttl)
514
- # next ttl 1.72032449344216
515
- end
516
-
517
- it "should generate a random ttl" do
518
- Thing.cached_resource.cache.should_receive(:write)
519
- Thing.cached_resource.cache.stub(:write) do |key, value, options|
520
- # we know the ttl should not be the same as the set ttl
521
- options[:expires_in].should_not == @ttl
522
- end
523
-
524
- Thing.find(1)
525
- end
526
-
527
- end
528
- end
529
-
530
- describe "when disabled" do
531
- before(:each) do
532
- Thing.cached_resource.cache.clear
533
- Thing.cached_resource.off!
534
-
535
- ActiveResource::HttpMock.reset!
536
- ActiveResource::HttpMock.respond_to do |mock|
537
- mock.get "/things/1.json", {}, @thing_json
538
- mock.get "/things/fded.json", {}, @string_thing_json
539
- end
540
- end
541
-
542
- it "should not cache a response" do
543
- result = Thing.find(1)
544
- read_from_cache("thing/1").should be_nil
545
- end
546
-
547
- it "should always remake the request" do
548
- Thing.find(1)
549
- ActiveResource::HttpMock.requests.length.should == 1
550
- Thing.find(1)
551
- ActiveResource::HttpMock.requests.length.should == 2
552
- end
553
-
554
- it "should always remake the request for a string primary key" do
555
- Thing.find("fded")
556
- ActiveResource::HttpMock.requests.length.should == 1
557
- Thing.find("fded")
558
- ActiveResource::HttpMock.requests.length.should == 2
559
- end
560
- end
561
-
562
- describe "when cache_collections is disabled" do
563
- before(:each) do
564
- Thing.cached_resource.cache.clear
565
- Thing.cached_resource.cache_collections = false
566
-
567
- ActiveResource::HttpMock.reset!
568
- ActiveResource::HttpMock.respond_to do |mock|
569
- mock.get "/things.json", {}, [@thing[:thing],@string_thing[:thing]].to_json(:root => :thing)
570
- mock.get "/things/1.json", {}, @thing_json
571
- mock.get "/things/fded.json", {}, @string_thing_json
572
- end
573
- end
574
-
575
- it "should cache a response" do
576
- result = Thing.find(1)
577
- read_from_cache("thing/1").should == result
578
- end
579
-
580
- it "should not remake a single request" do
581
- result = Thing.find(1)
582
- ActiveResource::HttpMock.requests.length.should == 1
583
- result = Thing.find(1)
584
- ActiveResource::HttpMock.requests.length.should == 1
585
- end
586
-
587
- it "should always remake the request for collections" do
588
- Thing.all
589
- ActiveResource::HttpMock.requests.length.should == 1
590
- Thing.all
591
- ActiveResource::HttpMock.requests.length.should == 2
592
- end
593
- end
594
- end