cached_resource 2.0.1 → 2.1.1a
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +5 -0
- data/README.md +47 -9
- data/Rakefile +0 -1
- data/lib/cached_resource/cached_resource.rb +12 -2
- data/lib/cached_resource/caching.rb +66 -16
- data/lib/cached_resource/configuration.rb +7 -3
- data/lib/cached_resource/version.rb +1 -1
- data/spec/cached_resource/caching_spec.rb +137 -2
- data/spec/cached_resource/configuration_spec.rb +95 -7
- data/spec/spec_helper.rb +0 -5
- metadata +75 -83
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -4,14 +4,26 @@ CachedResource is a Ruby gem whose goal is to increase the performance of intera
|
|
4
4
|
## Installation
|
5
5
|
gem install cached_resource
|
6
6
|
|
7
|
+
## Compatibility
|
8
|
+
CachedResource has been tested with the following Ruby versions:
|
9
|
+
|
10
|
+
* 1.8.7
|
11
|
+
* 1.9.2
|
12
|
+
* 1.9.3
|
13
|
+
|
14
|
+
CachedResource is designed to be framework agnostic, but will hook into Rails for caching and logging. If there is a `Rails.cache` and/or a `Rails.logger`, then it _should_ be OK. It is known to work with the following Rails versions:
|
15
|
+
|
16
|
+
* 3.1.3
|
17
|
+
* 3.2.2
|
18
|
+
|
7
19
|
## Configuration
|
8
|
-
|
20
|
+
**Set up CachedResource across all ActiveResources:**
|
9
21
|
|
10
22
|
class ActiveResource::Base
|
11
23
|
cached_resource
|
12
24
|
end
|
13
25
|
|
14
|
-
|
26
|
+
Or set up CachedResource for a single class:
|
15
27
|
|
16
28
|
class MyActiveResource < ActiveResource::Base
|
17
29
|
cached_resource
|
@@ -20,18 +32,20 @@ Enable CachedResource for a single class.
|
|
20
32
|
### Options
|
21
33
|
CachedResource accepts the following options:
|
22
34
|
|
23
|
-
* `:
|
35
|
+
* `:enabled` Default: `true`
|
24
36
|
* `:ttl` The time in seconds until the cache should expire. Default: `604800`
|
37
|
+
* `:collection_synchronize` Use collections to generate cache entries for individuals. Update the existing cached principal collection with new individuals. Default: `false`
|
38
|
+
* `:collection_arguments` The arguments that identify the principal collection request. Default: `[:all]`
|
25
39
|
* `:logger` The logger to which CachedResource messages should be written. Default: The `Rails.logger` if available, or an `ActiveSupport::BufferedLogger`
|
26
|
-
* `:
|
40
|
+
* `:cache` The cache store that CacheResource should use. Default: The `Rails.cache` if available, or an `ActiveSupport::Cache::MemoryStore`
|
27
41
|
|
28
42
|
You can set them like this:
|
29
43
|
|
30
|
-
cached_resource :cache => MyCacheStore.new, :ttl => 60, :logger => MyLogger.new, :enabled => false
|
44
|
+
cached_resource :cache => MyCacheStore.new, :ttl => 60, :collection_synchronize => true, :logger => MyLogger.new, :enabled => false
|
31
45
|
|
32
|
-
You can also change
|
46
|
+
You can also change them on the fly.
|
33
47
|
|
34
|
-
Turn CachedResource off. This will cause all responses to be retrieved normally (i.e. via the network).
|
48
|
+
Turn CachedResource off. This will cause all responses to be retrieved normally (i.e. via the network). All responses will still be cached.
|
35
49
|
|
36
50
|
MyActiveResource.cached_resource.off!
|
37
51
|
|
@@ -43,6 +57,14 @@ Set the cache expiry time to 60 seconds.
|
|
43
57
|
|
44
58
|
MyActiveResource.cached_resource.ttl = 60
|
45
59
|
|
60
|
+
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.
|
61
|
+
|
62
|
+
MyActiveResource.cached_resource.collection_synchronize = true
|
63
|
+
|
64
|
+
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 is retrieved.
|
65
|
+
|
66
|
+
MyActiveResource.cached_resource.collection_arguments = [:all, :params => {:name => "Bob"}]
|
67
|
+
|
46
68
|
Set a different logger.
|
47
69
|
|
48
70
|
MyActiveResource.cached_resource.logger = MyLogger.new
|
@@ -51,8 +73,19 @@ Set a different cache store.
|
|
51
73
|
|
52
74
|
MyActiveResource.cached_resource.cache = MyCacheStore.new
|
53
75
|
|
76
|
+
### Caveats
|
77
|
+
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:
|
78
|
+
|
79
|
+
class ActiveResource::Base
|
80
|
+
cached_resource
|
81
|
+
end
|
82
|
+
|
83
|
+
class MyActiveResource < ActiveResource::Base
|
84
|
+
self.cached_resource = CachedResource::Configuration.new(:collection_synchronize => true)
|
85
|
+
end
|
86
|
+
|
54
87
|
## Usage
|
55
|
-
Sit back and relax! If you need to reload a particular request you can
|
88
|
+
Sit back and relax! If you need to reload a particular request you can pass `:reload => true` into the options hash like this:
|
56
89
|
|
57
90
|
MyActiveResource.find(:all, :reload => true)
|
58
91
|
|
@@ -63,5 +96,10 @@ Sit back and relax! If you need to reload a particular request you can do someth
|
|
63
96
|
* quamen and [this gist](http://gist.github.com/947734)
|
64
97
|
* latimes and [this plugin](http://github.com/latimes/cached_resource)
|
65
98
|
|
99
|
+
## Feedback/Problems
|
100
|
+
Feedback is greatly appreciated! Check out this project's [issue tracker](https://github.com/Ahsizara/cached_resource/issues) if you've got anything to say.
|
101
|
+
|
66
102
|
## Future Work
|
67
|
-
|
103
|
+
This may change at any time.
|
104
|
+
|
105
|
+
* Consider checksums to improve the determination of freshness/changédness
|
data/Rakefile
CHANGED
@@ -9,12 +9,12 @@ module CachedResource
|
|
9
9
|
class << self
|
10
10
|
attr_accessor :cached_resource
|
11
11
|
|
12
|
-
#
|
12
|
+
# Initialize cached resource or retrieve the current cached resource configuration.
|
13
13
|
def cached_resource(options={})
|
14
14
|
defined?(@cached_resource) && @cached_resource || setup_cached_resource!(options)
|
15
15
|
end
|
16
16
|
|
17
|
-
#
|
17
|
+
# Set up cached resource for this class by creating a new configuration
|
18
18
|
# and establishing the necessary methods.
|
19
19
|
def setup_cached_resource!(options)
|
20
20
|
@cached_resource = CachedResource::Configuration.new(options)
|
@@ -24,5 +24,15 @@ module CachedResource
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
module ClassMethods
|
28
|
+
# Copy a superclass's cached resource configuration if
|
29
|
+
# it's defined. Unfortunately, this means that any subclass
|
30
|
+
# that wants an independent configuration will need to execute:
|
31
|
+
# self.cached_resource = CachedResource::Configuration.new(options={})
|
32
|
+
def inherited(child)
|
33
|
+
child.cached_resource = self.cached_resource if defined?(@cached_resource)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
27
37
|
end
|
28
38
|
end
|
@@ -11,41 +11,91 @@ module CachedResource
|
|
11
11
|
end
|
12
12
|
|
13
13
|
module ClassMethods
|
14
|
-
#
|
15
|
-
# if :reload is set to true or caching is disabled
|
14
|
+
# Find a resource using the cache or resend the request
|
15
|
+
# if :reload is set to true or caching is disabled.
|
16
16
|
def find_with_cache(*arguments)
|
17
17
|
arguments << {} unless arguments.last.is_a?(Hash)
|
18
18
|
should_reload = arguments.last.delete(:reload) || !cached_resource.enabled
|
19
19
|
arguments.pop if arguments.last.empty?
|
20
20
|
key = cache_key(arguments)
|
21
21
|
|
22
|
-
|
23
|
-
(should_reload ? find_via_reload(key, *arguments) : find_via_cache(key, *arguments))
|
24
|
-
rescue ActiveResource::ServerError, ActiveResource::ConnectionError, SocketError => e
|
25
|
-
raise(e)
|
26
|
-
end
|
22
|
+
should_reload ? find_via_reload(key, *arguments) : find_via_cache(key, *arguments)
|
27
23
|
end
|
28
24
|
|
29
25
|
private
|
30
26
|
|
31
|
-
#
|
27
|
+
# Try to find a cached response for the given key. If
|
32
28
|
# no cache entry exists, send a new request.
|
33
29
|
def find_via_cache(key, *arguments)
|
34
|
-
|
35
|
-
result && cached_resource.logger.info("#{CachedResource::Configuration::LOGGER_PREFIX} READ #{key} for #{arguments.inspect}")
|
36
|
-
result || find_via_reload(key, *arguments)
|
30
|
+
cache_read(key) || find_via_reload(key, *arguments)
|
37
31
|
end
|
38
32
|
|
39
|
-
#
|
33
|
+
# Re/send the request to fetch the resource. Cache the response
|
40
34
|
# for the request.
|
41
35
|
def find_via_reload(key, *arguments)
|
42
|
-
|
43
|
-
|
44
|
-
|
36
|
+
object = find_without_cache(*arguments)
|
37
|
+
cache_collection_synchronize(object, *arguments) if cached_resource.collection_synchronize
|
38
|
+
cache_write(key, object)
|
39
|
+
object
|
40
|
+
end
|
41
|
+
|
42
|
+
# If this is a pure, unadulterated "all" request
|
43
|
+
# write cache entries for all its members
|
44
|
+
# otherwise update an existing collection if possible.
|
45
|
+
def cache_collection_synchronize(object, *arguments)
|
46
|
+
if object.is_a? Array
|
47
|
+
update_singles_cache(object)
|
48
|
+
# update the collection only if this is a subset of it
|
49
|
+
update_collection_cache(object) unless is_collection?(*arguments)
|
50
|
+
else
|
51
|
+
update_collection_cache(object)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Update the cache of singles with an array of updates.
|
56
|
+
def update_singles_cache(updates)
|
57
|
+
updates = Array(updates)
|
58
|
+
updates.each { |object| cache_write(object.send(primary_key), object) }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Update the "mother" collection with an array of updates.
|
62
|
+
def update_collection_cache(updates)
|
63
|
+
updates = Array(updates)
|
64
|
+
collection = cache_read(cached_resource.collection_arguments)
|
65
|
+
|
66
|
+
if collection && !updates.empty?
|
67
|
+
store = RUBY_VERSION.to_f < 1.9 ? ActiveSupport::OrderedHash.new : {}
|
68
|
+
index = collection.inject(store) { |hash, object| hash[object.send(primary_key)] = object; hash }
|
69
|
+
updates.each { |object| index[object.send(primary_key)] = object }
|
70
|
+
cache_write(cached_resource.collection_arguments, index.values)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Determine if the given arguments represent
|
75
|
+
# the entire collection of objects.
|
76
|
+
def is_collection?(*arguments)
|
77
|
+
arguments == cached_resource.collection_arguments
|
78
|
+
end
|
79
|
+
|
80
|
+
# Read a entry from the cache for the given key.
|
81
|
+
# The key is processed to make sure it is valid.
|
82
|
+
def cache_read(key)
|
83
|
+
key = cache_key(Array(key)) unless key.is_a? String
|
84
|
+
object = cached_resource.cache.read(key).try(:dup)
|
85
|
+
object && cached_resource.logger.info("#{CachedResource::Configuration::LOGGER_PREFIX} READ #{key}")
|
86
|
+
object
|
87
|
+
end
|
88
|
+
|
89
|
+
# Write an entry to the cache for the given key and value.
|
90
|
+
# The key is processed to make sure it is valid.
|
91
|
+
def cache_write(key, object)
|
92
|
+
key = cache_key(Array(key)) unless key.is_a? String
|
93
|
+
result = cached_resource.cache.write(key, object, :expires_in => cached_resource.ttl)
|
94
|
+
result && cached_resource.logger.info("#{CachedResource::Configuration::LOGGER_PREFIX} WRITE #{key}")
|
45
95
|
result
|
46
96
|
end
|
47
97
|
|
48
|
-
#
|
98
|
+
# Generate the request cache key.
|
49
99
|
def cache_key(*arguments)
|
50
100
|
"#{name.parameterize.gsub("-", "/")}/#{arguments.join('/')}".downcase
|
51
101
|
end
|
@@ -16,23 +16,27 @@ module CachedResource
|
|
16
16
|
# defaults. The following options exist for cached resource:
|
17
17
|
# :enabled, default: true
|
18
18
|
# :ttl, default: 604800
|
19
|
+
# :collection_synchronize, default: false,
|
20
|
+
# :collection_arguments, default: [:all]
|
19
21
|
# :cache, default: Rails.cache or ActiveSupport::Cache::MemoryStore.new,
|
20
22
|
# :logger, default: Rails.logger or ActiveSupport::BufferedLogger.new(NilIO.new)
|
21
23
|
def initialize(options={})
|
22
|
-
super
|
24
|
+
super({
|
23
25
|
:enabled => true,
|
24
26
|
:ttl => 604800,
|
27
|
+
:collection_synchronize => false,
|
28
|
+
:collection_arguments => [:all],
|
25
29
|
:cache => defined?(Rails.cache) && Rails.cache || CACHE,
|
26
30
|
:logger => defined?(Rails.logger) && Rails.logger || LOGGER
|
27
31
|
}.merge(options))
|
28
32
|
end
|
29
33
|
|
30
|
-
#
|
34
|
+
# Enables caching.
|
31
35
|
def on!
|
32
36
|
self.enabled = true
|
33
37
|
end
|
34
38
|
|
35
|
-
#
|
39
|
+
# Disables caching.
|
36
40
|
def off!
|
37
41
|
self.enabled = false
|
38
42
|
end
|
@@ -10,6 +10,9 @@ describe CachedResource do
|
|
10
10
|
|
11
11
|
@thing = {:thing => {:id => 1, :name => "Ada"}}
|
12
12
|
@other_thing = {:thing => {:id => 1, :name => "Ari"}}
|
13
|
+
@thing2 = {:thing => {:id => 2, :name => "Joe"}}
|
14
|
+
@other_thing2 = {:thing => {:id => 2, :name => "Jeb"}}
|
15
|
+
@thing3 = {:thing => {:id => 3, :name => "Stu"}}
|
13
16
|
@thing_json = @thing.to_json
|
14
17
|
@other_thing_json = @other_thing.to_json
|
15
18
|
end
|
@@ -20,7 +23,6 @@ describe CachedResource do
|
|
20
23
|
end
|
21
24
|
|
22
25
|
describe "when enabled" do
|
23
|
-
|
24
26
|
before(:each) do
|
25
27
|
# it's on by default, but lets call the method
|
26
28
|
# to make sure it works
|
@@ -87,10 +89,143 @@ describe CachedResource do
|
|
87
89
|
Thing.find(1)
|
88
90
|
ActiveResource::HttpMock.requests.length.should == 2
|
89
91
|
end
|
92
|
+
|
93
|
+
describe "when collection synchronize is enabled" do
|
94
|
+
before(:each) do
|
95
|
+
Thing.cached_resource.cache.clear
|
96
|
+
Thing.cached_resource.collection_synchronize = true
|
97
|
+
|
98
|
+
ActiveResource::HttpMock.reset!
|
99
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
100
|
+
mock.get "/things/1.json", {}, @thing_json
|
101
|
+
mock.get "/things.json", {}, [@thing[:thing]].to_json(:root => :thing)
|
102
|
+
end
|
103
|
+
|
104
|
+
# make a request for all things
|
105
|
+
Thing.all
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should write cache entries for its members" do
|
109
|
+
result = Thing.find(1)
|
110
|
+
# only the all request should have been made
|
111
|
+
ActiveResource::HttpMock.requests.length.should == 1
|
112
|
+
# the result should be cached with the appropriate key
|
113
|
+
Thing.cached_resource.cache.read("thing/1").should == result
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should rewrite cache entries for its members when reloaded" do
|
117
|
+
# get the soon to be stale result so that we have a cache entry
|
118
|
+
old_result = Thing.find(1)
|
119
|
+
# change the server
|
120
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
121
|
+
mock.get "/things/1.json", {}, @other_thing_json
|
122
|
+
mock.get "/things.json", {}, [@other_thing[:thing]].to_json(:root => :thing)
|
123
|
+
end
|
124
|
+
# reload the collection
|
125
|
+
Thing.all(:reload => true)
|
126
|
+
# get the updated result, read from the cache
|
127
|
+
result = Thing.find(1)
|
128
|
+
Thing.cached_resource.cache.read("thing/all")[0].should == result
|
129
|
+
Thing.cached_resource.cache.read("thing/all")[0].name.should == result.name
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should update the collection when an individual request is reloaded" do
|
133
|
+
# change the server
|
134
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
135
|
+
mock.get "/things/1.json", {}, @other_thing_json
|
136
|
+
mock.get "/things.json", {}, [@other_thing[:thing]].to_json(:root => :thing)
|
137
|
+
end
|
138
|
+
|
139
|
+
# reload the individual
|
140
|
+
result = Thing.find(1, :reload => true)
|
141
|
+
Thing.cached_resource.cache.read("thing/all")[0].should == result
|
142
|
+
Thing.cached_resource.cache.read("thing/all")[0].name.should == result.name
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should update both the collection and the member cache entries when a subset of the collection is retrieved" do
|
146
|
+
# create cache entries for 1 and all
|
147
|
+
old_individual = Thing.find(1)
|
148
|
+
old_collection = Thing.all
|
149
|
+
|
150
|
+
# change the server
|
151
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
152
|
+
mock.get "/things.json?name=Ari", {}, [@other_thing[:thing]].to_json(:root => :thing)
|
153
|
+
end
|
154
|
+
|
155
|
+
# make a request for a subset of the "mother" collection
|
156
|
+
result = Thing.find(:all, :params => {:name => "Ari"})
|
157
|
+
# the collection should be updated to reflect the server change
|
158
|
+
Thing.cached_resource.cache.read("thing/all")[0].should == result[0]
|
159
|
+
Thing.cached_resource.cache.read("thing/all")[0].name.should == result[0].name
|
160
|
+
# the individual should be updated to reflect the server change
|
161
|
+
Thing.cached_resource.cache.read("thing/1").should == result[0]
|
162
|
+
Thing.cached_resource.cache.read("thing/1").name.should == result[0].name
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should maintain the order of the collection when updating it" do
|
166
|
+
# change the server to return a longer collection
|
167
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
168
|
+
mock.get "/things.json", {}, [@thing[:thing], @thing3[:thing], @thing2[:thing]].to_json(:root => :thing)
|
169
|
+
end
|
170
|
+
|
171
|
+
# create cache entry for the collection (we reload because in before block we make an all request)
|
172
|
+
old_collection = Thing.all(:reload => true)
|
173
|
+
|
174
|
+
# change the server's response for the thing with id 2
|
175
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
176
|
+
mock.get "/things/2.json", {}, @other_thing2.to_json(:root => :thing)
|
177
|
+
end
|
178
|
+
|
179
|
+
# get thing 2, thereby updating the collection
|
180
|
+
result = Thing.find(2, :reload => true)
|
181
|
+
# get the updated collection from the cache
|
182
|
+
updated_collection = Thing.all
|
183
|
+
# name should have changed to "Jeb"
|
184
|
+
updated_collection[2].name.should == result.name
|
185
|
+
# the updated collection should have the elements in the same order
|
186
|
+
old_collection.each_with_index do |thing, i|
|
187
|
+
updated_collection[i].id.should == thing.id
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe "when collection synchronize is disabled" do
|
193
|
+
before(:each) do
|
194
|
+
Thing.cached_resource.cache.clear
|
195
|
+
Thing.cached_resource.collection_synchronize = false
|
196
|
+
|
197
|
+
ActiveResource::HttpMock.reset!
|
198
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
199
|
+
mock.get "/things/1.json", {}, @thing_json
|
200
|
+
mock.get "/things.json", {}, [@thing[:thing]].to_json(:root => :thing)
|
201
|
+
end
|
202
|
+
|
203
|
+
# make a request for all things
|
204
|
+
Thing.all
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should not write cache entries for its members" do
|
208
|
+
result = Thing.find(1)
|
209
|
+
# both the all in the before each and this request should have been made
|
210
|
+
ActiveResource::HttpMock.requests.length.should == 2
|
211
|
+
end
|
212
|
+
|
213
|
+
it "should not update the collection when an individual request is reloaded" do
|
214
|
+
# change the server
|
215
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
216
|
+
mock.get "/things/1.json", {}, @other_thing_json
|
217
|
+
mock.get "/things.json", {}, [@other_thing[:thing]].to_json(:root => :thing)
|
218
|
+
end
|
219
|
+
|
220
|
+
# reload the individual
|
221
|
+
result = Thing.find(1, :reload => true)
|
222
|
+
# the ids are the same, but the names should be different
|
223
|
+
Thing.cached_resource.cache.read("thing/all")[0].name.should_not == result.name
|
224
|
+
end
|
225
|
+
end
|
90
226
|
end
|
91
227
|
|
92
228
|
describe "when disabled" do
|
93
|
-
|
94
229
|
before(:each) do
|
95
230
|
Thing.cached_resource.cache.clear
|
96
231
|
Thing.cached_resource.off!
|
@@ -13,10 +13,22 @@ describe "CachedResource::Configuration" do
|
|
13
13
|
configuration.ttl.should == 604800
|
14
14
|
end
|
15
15
|
|
16
|
+
it "should disable collection synchronization" do
|
17
|
+
configuration.collection_synchronize.should == false
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should default to :all for collection arguments" do
|
21
|
+
configuration.collection_arguments.should == [:all]
|
22
|
+
end
|
23
|
+
|
16
24
|
describe "outside a Rails environment" do
|
17
25
|
it "should be logging to a buffered logger attached to a NilIO" do
|
18
26
|
configuration.logger.class.should == ActiveSupport::BufferedLogger
|
19
|
-
|
27
|
+
# ActiveSupport switched around the log destination variables
|
28
|
+
# Check if either are what we expect to be compatible
|
29
|
+
old_as = configuration.logger.instance_variable_get(:@log).class == CachedResource::NilIO
|
30
|
+
new_as = configuration.logger.instance_variable_get(:@log_dest).class == CachedResource::NilIO
|
31
|
+
(old_as || new_as).should == true
|
20
32
|
end
|
21
33
|
|
22
34
|
it "should cache responses in a memory store" do
|
@@ -51,7 +63,13 @@ describe "CachedResource::Configuration" do
|
|
51
63
|
describe "when initialized through cached resource" do
|
52
64
|
before(:each) do
|
53
65
|
class Foo < ActiveResource::Base
|
54
|
-
cached_resource :ttl => 1,
|
66
|
+
cached_resource :ttl => 1,
|
67
|
+
:cache => "cache",
|
68
|
+
:logger => "logger",
|
69
|
+
:enabled => false,
|
70
|
+
:collection_synchronize => true,
|
71
|
+
:collection_arguments => [:every],
|
72
|
+
:custom => "irrelevant"
|
55
73
|
end
|
56
74
|
end
|
57
75
|
|
@@ -60,14 +78,18 @@ describe "CachedResource::Configuration" do
|
|
60
78
|
end
|
61
79
|
|
62
80
|
it "should relfect the specified options" do
|
63
|
-
Foo.cached_resource
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
81
|
+
cr = Foo.cached_resource
|
82
|
+
cr.ttl.should == 1
|
83
|
+
cr.cache.should == "cache"
|
84
|
+
cr.logger.should == "logger"
|
85
|
+
cr.enabled.should == false
|
86
|
+
cr.collection_synchronize.should == true
|
87
|
+
cr.collection_arguments.should == [:every]
|
88
|
+
cr.custom.should == "irrelevant"
|
68
89
|
end
|
69
90
|
end
|
70
91
|
|
92
|
+
# re-evaluate
|
71
93
|
describe "when multiple are initialized through cached resource" do
|
72
94
|
before(:each) do
|
73
95
|
class Foo < ActiveResource::Base
|
@@ -108,5 +130,71 @@ describe "CachedResource::Configuration" do
|
|
108
130
|
|
109
131
|
end
|
110
132
|
|
133
|
+
describe "when cached resource is inherited" do
|
134
|
+
before(:each) do
|
135
|
+
class Bar < ActiveResource::Base
|
136
|
+
cached_resource :ttl => 1,
|
137
|
+
:cache => "cache",
|
138
|
+
:logger => "logger",
|
139
|
+
:enabled => false,
|
140
|
+
:collection_synchronize => true,
|
141
|
+
:collection_arguments => [:every],
|
142
|
+
:custom => "irrelevant"
|
143
|
+
end
|
144
|
+
|
145
|
+
class Foo < Bar
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
after(:each) do
|
150
|
+
Object.send(:remove_const, :Foo)
|
151
|
+
Object.send(:remove_const, :Bar)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "it should make sure each subclass has the same configuration" do
|
155
|
+
Bar.cached_resource.object_id.should == Foo.cached_resource.object_id
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
describe "when cached resource is inherited and then overriden" do
|
161
|
+
before(:each) do
|
162
|
+
class Bar < ActiveResource::Base
|
163
|
+
cached_resource :ttl => 1,
|
164
|
+
:cache => "cache",
|
165
|
+
:logger => "logger",
|
166
|
+
:enabled => false,
|
167
|
+
:collection_synchronize => true,
|
168
|
+
:collection_arguments => [:every],
|
169
|
+
:custom => "irrelevant"
|
170
|
+
end
|
171
|
+
|
172
|
+
class Foo < Bar
|
173
|
+
# override the superclasses configuration
|
174
|
+
self.cached_resource = CachedResource::Configuration.new(:ttl => 60)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
after(:each) do
|
179
|
+
Object.send(:remove_const, :Foo)
|
180
|
+
Object.send(:remove_const, :Bar)
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should have the specified options" do
|
184
|
+
cr = Foo.cached_resource
|
185
|
+
cr.ttl.should == 60
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should have the default options for anything unspecified" do
|
189
|
+
cr = Foo.cached_resource
|
190
|
+
cr.cache.class.should == ActiveSupport::Cache::MemoryStore
|
191
|
+
cr.logger.class.should == ActiveSupport::BufferedLogger
|
192
|
+
cr.enabled.should == true
|
193
|
+
cr.collection_synchronize.should == false
|
194
|
+
cr.collection_arguments.should == [:all]
|
195
|
+
cr.custom.should == nil
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
111
199
|
|
112
200
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,16 +1,11 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'bundler/setup'
|
3
|
-
|
4
3
|
require 'active_resource'
|
5
4
|
require 'active_support'
|
6
5
|
|
7
6
|
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
8
7
|
require 'cached_resource'
|
9
8
|
|
10
|
-
|
11
9
|
RSpec.configure do |config|
|
12
10
|
# nada
|
13
11
|
end
|
14
|
-
|
15
|
-
# change the logger so that we log to STDOUT
|
16
|
-
# CachedResource.logger = ActiveSupport::BufferedLogger.new(STDOUT)
|
metadata
CHANGED
@@ -1,89 +1,89 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: cached_resource
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 2
|
8
|
-
- 0
|
9
|
-
- 1
|
10
|
-
version: 2.0.1
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.1.1a
|
5
|
+
prerelease: 5
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Andrew Chan
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2012-04-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: rake
|
22
|
-
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
24
17
|
none: false
|
25
|
-
requirements:
|
26
|
-
- -
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
segments:
|
30
|
-
- 0
|
31
|
-
version: "0"
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
32
22
|
type: :runtime
|
33
|
-
version_requirements: *id001
|
34
|
-
- !ruby/object:Gem::Dependency
|
35
|
-
name: activeresource
|
36
23
|
prerelease: false
|
37
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activeresource
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
38
33
|
none: false
|
39
|
-
requirements:
|
40
|
-
- -
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
|
43
|
-
segments:
|
44
|
-
- 0
|
45
|
-
version: "0"
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
46
38
|
type: :runtime
|
47
|
-
version_requirements: *id002
|
48
|
-
- !ruby/object:Gem::Dependency
|
49
|
-
name: activesupport
|
50
39
|
prerelease: false
|
51
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: activesupport
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
52
49
|
none: false
|
53
|
-
requirements:
|
54
|
-
- -
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
|
57
|
-
segments:
|
58
|
-
- 0
|
59
|
-
version: "0"
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
60
54
|
type: :runtime
|
61
|
-
version_requirements: *id003
|
62
|
-
- !ruby/object:Gem::Dependency
|
63
|
-
name: rspec
|
64
55
|
prerelease: false
|
65
|
-
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
66
65
|
none: false
|
67
|
-
requirements:
|
68
|
-
- -
|
69
|
-
- !ruby/object:Gem::Version
|
70
|
-
|
71
|
-
segments:
|
72
|
-
- 0
|
73
|
-
version: "0"
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
74
70
|
type: :development
|
75
|
-
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
76
78
|
description: Enables request-based caching for ActiveResource
|
77
79
|
email: email@suspi.net
|
78
80
|
executables: []
|
79
|
-
|
80
81
|
extensions: []
|
81
|
-
|
82
82
|
extra_rdoc_files: []
|
83
|
-
|
84
|
-
files:
|
83
|
+
files:
|
85
84
|
- .gitignore
|
86
85
|
- .rspec
|
86
|
+
- .travis.yml
|
87
87
|
- Gemfile
|
88
88
|
- MIT-LICENSE
|
89
89
|
- README.md
|
@@ -101,39 +101,31 @@ files:
|
|
101
101
|
- spec/spec_helper.rb
|
102
102
|
homepage: http://github.com/Ahsizara/cached_resource
|
103
103
|
licenses: []
|
104
|
-
|
105
104
|
post_install_message:
|
106
105
|
rdoc_options: []
|
107
|
-
|
108
|
-
require_paths:
|
106
|
+
require_paths:
|
109
107
|
- lib
|
110
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
109
|
none: false
|
112
|
-
requirements:
|
113
|
-
- -
|
114
|
-
- !ruby/object:Gem::Version
|
115
|
-
|
116
|
-
|
117
|
-
- 0
|
118
|
-
version: "0"
|
119
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ! '>='
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
115
|
none: false
|
121
|
-
requirements:
|
122
|
-
- -
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
|
125
|
-
segments:
|
126
|
-
- 0
|
127
|
-
version: "0"
|
116
|
+
requirements:
|
117
|
+
- - ! '>'
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: 1.3.1
|
128
120
|
requirements: []
|
129
|
-
|
130
121
|
rubyforge_project:
|
131
|
-
rubygems_version: 1.8.
|
122
|
+
rubygems_version: 1.8.21
|
132
123
|
signing_key:
|
133
124
|
specification_version: 3
|
134
125
|
summary: Caching for ActiveResource
|
135
|
-
test_files:
|
126
|
+
test_files:
|
136
127
|
- spec/cached_resource/caching_spec.rb
|
137
128
|
- spec/cached_resource/configuration_spec.rb
|
138
129
|
- spec/cached_resource/nilio_spec.rb
|
139
130
|
- spec/spec_helper.rb
|
131
|
+
has_rdoc:
|