cached_resource 5.1.0 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 649cd7b534f3afc2628e4400f3b08e05769ed68b
4
- data.tar.gz: 4e47e85d1ae399953ae9544bfe8b0ae0de9c99c7
2
+ SHA256:
3
+ metadata.gz: 29a5076e42c43b34a6d5b430c8dc16ce062dd3f7f1ac4f63f992efef03b3ccf9
4
+ data.tar.gz: ff819f199885c83cb65a6e21d1a319c5bb6b747dd467870c913043765fbfa111
5
5
  SHA512:
6
- metadata.gz: dabb024419aeb48a493fe0d4b1bdffdf7177b17a6c4fbcd573f8fbe99ee756acbbfb809daf8dd8a314843ab32bde126b9e5609b941864e5bc95f99949d6e00fe
7
- data.tar.gz: 6008d2ed179cd97d1187cfe4a0f15922a48e1b5ae88af77f857643ac05e76eb8d2326675ecaeca2b78b4d3df75d6fb3c294a5492d65458a6d4bf0a7453fb36e5
6
+ metadata.gz: ea526acccd50610cf5c39248c9c0180c7a6ca1ca42cb6975df9b6f97b668febda6fc17684e9a9a7ca4b371798019d7d5e818e7a34e428702aeb5f475859ddbae
7
+ data.tar.gz: cc087aa9dcc0ad28b8779cf4412fd999b84019b7398d610e2e9e271b14d550bb9baa9dd039a0fa10da6bd15c83f76cba843769723e7bd0ca13a8d3f2f24c0e5e
data/.travis.yml CHANGED
@@ -1,5 +1,7 @@
1
1
  language: ruby
2
2
  sudo: false
3
+ before_install:
4
+ - gem install bundler:1.17.3
3
5
  cache: bundler
4
6
 
5
7
  rvm:
data/README.md CHANGED
@@ -1,8 +1,11 @@
1
- # CachedResource [![Build Status](https://secure.travis-ci.org/mhgbrown/cached_resource.png)](http://travis-ci.org/mhgbrown/cached_resource)
1
+ # CachedResource [![Build Status](https://travis-ci.org/mhgbrown/cached_resource.svg?branch=master)](http://travis-ci.org/mhgbrown/cached_resource)
2
2
  CachedResource is a Ruby gem whose goal is to increase the performance of interacting with web services via ActiveResource by caching responses based on request parameters. It can help reduce the lag created by making repeated requests across a network.
3
3
 
4
4
  ## Installation
5
- gem install cached_resource
5
+
6
+ ```ruby
7
+ gem install cached_resource
8
+ ```
6
9
 
7
10
  ## Compatibility
8
11
  CachedResource supports the following Ruby versions:
@@ -24,15 +27,19 @@ For previously supported versions, use 4.2.0 or below.
24
27
  ## Configuration
25
28
  **Set up CachedResource across all ActiveResources:**
26
29
 
27
- class ActiveResource::Base
28
- cached_resource
29
- end
30
+ ```ruby
31
+ class ActiveResource::Base
32
+ cached_resource
33
+ end
34
+ ```
30
35
 
31
36
  Or set up CachedResource for a single class:
32
37
 
33
- class MyActiveResource < ActiveResource::Base
34
- cached_resource
35
- end
38
+ ```ruby
39
+ class MyActiveResource < ActiveResource::Base
40
+ cached_resource
41
+ end
42
+ ```
36
43
 
37
44
  ### Options
38
45
  CachedResource accepts the following options:
@@ -49,81 +56,115 @@ CachedResource accepts the following options:
49
56
 
50
57
  You can set them like this:
51
58
 
52
- cached_resource :cache => MyCacheStore.new, :ttl => 60, :collection_synchronize => true, :logger => MyLogger.new
59
+ ```ruby
60
+ cached_resource :cache => MyCacheStore.new, :ttl => 60, :collection_synchronize => true, :logger => MyLogger.new
61
+ ```
53
62
 
54
63
  You can also change them on the fly.
55
64
 
