active_rest_client 0.9.58
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.simplecov +4 -0
- data/Gemfile +4 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +585 -0
- data/Rakefile +3 -0
- data/active_rest_client.gemspec +34 -0
- data/lib/active_rest_client.rb +23 -0
- data/lib/active_rest_client/base.rb +128 -0
- data/lib/active_rest_client/caching.rb +84 -0
- data/lib/active_rest_client/configuration.rb +69 -0
- data/lib/active_rest_client/connection.rb +76 -0
- data/lib/active_rest_client/connection_manager.rb +21 -0
- data/lib/active_rest_client/headers_list.rb +47 -0
- data/lib/active_rest_client/instrumentation.rb +62 -0
- data/lib/active_rest_client/lazy_association_loader.rb +95 -0
- data/lib/active_rest_client/lazy_loader.rb +23 -0
- data/lib/active_rest_client/logger.rb +67 -0
- data/lib/active_rest_client/mapping.rb +65 -0
- data/lib/active_rest_client/proxy_base.rb +143 -0
- data/lib/active_rest_client/recording.rb +24 -0
- data/lib/active_rest_client/request.rb +412 -0
- data/lib/active_rest_client/request_filtering.rb +52 -0
- data/lib/active_rest_client/result_iterator.rb +66 -0
- data/lib/active_rest_client/validation.rb +60 -0
- data/lib/active_rest_client/version.rb +3 -0
- data/spec/lib/base_spec.rb +245 -0
- data/spec/lib/caching_spec.rb +179 -0
- data/spec/lib/configuration_spec.rb +105 -0
- data/spec/lib/connection_manager_spec.rb +36 -0
- data/spec/lib/connection_spec.rb +73 -0
- data/spec/lib/headers_list_spec.rb +61 -0
- data/spec/lib/instrumentation_spec.rb +59 -0
- data/spec/lib/lazy_association_loader_spec.rb +118 -0
- data/spec/lib/lazy_loader_spec.rb +25 -0
- data/spec/lib/logger_spec.rb +63 -0
- data/spec/lib/mapping_spec.rb +48 -0
- data/spec/lib/proxy_spec.rb +154 -0
- data/spec/lib/recording_spec.rb +34 -0
- data/spec/lib/request_filtering_spec.rb +72 -0
- data/spec/lib/request_spec.rb +471 -0
- data/spec/lib/result_iterator_spec.rb +104 -0
- data/spec/lib/validation_spec.rb +113 -0
- data/spec/spec_helper.rb +22 -0
- metadata +265 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
module ActiveRestClient
|
2
|
+
class ResultIterator
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_accessor :_status
|
6
|
+
attr_reader :items
|
7
|
+
|
8
|
+
def initialize(status = nil)
|
9
|
+
@_status = status
|
10
|
+
@items = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def <<(item)
|
14
|
+
@items << item
|
15
|
+
end
|
16
|
+
|
17
|
+
def size
|
18
|
+
@items.size
|
19
|
+
end
|
20
|
+
|
21
|
+
def index(value)
|
22
|
+
@items.index(value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def empty?
|
26
|
+
size == 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def each
|
30
|
+
@items.each do |el|
|
31
|
+
yield el
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def last
|
36
|
+
@items.last
|
37
|
+
end
|
38
|
+
|
39
|
+
def [](key)
|
40
|
+
@items[key]
|
41
|
+
end
|
42
|
+
|
43
|
+
def shuffle
|
44
|
+
@items = @items.shuffle
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def parallelise(method=nil)
|
49
|
+
collected_responses = []
|
50
|
+
threads = []
|
51
|
+
@items.each do |item|
|
52
|
+
threads << Thread.new do
|
53
|
+
ret = item.send(method) if method
|
54
|
+
ret = yield(item) if block_given?
|
55
|
+
Thread.current[:response] = ret
|
56
|
+
end
|
57
|
+
end
|
58
|
+
threads.each do |t|
|
59
|
+
t.join
|
60
|
+
collected_responses << t[:response]
|
61
|
+
end
|
62
|
+
collected_responses
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module ActiveRestClient
|
2
|
+
module Validation
|
3
|
+
module ClassMethods
|
4
|
+
def validates(field_name, options={}, &block)
|
5
|
+
@_validations ||= []
|
6
|
+
@_validations << {field_name:field_name, options:options, block:block}
|
7
|
+
end
|
8
|
+
|
9
|
+
def _validations
|
10
|
+
@_validations ||= []
|
11
|
+
@_validations
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.included(base)
|
16
|
+
base.extend(ClassMethods)
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid?
|
20
|
+
@errors = Hash.new {|h,k| h[k] = []}
|
21
|
+
self.class._validations.each do |validation|
|
22
|
+
value = self.send(validation[:field_name])
|
23
|
+
validation[:options].each do |type, options|
|
24
|
+
if type == :presence
|
25
|
+
if value.nil?
|
26
|
+
@errors[validation[:field_name]] << "must be present"
|
27
|
+
end
|
28
|
+
elsif type == :length
|
29
|
+
if options[:within]
|
30
|
+
@errors[validation[:field_name]] << "must be within range #{options[:within]}" unless options[:within].include?(value.to_s.length )
|
31
|
+
end
|
32
|
+
if options[:minimum]
|
33
|
+
@errors[validation[:field_name]] << "must be at least #{options[:minimum]} characters long" unless value.to_s.length >= options[:minimum]
|
34
|
+
end
|
35
|
+
if options[:maximum]
|
36
|
+
@errors[validation[:field_name]] << "must be no more than #{options[:minimum]} characters long" unless value.to_s.length <= options[:maximum]
|
37
|
+
end
|
38
|
+
elsif type == :numericality
|
39
|
+
numeric = (true if Float(value) rescue false)
|
40
|
+
@errors[validation[:field_name]] << "must be numeric" unless numeric
|
41
|
+
elsif type == :minimum && !value.nil?
|
42
|
+
@errors[validation[:field_name]] << "must be at least #{options}" unless value.to_f >= options.to_f
|
43
|
+
elsif type == :maximum && !value.nil?
|
44
|
+
@errors[validation[:field_name]] << "must be no more than #{options}" unless value.to_f <= options.to_f
|
45
|
+
end
|
46
|
+
end
|
47
|
+
if validation[:block]
|
48
|
+
validation[:block].call(self, validation[:field_name], value)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
@errors.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
def errors
|
55
|
+
@errors ||= Hash.new {|h,k| h[k] = []}
|
56
|
+
@errors
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,245 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class EmptyExample < ActiveRestClient::Base
|
4
|
+
whiny_missing true
|
5
|
+
end
|
6
|
+
|
7
|
+
class TranslatorExample
|
8
|
+
def self.all(object)
|
9
|
+
ret = {}
|
10
|
+
ret["first_name"] = object["name"]
|
11
|
+
ret
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class AlteringClientExample < ActiveRestClient::Base
|
16
|
+
translator TranslatorExample
|
17
|
+
base_url "http://www.example.com"
|
18
|
+
|
19
|
+
get :all, "/all", fake:"{\"name\":\"Billy\"}"
|
20
|
+
get :list, "/list", fake:"{\"name\":\"Billy\", \"country\":\"United Kingdom\"}"
|
21
|
+
get :iterate, "/iterate", fake:"{\"name\":\"Billy\", \"country\":\"United Kingdom\"}"
|
22
|
+
get :find, "/find/:id"
|
23
|
+
end
|
24
|
+
|
25
|
+
class RecordResponseExample < ActiveRestClient::Base
|
26
|
+
base_url "http://www.example.com"
|
27
|
+
|
28
|
+
record_response do |url, response|
|
29
|
+
raise Exception.new("#{url}|#{response.body}")
|
30
|
+
end
|
31
|
+
|
32
|
+
get :all, "/all"
|
33
|
+
end
|
34
|
+
|
35
|
+
describe ActiveRestClient::Base do
|
36
|
+
it 'should instantiate a new descendant' do
|
37
|
+
expect{EmptyExample.new}.to_not raise_error(Exception)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should not instantiate a new base class" do
|
41
|
+
expect{ActiveRestClient::Base.new}.to raise_error(Exception)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should save attributes passed in constructor" do
|
45
|
+
client = EmptyExample.new(:test => "Something")
|
46
|
+
expect(client._attributes[:test]).to be_a(String)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should allow attribute reading using missing method names" do
|
50
|
+
client = EmptyExample.new(:test => "Something")
|
51
|
+
expect(client.test).to eq("Something")
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should allow attribute reading using [] array notation" do
|
55
|
+
client = EmptyExample.new(:test => "Something")
|
56
|
+
expect(client["test"]).to eq("Something")
|
57
|
+
end
|
58
|
+
|
59
|
+
it "allows iteration over attributes using each" do
|
60
|
+
client = AlteringClientExample.iterate
|
61
|
+
expect(client).to be_respond_to(:each)
|
62
|
+
keys = []
|
63
|
+
values = []
|
64
|
+
client.each do |key, value|
|
65
|
+
keys << key ; values << value
|
66
|
+
end
|
67
|
+
expect(keys).to eq(%i{name country})
|
68
|
+
expect(values).to eq(["Billy", "United Kingdom"])
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should automatically parse ISO 8601 format dates" do
|
72
|
+
t = Time.now
|
73
|
+
client = EmptyExample.new(:test => t.iso8601)
|
74
|
+
expect(client["test"]).to be_an_instance_of(DateTime)
|
75
|
+
expect(client["test"].to_s).to eq(t.to_datetime.to_s)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should store attributes set using missing method names and mark them as dirty" do
|
79
|
+
client = EmptyExample.new()
|
80
|
+
client.test = "Something"
|
81
|
+
expect(client.test.to_s).to eq("Something")
|
82
|
+
expect(client).to be_dirty
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should store attribute set using []= array notation and mark them as dirty" do
|
86
|
+
client = EmptyExample.new()
|
87
|
+
client["test"] = "Something"
|
88
|
+
expect(client["test"].to_s).to eq("Something")
|
89
|
+
expect(client).to be_dirty
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should overwrite attributes already set and mark them as dirty" do
|
93
|
+
client = EmptyExample.new(:hello => "World")
|
94
|
+
client._clean!
|
95
|
+
expect(client).to_not be_dirty
|
96
|
+
|
97
|
+
client.hello = "Everybody"
|
98
|
+
expect(client).to be_dirty
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should respond_to? attributes defined in the response' do
|
102
|
+
client = EmptyExample.new(:hello => "World")
|
103
|
+
client.respond_to?(:hello).should be_true
|
104
|
+
client.respond_to?(:world).should be_false
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should save the base URL for the API server" do
|
108
|
+
class BaseExample < ActiveRestClient::Base
|
109
|
+
base_url "https://www.example.com/api/v1"
|
110
|
+
end
|
111
|
+
expect(BaseExample.base_url).to eq("https://www.example.com/api/v1")
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should allow changing the base_url while running" do
|
115
|
+
class OutsideBaseExample < ActiveRestClient::Base ; end
|
116
|
+
|
117
|
+
ActiveRestClient::Base.base_url = "https://www.example.com/api/v1"
|
118
|
+
expect(OutsideBaseExample.base_url).to eq("https://www.example.com/api/v1")
|
119
|
+
|
120
|
+
ActiveRestClient::Base.base_url = "https://www.example.com/api/v2"
|
121
|
+
expect(OutsideBaseExample.base_url).to eq("https://www.example.com/api/v2")
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should include the Mapping module" do
|
125
|
+
expect(EmptyExample).to respond_to(:_calls)
|
126
|
+
expect(EmptyExample).to_not respond_to(:_non_existant)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should be able to easily clean all attributes" do
|
130
|
+
client = EmptyExample.new(hello:"World", goodbye:"Everyone")
|
131
|
+
expect(client).to be_dirty
|
132
|
+
client._clean!
|
133
|
+
expect(client).to_not be_dirty
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should not overly pollute the instance method namespace to reduce chances of clashing (<10 instance methods)" do
|
137
|
+
instance_methods = EmptyExample.instance_methods - Object.methods
|
138
|
+
instance_methods = instance_methods - instance_methods.grep(/^_/)
|
139
|
+
expect(instance_methods.size).to be < 10
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should raise an exception for missing attributes if whiny_missing is enabled" do
|
143
|
+
expect{EmptyExample.new.first_name}.to raise_error(ActiveRestClient::NoAttributeException)
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should be able to lazy instantiate an object from a prefixed lazy_ method call" do
|
147
|
+
ActiveRestClient::Connection.any_instance.should_receive(:get).with('/find/1', anything).and_return(OpenStruct.new(status:200, headers:{}, body:"{\"first_name\":\"Billy\"}"))
|
148
|
+
example = AlteringClientExample.lazy_find(1)
|
149
|
+
expect(example).to be_an_instance_of(ActiveRestClient::LazyLoader)
|
150
|
+
expect(example.first_name).to eq("Billy")
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should be able to lazy instantiate an object from a prefixed lazy_ method call from an instance" do
|
154
|
+
ActiveRestClient::Connection.any_instance.should_receive(:get).with('/find/1', anything).and_return(OpenStruct.new(status:200, headers:{}, body:"{\"first_name\":\"Billy\"}"))
|
155
|
+
example = AlteringClientExample.new.lazy_find(1)
|
156
|
+
expect(example).to be_an_instance_of(ActiveRestClient::LazyLoader)
|
157
|
+
expect(example.first_name).to eq("Billy")
|
158
|
+
end
|
159
|
+
|
160
|
+
context "accepts a Translator to reformat JSON" do
|
161
|
+
it "should log a deprecation warning when using a translator" do
|
162
|
+
ActiveRestClient::Logger.should_receive(:warn) do |message|
|
163
|
+
expect(message).to start_with("DEPRECATION")
|
164
|
+
end
|
165
|
+
Proc.new do
|
166
|
+
class DummyExample < ActiveRestClient::Base
|
167
|
+
translator TranslatorExample
|
168
|
+
end
|
169
|
+
end.call
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should call Translator#method when calling the mapped method if it responds to it" do
|
173
|
+
TranslatorExample.should_receive(:all).with(an_instance_of(Hash)).and_return({})
|
174
|
+
AlteringClientExample.all
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should not raise errors when calling Translator#method if it does not respond to it" do
|
178
|
+
expect {AlteringClientExample.list}.to_not raise_error
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should translate JSON returned through the Translator" do
|
182
|
+
ret = AlteringClientExample.all
|
183
|
+
expect(ret.first_name).to eq("Billy")
|
184
|
+
expect(ret.name).to be_nil
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should return original JSON for items that aren't handled by the Translator" do
|
188
|
+
ret = AlteringClientExample.list
|
189
|
+
expect(ret.name).to eq("Billy")
|
190
|
+
expect(ret.first_name).to be_nil
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
context "directly call a URL, rather than via a mapped method" do
|
195
|
+
it "should be able to directly call a URL" do
|
196
|
+
ActiveRestClient::Request.any_instance.should_receive(:do_request).with(any_args).and_return(OpenStruct.new(status:200, headers:{}, body:"{\"first_name\":\"Billy\"}"))
|
197
|
+
EmptyExample._request("http://api.example.com/")
|
198
|
+
end
|
199
|
+
|
200
|
+
it "runs filters as usual" do
|
201
|
+
ActiveRestClient::Request.any_instance.should_receive(:do_request).with(any_args).and_return(OpenStruct.new(status:200, headers:{}, body:"{\"first_name\":\"Billy\"}"))
|
202
|
+
EmptyExample.should_receive(:_filter_request).with(any_args)
|
203
|
+
EmptyExample._request("http://api.example.com/")
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should make an HTTP request" do
|
207
|
+
ActiveRestClient::Connection.any_instance.should_receive(:get).with(any_args).and_return(OpenStruct.new(status:200, headers:{}, body:"{\"first_name\":\"Billy\"}"))
|
208
|
+
EmptyExample._request("http://api.example.com/")
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should map the response from the directly called URL in the normal way" do
|
212
|
+
ActiveRestClient::Request.any_instance.should_receive(:do_request).with(any_args).and_return(OpenStruct.new(status:200, headers:{}, body:"{\"first_name\":\"Billy\"}"))
|
213
|
+
example = EmptyExample._request("http://api.example.com/")
|
214
|
+
expect(example.first_name).to eq("Billy")
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should be able to lazy load a direct URL request" do
|
218
|
+
ActiveRestClient::Request.any_instance.should_receive(:do_request).with(any_args).and_return(OpenStruct.new(status:200, headers:{}, body:"{\"first_name\":\"Billy\"}"))
|
219
|
+
example = EmptyExample._lazy_request("http://api.example.com/")
|
220
|
+
expect(example).to be_an_instance_of(ActiveRestClient::LazyLoader)
|
221
|
+
expect(example.first_name).to eq("Billy")
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should be able to specify a method and parameters for the call" do
|
225
|
+
ActiveRestClient::Connection.any_instance.should_receive(:post).with(any_args).and_return(OpenStruct.new(status:200, headers:{}, body:"{\"first_name\":\"Billy\"}"))
|
226
|
+
EmptyExample._request("http://api.example.com/", :post, {id:1234})
|
227
|
+
end
|
228
|
+
|
229
|
+
it "should be able to use mapped methods to create a request to pass in to _lazy_request" do
|
230
|
+
ActiveRestClient::Connection.any_instance.should_receive(:get).with('/find/1', anything).and_return(OpenStruct.new(status:200, headers:{}, body:"{\"first_name\":\"Billy\"}"))
|
231
|
+
request = AlteringClientExample._request_for(:find, :id => 1)
|
232
|
+
example = AlteringClientExample._lazy_request(request)
|
233
|
+
expect(example.first_name).to eq("Billy")
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
context "Recording a response" do
|
238
|
+
it "calls back to the record_response callback with the url and response body" do
|
239
|
+
ActiveRestClient::Connection.any_instance.should_receive(:get).with(any_args).and_return(OpenStruct.new(status:200, headers:{}, body:"Hello world"))
|
240
|
+
expect{RecordResponseExample.all}.to raise_error(Exception, "/all|Hello world")
|
241
|
+
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveRestClient::Caching do
|
4
|
+
before :each do
|
5
|
+
ActiveRestClient::Base._reset_caching!
|
6
|
+
end
|
7
|
+
|
8
|
+
context "Configuration of caching" do
|
9
|
+
it "should not have caching enabled by default" do
|
10
|
+
class CachingExample1
|
11
|
+
include ActiveRestClient::Caching
|
12
|
+
end
|
13
|
+
expect(CachingExample1.perform_caching).to be_false
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should be able to have caching enabled without affecting ActiveRestClient::Base" do
|
17
|
+
class CachingExample2
|
18
|
+
include ActiveRestClient::Caching
|
19
|
+
end
|
20
|
+
CachingExample2.perform_caching true
|
21
|
+
expect(CachingExample2.perform_caching).to be_true
|
22
|
+
expect(ActiveRestClient::Base.perform_caching).to be_false
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should be possible to enable caching for all objects" do
|
26
|
+
class CachingExample3 < ActiveRestClient::Base ; end
|
27
|
+
ActiveRestClient::Base._reset_caching!
|
28
|
+
|
29
|
+
expect(ActiveRestClient::Base.perform_caching).to be_false
|
30
|
+
|
31
|
+
ActiveRestClient::Base.perform_caching = true
|
32
|
+
expect(ActiveRestClient::Base.perform_caching).to be_true
|
33
|
+
expect(EmptyExample.perform_caching).to be_true
|
34
|
+
|
35
|
+
ActiveRestClient::Base._reset_caching!
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should use Rails.cache if available" do
|
39
|
+
begin
|
40
|
+
class Rails
|
41
|
+
def self.cache
|
42
|
+
true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
expect(ActiveRestClient::Base.cache_store).to eq(true)
|
46
|
+
ensure
|
47
|
+
Object.send(:remove_const, :Rails) if defined?(Rails)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should use a custom cache store if a valid one is manually set" do
|
53
|
+
class CachingExampleCacheStore1
|
54
|
+
def read(key) ; end
|
55
|
+
def write(key, value, options={}) ; end
|
56
|
+
def fetch(key, &block) ; end
|
57
|
+
end
|
58
|
+
cache_store = CachingExampleCacheStore1.new
|
59
|
+
ActiveRestClient::Base.cache_store = cache_store
|
60
|
+
expect(ActiveRestClient::Base.cache_store).to eq(cache_store)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should error if you try to use a custom cache store that doesn't match the required interface" do
|
64
|
+
class CachingExampleCacheStore2
|
65
|
+
def write(key, value, options={}) ; end
|
66
|
+
def fetch(key, &block) ; end
|
67
|
+
end
|
68
|
+
class CachingExampleCacheStore3
|
69
|
+
def read(key) ; end
|
70
|
+
def fetch(key, &block) ; end
|
71
|
+
end
|
72
|
+
class CachingExampleCacheStore4
|
73
|
+
def read(key) ; end
|
74
|
+
def write(key, value, options={}) ; end
|
75
|
+
end
|
76
|
+
|
77
|
+
expect{ ActiveRestClient::Base.cache_store = CachingExampleCacheStore2.new }.to raise_error
|
78
|
+
expect{ ActiveRestClient::Base.cache_store = CachingExampleCacheStore3.new }.to raise_error
|
79
|
+
expect{ ActiveRestClient::Base.cache_store = CachingExampleCacheStore4.new }.to raise_error
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "Reading/writing to the cache" do
|
84
|
+
before :each do
|
85
|
+
Object.send(:remove_const, :CachingExampleCacheStore5) if defined?(CachingExampleCacheStore5)
|
86
|
+
class CachingExampleCacheStore5
|
87
|
+
def read(key) ; end
|
88
|
+
def write(key, value, options={}) ; end
|
89
|
+
def fetch(key, &block) ; end
|
90
|
+
end
|
91
|
+
|
92
|
+
class Person < ActiveRestClient::Base
|
93
|
+
perform_caching true
|
94
|
+
base_url "http://www.example.com"
|
95
|
+
get :all, "/"
|
96
|
+
end
|
97
|
+
|
98
|
+
Person.cache_store = CachingExampleCacheStore5.new
|
99
|
+
|
100
|
+
@etag = "6527914a91e0c5769f6de281f25bd891"
|
101
|
+
@cached_object = Person.new(first_name:"Johnny")
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should read from the cache store, to check for an etag" do
|
105
|
+
cached_response = ActiveRestClient::CachedResponse.new(
|
106
|
+
status:200,
|
107
|
+
result:@cached_object,
|
108
|
+
etag:@etag)
|
109
|
+
CachingExampleCacheStore5.any_instance.should_receive(:read).once.with("Person:/").and_return(cached_response)
|
110
|
+
ActiveRestClient::Connection.any_instance.should_receive(:get).with("/", hash_including("If-None-Match" => @etag)).and_return(OpenStruct.new(status:304, headers:{}))
|
111
|
+
ret = Person.all
|
112
|
+
expect(ret.first_name).to eq("Johnny")
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should read from the cache store, and not call the server if there's a hard expiry" do
|
116
|
+
cached_response = ActiveRestClient::CachedResponse.new(
|
117
|
+
status:200,
|
118
|
+
result:@cached_object,
|
119
|
+
expires:Time.now + 30)
|
120
|
+
CachingExampleCacheStore5.any_instance.should_receive(:read).once.with("Person:/").and_return(cached_response)
|
121
|
+
ActiveRestClient::Connection.any_instance.should_not_receive(:get)
|
122
|
+
ret = Person.all
|
123
|
+
expect(ret.first_name).to eq("Johnny")
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should read from the cache store and restore to the same object" do
|
127
|
+
cached_response = ActiveRestClient::CachedResponse.new(
|
128
|
+
status:200,
|
129
|
+
result:@cached_object,
|
130
|
+
expires:Time.now + 30)
|
131
|
+
CachingExampleCacheStore5.any_instance.should_receive(:read).once.with("Person:/").and_return(cached_response)
|
132
|
+
ActiveRestClient::Connection.any_instance.should_not_receive(:get)
|
133
|
+
p = Person.new(first_name:"Billy")
|
134
|
+
ret = p.all({})
|
135
|
+
expect(ret.first_name).to eq("Johnny")
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should restore a result iterator from the cache store, if there's a hard expiry" do
|
139
|
+
class CachingExample3 < ActiveRestClient::Base ; end
|
140
|
+
object = ActiveRestClient::ResultIterator.new(200)
|
141
|
+
object << CachingExample3.new(first_name:"Johnny")
|
142
|
+
object << CachingExample3.new(first_name:"Billy")
|
143
|
+
etag = "6527914a91e0c5769f6de281f25bd891"
|
144
|
+
cached_response = ActiveRestClient::CachedResponse.new(
|
145
|
+
status:200,
|
146
|
+
result:object,
|
147
|
+
etag:@etag,
|
148
|
+
expires:Time.now + 30)
|
149
|
+
CachingExampleCacheStore5.any_instance.should_receive(:read).once.with("Person:/").and_return(cached_response)
|
150
|
+
ActiveRestClient::Connection.any_instance.should_not_receive(:get)
|
151
|
+
ret = Person.all
|
152
|
+
expect(ret.first.first_name).to eq("Johnny")
|
153
|
+
expect(ret._status).to eq(200)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should not write the response to the cache unless if has caching headers" do
|
157
|
+
CachingExampleCacheStore5.any_instance.should_receive(:read).once.with("Person:/").and_return(nil)
|
158
|
+
CachingExampleCacheStore5.any_instance.should_not_receive(:write)
|
159
|
+
ActiveRestClient::Connection.any_instance.should_receive(:get).with("/", an_instance_of(Hash)).and_return(OpenStruct.new(status:200, body:"{\"result\":true}", headers:{}))
|
160
|
+
ret = Person.all
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should write the response to the cache if there's an etag" do
|
164
|
+
CachingExampleCacheStore5.any_instance.should_receive(:read).once.with("Person:/").and_return(nil)
|
165
|
+
CachingExampleCacheStore5.any_instance.should_receive(:write).once.with("Person:/", an_instance_of(ActiveRestClient::CachedResponse), {})
|
166
|
+
ActiveRestClient::Connection.any_instance.should_receive(:get).with("/", an_instance_of(Hash)).and_return(OpenStruct.new(status:200, body:"{\"result\":true}", headers:{etag:"1234567890"}))
|
167
|
+
ret = Person.all
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should write the response to the cache if there's a hard expiry" do
|
171
|
+
CachingExampleCacheStore5.any_instance.should_receive(:read).once.with("Person:/").and_return(nil)
|
172
|
+
CachingExampleCacheStore5.any_instance.should_receive(:write).once.with("Person:/", an_instance_of(ActiveRestClient::CachedResponse), an_instance_of(Hash))
|
173
|
+
ActiveRestClient::Connection.any_instance.should_receive(:get).with("/", an_instance_of(Hash)).and_return(OpenStruct.new(status:200, body:"{\"result\":true}", headers:{expires:(Time.now + 30).rfc822}))
|
174
|
+
ret = Person.all
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|