active_rest_client 0.9.58
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.
- 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
|