56
65
  Turn CachedResource off. This will cause all responses to be retrieved normally (i.e. via the network). All responses will still be cached.
57
66
 
58
- MyActiveResource.cached_resource.off!
67
+ ```ruby
68
+ MyActiveResource.cached_resource.off!
69
+ ```
59
70
 
60
71
  Turn CachedResource on.
61
-
62
- MyActiveResource.cached_resource.on!
72
+ ```ruby
73
+ MyActiveResource.cached_resource.on!
74
+ ```
63
75
 
64
76
  Set the cache expiry time to 60 seconds.
65
77
 
66
- MyActiveResource.cached_resource.ttl = 60
78
+ ```ruby
79
+ MyActiveResource.cached_resource.ttl = 60
80
+ ```
67
81
 
68
82
  Enable cache expiry time randomization, allowing it to fall randomly between 60 and 120 seconds.
69
83
 
70
- MyActiveResource.cached_resource.ttl_randomization = true
84
+ ```ruby
85
+ MyActiveResource.cached_resource.ttl_randomization = true
86
+ ```
71
87
 
72
88
  Change the cache expiry time randomization scale so that the cache expiry time falls randomly between 30 and 180 seconds.
73
89
 
74
- MyActiveResource.cached_resource.ttl_randomization_scale = 0.5..3
75
-
90
+ ```ruby
91
+ MyActiveResource.cached_resource.ttl_randomization_scale = 0.5..3
92
+ ```
76
93
  Enable collection synchronization. This will cause a call to `MyActiveResource.all` to also create cache entries for each of its members. So, for example, a later call to `MyActiveResource.find(1)` will be read from the cache instead of requested from the remote service.
77
94
 
78
- MyActiveResource.cached_resource.collection_synchronize = true
79
-
95
+ ```ruby
96
+ MyActiveResource.cached_resource.collection_synchronize = true
97
+ ```
80
98
  Change the arguments that identify the principal collection request. If for some reason you are concerned with a collection that is retrieved at a "non-standard" URL, you may specify the Ruby arguments that produce that URL. When `collection_synchronize` is `true`, the collection returned from a request that matches these arguments will be cached and later updated when one of its members or a subset is retrieved.
81
99
 
82
- MyActiveResource.cached_resource.collection_arguments = [:all, :params => {:name => "Bob"}]
83
-
100
+ ```ruby
101
+ MyActiveResource.cached_resource.collection_arguments = [:all, :params => {:name => "Bob"}]
102
+ ```
84
103
  Set a different logger.
85
104
 
86
- MyActiveResource.cached_resource.logger = MyLogger.new
87
-
105
+ ```ruby
106
+ MyActiveResource.cached_resource.logger = MyLogger.new
107
+ ```
88
108
  Set a different cache store.
89
109
 
90
- MyActiveResource.cached_resource.cache = MyCacheStore.new
110
+ ```ruby
111
+ MyActiveResource.cached_resource.cache = MyCacheStore.new
112
+ ```
91
113
 
92
114
  ### Caveats
93
115
  If you set up CachedResource across all ActiveResources or any subclass of ActiveResource that will be inherited by other classes and you want some of those others to have independent CachedResource configurations, then check out the example below:
94
116
 
95
- class ActiveResource::Base
96
- cached_resource
97
- end
98
-
99
- class MyActiveResource < ActiveResource::Base
100
- self.cached_resource = CachedResource::Configuration.new(:collection_synchronize => true)
101
- end
102
-
117
+ ```ruby
118
+ class ActiveResource::Base
119
+ cached_resource
120
+ end
121
+ ```
122
+
123
+ ```ruby
124
+ class MyActiveResource < ActiveResource::Base
125
+ self.cached_resource = CachedResource::Configuration.new(:collection_synchronize => true)
126
+ end
127
+ ```
103
128
  ## Usage
104
129
  Sit back and relax! If you need to reload a particular request you can pass `:reload => true` into the options hash like this:
105
130
 
106
- MyActiveResource.find(:all, :reload => true)
107
-
131
+ ```ruby
132
+ MyActiveResource.find(:all, :reload => true)
133
+ ```
108
134
  If you need to clear the entire cache just do the following:
