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