cached_resource 7.0.0 → 7.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +2 -2
- data/FUNDING.json +7 -0
- data/README.md +7 -0
- data/lib/cached_resource/caching.rb +47 -20
- data/lib/cached_resource/version.rb +1 -1
- data/spec/cached_resource/caching_spec.rb +42 -49
- data/spec/spec_helper.rb +1 -0
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1c2150c2dc21cf091c9b26674b3e52a7bd43938318704f7c2f0739a0b9756aa
|
4
|
+
data.tar.gz: 2f7b9665c688f2a60b7def43cfafbe2701accded9fcf9aabc371fe91bf78362d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c0c94c6b77f623a49a3259a69a4bb1b1d2d7fc219d590b740d5a86c71f3cfbf7eb783a2e8a2c06631c926bd2679b220341d8fbf03b77a3651d5219a70bb8d779
|
7
|
+
data.tar.gz: 207c37429a14511fb788d21bc69e86b01f8f59a303b20aebafb2f07e54aba48546c03a5ac7fa4b99da0bd9b7cd53d385ddb73306c643d9044d113ea638949e61
|
data/.github/workflows/ruby.yml
CHANGED
@@ -15,7 +15,7 @@ permissions:
|
|
15
15
|
jobs:
|
16
16
|
test:
|
17
17
|
|
18
|
-
runs-on: ubuntu-
|
18
|
+
runs-on: ubuntu-20.04
|
19
19
|
|
20
20
|
strategy:
|
21
21
|
matrix:
|
@@ -106,4 +106,4 @@ jobs:
|
|
106
106
|
TEST_RAILS_VERSION: ${{ matrix.rails-version }}
|
107
107
|
DEBUG: true
|
108
108
|
- name: Run tests
|
109
|
-
run: bundle exec rake
|
109
|
+
run: bundle exec rake
|
data/FUNDING.json
ADDED
data/README.md
CHANGED
@@ -25,6 +25,13 @@ CachedResource is designed to be framework agnostic, but will hook into Rails fo
|
|
25
25
|
| 💎 3.1 | | | | ✅ | ✅ | ✅ |
|
26
26
|
| 💎 3.2 | | | | ✅ | ✅ | ✅ |
|
27
27
|
|
28
|
+
## Limitations
|
29
|
+
|
30
|
+
The following are limitations for ActiveResource/Rails versions
|
31
|
+
|
32
|
+
| ActiveSupport Version | Limitation |
|
33
|
+
|---------------------- | ----------------------------------------------------------------------- |
|
34
|
+
| 🛤️ 4.X | You cannot chain calls. Ie `Thing.where(fn: 'foo').where(ln: 'bar')`. <br> However, you can still access `original_params` and the `resource_class` and replicate it with <br>`call1 = Thing.where(fn: 'foo')`<br>`call1.resource_class.where(call1.original_params.merge(ln: 'bar'))` |
|
28
35
|
|
29
36
|
## Configuration
|
30
37
|
|
@@ -37,13 +37,14 @@ module CachedResource
|
|
37
37
|
cache_read(key) || find_via_reload(key, *arguments)
|
38
38
|
end
|
39
39
|
|
40
|
-
# Re/send the request to fetch the resource
|
41
|
-
# for the request.
|
40
|
+
# Re/send the request to fetch the resource
|
42
41
|
def find_via_reload(key, *arguments)
|
43
42
|
object = find_without_cache(*arguments)
|
43
|
+
return object unless should_cache?(object)
|
44
|
+
|
44
45
|
cache_collection_synchronize(object, *arguments) if cached_resource.collection_synchronize
|
45
46
|
return object if !cached_resource.cache_collections && is_any_collection?(*arguments)
|
46
|
-
cache_write(key, object)
|
47
|
+
cache_write(key, object, *arguments)
|
47
48
|
cache_read(key)
|
48
49
|
end
|
49
50
|
|
@@ -52,32 +53,38 @@ module CachedResource
|
|
52
53
|
# otherwise update an existing collection if possible.
|
53
54
|
def cache_collection_synchronize(object, *arguments)
|
54
55
|
if object.is_a? Enumerable
|
55
|
-
update_singles_cache(object)
|
56
|
+
update_singles_cache(object, *arguments)
|
56
57
|
# update the collection only if this is a subset of it
|
57
|
-
update_collection_cache(object) unless is_collection?(*arguments)
|
58
|
+
update_collection_cache(object, *arguments) unless is_collection?(*arguments)
|
58
59
|
else
|
59
|
-
update_collection_cache(object)
|
60
|
+
update_collection_cache(object, *arguments)
|
60
61
|
end
|
61
62
|
end
|
62
63
|
|
63
64
|
# Update the cache of singles with an array of updates.
|
64
|
-
def update_singles_cache(updates)
|
65
|
+
def update_singles_cache(updates, *arguments)
|
65
66
|
updates = Array(updates)
|
66
|
-
updates.each { |object| cache_write(cache_key(object.send(primary_key)), object) }
|
67
|
+
updates.each { |object| cache_write(cache_key(object.send(primary_key)), object, *arguments) }
|
67
68
|
end
|
68
69
|
|
69
70
|
# Update the "mother" collection with an array of updates.
|
70
|
-
def update_collection_cache(updates)
|
71
|
+
def update_collection_cache(updates, *arguments)
|
71
72
|
updates = Array(updates)
|
72
73
|
collection = cache_read(cache_key(cached_resource.collection_arguments))
|
73
74
|
|
74
75
|
if collection && !updates.empty?
|
75
76
|
index = collection.inject({}) { |hash, object| hash[object.send(primary_key)] = object; hash }
|
76
77
|
updates.each { |object| index[object.send(primary_key)] = object }
|
77
|
-
cache_write(cache_key(cached_resource.collection_arguments), index.values)
|
78
|
+
cache_write(cache_key(cached_resource.collection_arguments), index.values, *arguments)
|
78
79
|
end
|
79
80
|
end
|
80
81
|
|
82
|
+
# Avoid cache nil or [] objects
|
83
|
+
def should_cache?(object)
|
84
|
+
return false unless cached_resource.enabled
|
85
|
+
object.respond_to?(:empty?) ? !object.empty? : !!object
|
86
|
+
end
|
87
|
+
|
81
88
|
# Determine if the given arguments represent
|
82
89
|
# the entire collection of objects.
|
83
90
|
def is_collection?(*arguments)
|
@@ -101,7 +108,10 @@ module CachedResource
|
|
101
108
|
if cache.is_a? Enumerable
|
102
109
|
restored = cache.map { |record| full_dup(record) }
|
103
110
|
next restored unless respond_to?(:collection_parser)
|
104
|
-
collection_parser.new(restored)
|
111
|
+
collection_parser.new(restored).tap do |parser|
|
112
|
+
parser.resource_class = self
|
113
|
+
parser.original_params = json['original_params']
|
114
|
+
end
|
105
115
|
else
|
106
116
|
full_dup(cache)
|
107
117
|
end
|
@@ -112,8 +122,12 @@ module CachedResource
|
|
112
122
|
end
|
113
123
|
|
114
124
|
# Write an entry to the cache for the given key and value.
|
115
|
-
def cache_write(key, object)
|
116
|
-
|
125
|
+
def cache_write(key, object, *arguments)
|
126
|
+
options = arguments[1] || {}
|
127
|
+
params = options[:params]
|
128
|
+
prefix_options, query_options = split_options(params)
|
129
|
+
|
130
|
+
result = cached_resource.cache.write(key, object_to_json(object, prefix_options, query_options), :race_condition_ttl => cached_resource.race_condition_ttl, :expires_in => cached_resource.generate_ttl)
|
117
131
|
result && cached_resource.logger.info("#{CachedResource::Configuration::LOGGER_PREFIX} WRITE #{key}")
|
118
132
|
result
|
119
133
|
end
|
@@ -150,21 +164,34 @@ module CachedResource
|
|
150
164
|
end
|
151
165
|
|
152
166
|
def json_to_object(json)
|
153
|
-
|
154
|
-
|
155
|
-
|
167
|
+
resource = json['resource']
|
168
|
+
if resource.is_a? Array
|
169
|
+
resource.map do |attrs|
|
170
|
+
self.new(attrs["object"], attrs["persistence"]).tap do |resource|
|
171
|
+
resource.prefix_options = json['prefix_options']
|
172
|
+
end
|
173
|
+
end
|
156
174
|
else
|
157
|
-
self.new(
|
175
|
+
self.new(resource["object"], resource["persistence"]).tap do |resource|
|
176
|
+
resource.prefix_options = json['prefix_options']
|
177
|
+
end
|
158
178
|
end
|
159
179
|
end
|
160
180
|
|
161
|
-
def object_to_json(object)
|
181
|
+
def object_to_json(object, prefix_options, query_options)
|
162
182
|
if object.is_a? Enumerable
|
163
|
-
|
183
|
+
{
|
184
|
+
:resource => object.map { |o| { :object => o, :persistence => o.persisted? } },
|
185
|
+
:prefix_options => prefix_options,
|
186
|
+
:original_params => query_options
|
187
|
+
}.to_json
|
164
188
|
elsif object.nil?
|
165
189
|
nil.to_json
|
166
190
|
else
|
167
|
-
{
|
191
|
+
{
|
192
|
+
:resource => { :object => object, :persistence => object.persisted? },
|
193
|
+
:prefix_options => prefix_options
|
194
|
+
}.to_json
|
168
195
|
end
|
169
196
|
end
|
170
197
|
end
|
@@ -18,6 +18,8 @@ describe CachedResource do
|
|
18
18
|
end
|
19
19
|
|
20
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'}]
|
21
23
|
@other_thing = {:thing => {:id => 1, :name => "Ari"}}
|
22
24
|
@thing2 = {:thing => {:id => 2, :name => "Joe"}}
|
23
25
|
@other_thing2 = {:thing => {:id => 2, :name => "Jeb"}}
|
@@ -31,6 +33,7 @@ describe CachedResource do
|
|
31
33
|
@other_string_thing_json = @other_string_thing.to_json
|
32
34
|
@date_thing_json = @date_thing.to_json
|
33
35
|
@nil_thing = nil.to_json
|
36
|
+
@empty_array_thing = [].to_json
|
34
37
|
@not_the_thing = {:not_the_thing => {:id => 1, :name => "Not"}}
|
35
38
|
@not_the_thing_json = @not_the_thing.to_json
|
36
39
|
end
|
@@ -57,6 +60,7 @@ describe CachedResource do
|
|
57
60
|
mock.get "/things/1.json?foo=bar", {}, @thing_json
|
58
61
|
mock.get "/things/fded.json", {}, @string_thing_json
|
59
62
|
mock.get "/things.json?name=42", {}, @nil_thing, 404
|
63
|
+
mock.get "/things.json?name=43", {}, @empty_array_thing
|
60
64
|
mock.get "/things/4.json", {}, @date_thing_json
|
61
65
|
mock.get "/not_the_things/1.json", {}, @not_the_thing_json
|
62
66
|
end
|
@@ -64,15 +68,19 @@ describe CachedResource do
|
|
64
68
|
|
65
69
|
it "should cache a response" do
|
66
70
|
result = Thing.find(1)
|
67
|
-
|
68
71
|
read_from_cache("thing/1").should == result
|
69
72
|
end
|
70
73
|
|
71
|
-
it "
|
72
|
-
|
74
|
+
it "shouldn't cache nil response" do
|
75
|
+
Thing.find(:all, :params => { :name => '42' })
|
73
76
|
read_from_cache("thing/all/name/42").should == nil
|
74
77
|
end
|
75
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
|
+
|
76
84
|
it "should cache a response for a string primary key" do
|
77
85
|
result = Thing.find("fded")
|
78
86
|
read_from_cache("thing/fded").should == result
|
@@ -215,13 +223,39 @@ describe CachedResource do
|
|
215
223
|
cached.should be_instance_of(ActiveResource::Collection)
|
216
224
|
end
|
217
225
|
|
218
|
-
it "should return
|
226
|
+
it "should return a chainable instance of the collection_parser" do
|
219
227
|
Thing.cached_resource.cache.clear
|
220
228
|
class CustomCollection < ActiveResource::Collection; end
|
221
229
|
Thing.collection_parser = CustomCollection
|
222
|
-
|
223
|
-
|
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"}}')
|
224
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]}
|
225
259
|
end
|
226
260
|
else
|
227
261
|
it "should return an Array" do
|
@@ -505,14 +539,9 @@ describe CachedResource do
|
|
505
539
|
end
|
506
540
|
end
|
507
541
|
|
508
|
-
it "should cache a response" do
|
542
|
+
it "should not cache a response" do
|
509
543
|
result = Thing.find(1)
|
510
|
-
read_from_cache("thing/1").should
|
511
|
-
end
|
512
|
-
|
513
|
-
it "should cache a response for a string primary key" do
|
514
|
-
result = Thing.find("fded")
|
515
|
-
read_from_cache("thing/fded").should == result
|
544
|
+
read_from_cache("thing/1").should be_nil
|
516
545
|
end
|
517
546
|
|
518
547
|
it "should always remake the request" do
|
@@ -528,42 +557,6 @@ describe CachedResource do
|
|
528
557
|
Thing.find("fded")
|
529
558
|
ActiveResource::HttpMock.requests.length.should == 2
|
530
559
|
end
|
531
|
-
|
532
|
-
it "should rewrite the cache for each request" do
|
533
|
-
Thing.find(1)
|
534
|
-
old_result = read_from_cache("thing/1")
|
535
|
-
|
536
|
-
# change the response
|
537
|
-
ActiveResource::HttpMock.reset!
|
538
|
-
ActiveResource::HttpMock.respond_to do |mock|
|
539
|
-
mock.get "/things/1.json", {}, @other_thing_json
|
540
|
-
end
|
541
|
-
|
542
|
-
Thing.find(1)
|
543
|
-
new_result = read_from_cache("thing/1")
|
544
|
-
# since active resources are equal if and only if they
|
545
|
-
# are the same object or an instance of the same class,
|
546
|
-
# not new?, and have the same id.
|
547
|
-
new_result.name.should_not == old_result.name
|
548
|
-
end
|
549
|
-
|
550
|
-
it "should rewrite the cache for each request for a string primary key" do
|
551
|
-
Thing.find("fded")
|
552
|
-
old_result = read_from_cache("thing/fded")
|
553
|
-
|
554
|
-
# change the response
|
555
|
-
ActiveResource::HttpMock.reset!
|
556
|
-
ActiveResource::HttpMock.respond_to do |mock|
|
557
|
-
mock.get "/things/fded.json", {}, @other_string_thing_json
|
558
|
-
end
|
559
|
-
|
560
|
-
Thing.find("fded")
|
561
|
-
new_result = read_from_cache("thing/fded")
|
562
|
-
# since active resources are equal if and only if they
|
563
|
-
# are the same object or an instance of the same class,
|
564
|
-
# not new?, and have the same id.
|
565
|
-
new_result.name.should_not == old_result.name
|
566
|
-
end
|
567
560
|
end
|
568
561
|
|
569
562
|
describe "when cache_collections is disabled" do
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cached_resource
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.
|
4
|
+
version: 7.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Morgan Brown
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-03-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activeresource
|
@@ -89,6 +89,7 @@ files:
|
|
89
89
|
- ".github/workflows/ruby.yml"
|
90
90
|
- ".gitignore"
|
91
91
|
- ".rspec"
|
92
|
+
- FUNDING.json
|
92
93
|
- Gemfile
|
93
94
|
- LICENSE
|
94
95
|
- README.md
|
@@ -132,4 +133,8 @@ rubygems_version: 3.1.6
|
|
132
133
|
signing_key:
|
133
134
|
specification_version: 4
|
134
135
|
summary: Caching for ActiveResource
|
135
|
-
test_files:
|
136
|
+
test_files:
|
137
|
+
- spec/cached_resource/cached_resource_spec.rb
|
138
|
+
- spec/cached_resource/caching_spec.rb
|
139
|
+
- spec/cached_resource/configuration_spec.rb
|
140
|
+
- spec/spec_helper.rb
|