109
135
 
110
- MyActiveResource.clear_cache
111
-
136
+ ```ruby
137
+ MyActiveResource.clear_cache
138
+ ```
112
139
  ---
113
140
  Sometimes you might have a case the resource pathing is non-unique per call. This can create a situation where your caching the same result for multiple calls:
114
141
 
115
- MyActiveResource.find(:one, from: "/admin/shop.json")
142
+ ```ruby
143
+ MyActiveResource.find(:one, from: "/admin/shop.json")
144
+ ```
116
145
 
117
146
  Since resources are cached with an argument based key, you may pass in extra data to be appended to the cache key:
118
147
 
119
- MyActiveResource.find(:one, from: "/admin/shop.json", uid: "unique value")
120
-
148
+ ```ruby
149
+ MyActiveResource.find(:one, from: "/admin/shop.json", uid: "unique value")
150
+ ```
121
151
  ## Testing
122
- rake
123
152
 
124
- or to test all supported environments
153
+ ```ruby
154
+ rake
155
+ ```
156
+
157
+ or to test all supported environments, make sure appraisal is setup
158
+
159
+ ```ruby
160
+ bundle exec appraisal install
161
+ ```
162
+
163
+ and then run
125
164
 
126
- bundle exec appraisal rake
165
+ ```ruby
166
+ bundle exec appraisal rake
167
+ ```
127
168
 
128
169
  For more details, head over to the [appraisal](https://github.com/thoughtbot/appraisal) documentation.
129
170
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: ../
2
+ remote: ..
3
3
  specs:
4
- cached_resource (5.1.0)
4
+ cached_resource (5.1.3)
5
5
  activeresource (>= 4.0)
6
6
  activesupport (>= 4.0)
7
7
  nilio (>= 1.0)
@@ -93,7 +93,7 @@ GEM
93
93
  rails-deprecated_sanitizer (>= 1.0.1)
94
94
  rails-html-sanitizer (1.0.3)
95
95
  loofah (~> 2.0)
96
- rails-observers (0.1.4)
96
+ rails-observers (0.1.5)
97
97
  activemodel (>= 4.0)
98
98
  railties (4.2.9)
99
99
  actionpack (= 4.2.9)
@@ -137,4 +137,4 @@ DEPENDENCIES
137
137
  rspec
138
138
 
139
139
  BUNDLED WITH
140
- 1.15.1
140
+ 1.17.3
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: ../
2
+ remote: ..
3
3
  specs:
4
- cached_resource (5.1.0)
4
+ cached_resource (5.1.3)
5
5
  activeresource (>= 4.0)
6
6
  activesupport (>= 4.0)
7
7
  nilio (>= 1.0)
@@ -37,19 +37,18 @@ GEM
37
37
  globalid (>= 0.3.6)
38
38
  activemodel (5.0.2)
39
39
  activesupport (= 5.0.2)
40
- activemodel-serializers-xml (1.0.1)
40
+ activemodel-serializers-xml (1.0.2)
41
41
  activemodel (> 5.x)
42
- activerecord (> 5.x)
43
42
  activesupport (> 5.x)
44
43
  builder (~> 3.1)
45
44
  activerecord (5.0.2)
46
45
  activemodel (= 5.0.2)
47
46
  activesupport (= 5.0.2)
48
47
  arel (~> 7.0)
49
- activeresource (5.0.0)
50
- activemodel (>= 5.0, < 6)
48
+ activeresource (5.1.1)
49
+ activemodel (>= 5.0, < 7)
51
50
  activemodel-serializers-xml (~> 1.0)
52
- activesupport (>= 5.0, < 6)
51
+ activesupport (>= 5.0, < 7)
53
52
  activesupport (5.0.2)
54
53
  concurrent-ruby (~> 1.0, >= 1.0.2)
55
54
  i18n (~> 0.7)
@@ -147,4 +146,4 @@ DEPENDENCIES
147
146
  rspec
148
147
 
149
148
  BUNDLED WITH
150
- 1.15.1
149
+ 1.17.3
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: ../
2
+ remote: ..
3
3
  specs:
4
- cached_resource (5.1.0)
4
+ cached_resource (5.1.3)
5
5
  activeresource (>= 4.0)
6
6
  activesupport (>= 4.0)
7
7
  nilio (>= 1.0)
@@ -37,19 +37,18 @@ GEM
37
37
  globalid (>= 0.3.6)
38
38
  activemodel (5.1.2)
39
39
  activesupport (= 5.1.2)
40
- activemodel-serializers-xml (1.0.1)
40
+ activemodel-serializers-xml (1.0.2)
41
41
  activemodel (> 5.x)
42
- activerecord (> 5.x)
43
42
  activesupport (> 5.x)
44
43
  builder (~> 3.1)
45
44
  activerecord (5.1.2)
46
45
  activemodel (= 5.1.2)
47
46
  activesupport (= 5.1.2)
48
47
  arel (~> 8.0)
49
- activeresource (5.0.0)
50
- activemodel (>= 5.0, < 6)
48
+ activeresource (5.1.1)
49
+ activemodel (>= 5.0, < 7)
51
50
  activemodel-serializers-xml (~> 1.0)
52
- activesupport (>= 5.0, < 6)
51
+ activesupport (>= 5.0, < 7)
53
52
  activesupport (5.1.2)
54
53
  concurrent-ruby (~> 1.0, >= 1.0.2)
55
54
  i18n (~> 0.7)
@@ -147,4 +146,4 @@ DEPENDENCIES
147
146
  rspec
148
147
 
149
148
  BUNDLED WITH
150
- 1.15.1
149
+ 1.17.3
@@ -24,8 +24,8 @@ module CachedResource
24
24
  end
25
25
 
26
26
  # Clear the cache.
27
- def clear_cache
28
- cache_clear
27
+ def clear_cache(options=nil)
28
+ cache_clear(options)
29
29
  end
30
30
 
31
31
  private
@@ -85,13 +85,18 @@ module CachedResource
85
85
  # Read a entry from the cache for the given key.
86
86
  def cache_read(key)
87
87
  object = cached_resource.cache.read(key).try do |json_cache|
88
- cache = json_to_object(JSON.parse(json_cache))
89
- if cache.is_a? Enumerable
90
- restored = cache.map { |record| full_dup(record) }
91
- next restored unless respond_to?(:collection_parser)
92
- collection_parser.new(restored)
93
- else
94
- full_dup(cache)
88
+
89
+ json = ActiveSupport::JSON.decode(json_cache)
90
+
91
+ unless json.nil?
92
+ cache = json_to_object(json)
93
+ if cache.is_a? Enumerable
94
+ restored = cache.map { |record| full_dup(record) }
95
+ next restored unless respond_to?(:collection_parser)
96
+ collection_parser.new(restored)
97
+ else
98
+ full_dup(cache)
99
+ end
95
100
  end
96
101
  end
97
102
  object && cached_resource.logger.info("#{CachedResource::Configuration::LOGGER_PREFIX} READ #{key}")
@@ -100,21 +105,32 @@ module CachedResource
100
105
 
101
106
  # Write an entry to the cache for the given key and value.
102
107
  def cache_write(key, object)
103
- result = cached_resource.cache.write(key, object.to_json, :race_condition_ttl => cached_resource.race_condition_ttl, :expires_in => cached_resource.generate_ttl)
108
+ result = cached_resource.cache.write(key, object_to_json(object), :race_condition_ttl => cached_resource.race_condition_ttl, :expires_in => cached_resource.generate_ttl)
104
109
  result && cached_resource.logger.info("#{CachedResource::Configuration::LOGGER_PREFIX} WRITE #{key}")
105
110
  result
106
111
  end
107
112
 
108
113
  # Clear the cache.
109
- def cache_clear
110
- cached_resource.cache.clear.tap do |result|
111
- cached_resource.logger.info("#{CachedResource::Configuration::LOGGER_PREFIX} CLEAR")
114
+ def cache_clear(options=nil)
115
+ # Memcache doesn't support delete_matched, which can also be computationally expensive
116
+ if cached_resource.cache.class.to_s == 'ActiveSupport::Cache::MemCacheStore' || options.try(:fetch,:all)
117
+ cached_resource.cache.clear.tap do |result|
118
+ cached_resource.logger.info("#{CachedResource::Configuration::LOGGER_PREFIX} CLEAR ALL")
119
+ end
120
+ else
121
+ cached_resource.cache.delete_matched("^#{name_key}/*").tap do |result|
122
+ cached_resource.logger.info("#{CachedResource::Configuration::LOGGER_PREFIX} CLEAR #{name_key}/*")
123
+ end
112
124
  end
113
125
  end
114
126
 
115
127
  # Generate the request cache key.
116
128
  def cache_key(*arguments)
117
- "#{name.parameterize.gsub("-", "/")}/#{arguments.join('/')}".downcase.delete(' ')
129
+ "#{name_key}/#{arguments.join('/')}".downcase.delete(' ')
130
+ end
131
+
132
+ def name_key
133
+ name.parameterize.gsub("-", "/")
118
134
  end
119
135
 
120
136
  # Make a full duplicate of an ActiveResource record.
@@ -127,12 +143,22 @@ module CachedResource
127
143
 
128
144
  def json_to_object(json)
129
145
  if json.is_a? Array
130
- json.map { |attrs| self.new(attrs) }
146
+ json.map { |attrs|
147
+ self.new(attrs["object"], attrs["persistence"]) }
131
148
  else
132
- self.new(json)
149
+ self.new(json["object"], json["persistence"])
133
150
  end
134
151
  end
135
152
 
153
+ def object_to_json(object)
154
+ if object.is_a? Enumerable
155
+ object.map { |o| { :object => o, :persistence => o.persisted? } }.to_json
156
+ elsif object.nil?
157
+ nil.to_json
158
+ else
159
+ { :object => object, :persistence => object.persisted? }.to_json
160
+ end
161
+ end
136
162
  end
137
163
  end
138
164
  end
@@ -1,3 +1,3 @@
1
1
  module CachedResource
2
- VERSION = "5.1.0"
2
+ VERSION = "5.2.0"
3
3
  end
@@ -12,6 +12,11 @@ describe CachedResource do
12
12
  cached_resource
13
13
  end
14
14
 
15
+ class NotTheThing < ActiveResource::Base
16
+ self.site = "http://api.notthething.com"
17
+ cached_resource
18
+ end
19
+
15
20
  @thing = {:thing => {:id => 1, :name => "Ada"}}
16
21
  @other_thing = {:thing => {:id => 1, :name => "Ari"}}
17
22
  @thing2 = {:thing => {:id => 2, :name => "Joe"}}
@@ -19,15 +24,22 @@ describe CachedResource do
19
24
  @thing3 = {:thing => {:id => 3, :name => "Stu"}}
20
25
  @string_thing = {:thing => {:id => "fded", :name => "Lev"}}
21
26
  @other_string_thing = {:thing => {:id => "fded", :name => "Lon"}}
27
+ @date_thing = {:thing => {:id => 4, :created_at => Time.utc(2020).iso8601}}
22
28
  @thing_json = @thing.to_json
23
29
  @other_thing_json = @other_thing.to_json
24
30
  @string_thing_json = @string_thing.to_json
25
31
  @other_string_thing_json = @other_string_thing.to_json
32
+ @date_thing_json = @date_thing.to_json
33
+ @nil_thing = nil.to_json
34
+ @not_the_thing = {:not_the_thing => {:id => 1, :name => "Not"}}
35
+ @not_the_thing_json = @not_the_thing.to_json
26
36
  end
27
37
 
28
38
  after(:each) do
29
39
  Thing.cached_resource.cache.clear
30
40
  Object.send(:remove_const, :Thing)
41
+ NotTheThing.cached_resource.cache.clear
42
+ Object.send(:remove_const, :NotTheThing)
31
43
  end
32
44
 
33
45
  describe "when enabled" do
@@ -36,12 +48,17 @@ describe CachedResource do
36
48
  # to make sure it works
37
49
  Thing.cached_resource.cache.clear
38
50
  Thing.cached_resource.on!
51
+ NotTheThing.cached_resource.cache.clear
52
+ NotTheThing.cached_resource.on!
39
53
 
40
54
  ActiveResource::HttpMock.reset!
41
55
  ActiveResource::HttpMock.respond_to do |mock|
42
56
  mock.get "/things/1.json", {}, @thing_json
43
57
  mock.get "/things/1.json?foo=bar", {}, @thing_json
44
58
  mock.get "/things/fded.json", {}, @string_thing_json
59
+ mock.get "/things.json?name=42", {}, @nil_thing, 404
60
+ mock.get "/things/4.json", {}, @date_thing_json
61
+ mock.get "/not_the_things/1.json", {}, @not_the_thing_json
45
62
  end
46
63
  end
47
64
 
@@ -51,6 +68,11 @@ describe CachedResource do
51
68
  read_from_cache("thing/1").should == result
52
69
  end
53
70
 
71
+ it "should cache a nil response" do
72
+ result = Thing.find(:all, :params => { :name => '42' })
73
+ read_from_cache("thing/all/name/42").should == nil
74
+ end
75
+
54
76
  it "should cache a response for a string primary key" do
55
77
  result = Thing.find("fded")
56
78
  read_from_cache("thing/fded").should == result
@@ -67,8 +89,23 @@ describe CachedResource do
67
89
  read_from_cache("thing/1").should == nil
68
90
  end
69
91
 
92
+ it "should not empty the cache of NotTheThing when clear_cache is called on the Thing" do
93
+ result1 = Thing.find(1)
94
+ result2 = NotTheThing.find(1)
95
+ Thing.clear_cache
96
+ NotTheThing.send(:cache_read, 'notthething/1').should == result2
97
+ end
98
+
99
+ it "should empty all the cache when clear_cache is called on the Thing with :all option set" do
100
+ result1 = Thing.find(1)
101
+ result2 = NotTheThing.find(1)
102
+ Thing.clear_cache(all: true)
103
+ NotTheThing.send(:cache_read, 'notthething/1').should == nil
104
+ end
105
+
70
106
  it "should cache a response with the same persistence" do
71
107
  result1 = Thing.find(1)
108
+ result1.persisted?.should be true
72
109
  result2 = Thing.find(1)
73
110
  result1.persisted?.should == result2.persisted?
74
111
  end
@@ -158,6 +195,19 @@ describe CachedResource do
158
195
  result1.should_not be_frozen
159
196
  end
160
197
 
198
+ describe "when ActiveSupport.parse_json_times is enabled" do
199
+ before(:all) do
200
+ Time.zone = 'UTC'
201
+ ActiveSupport.parse_json_times = true
202
+ end
203
+
204
+ it "should convert date times to objects when reading from cache" do
205
+ Thing.find(4)
206
+
207
+ read_from_cache("thing/4").created_at.should == @date_thing[:thing][:created_at]
208
+ end
209
+ end
210
+
161
211
  shared_examples "collection_return_type" do
162
212
  if ActiveResource::VERSION::MAJOR >= 4
163
213
  it "should return an ActiveResource::Collection" do
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: 5.1.0
4
+ version: 5.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: 2018-08-09 00:00:00.000000000 Z
11
+ date: 2021-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activeresource
@@ -143,13 +143,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
143
143
  - !ruby/object:Gem::Version
144
144
  version: '0'
145
145
  requirements: []
146
- rubyforge_project:
147
- rubygems_version: 2.5.2
146
+ rubygems_version: 3.0.8
148
147
  signing_key:
149
148
  specification_version: 4
150
149
  summary: Caching for ActiveResource
151
- test_files:
152
- - spec/cached_resource/cached_resource_spec.rb
153
- - spec/cached_resource/caching_spec.rb
154
- - spec/cached_resource/configuration_spec.rb
155
- - spec/spec_helper.rb
150
+ test_files: []