px-service-client 1.0.6 → 1.1.0
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 +4 -4
- data/lib/px/service/client/base.rb +0 -1
- data/lib/px/service/client/caching/cache_entry.rb +5 -4
- data/lib/px/service/client/caching.rb +97 -44
- data/lib/px/service/client/multiplexer.rb +3 -1
- data/lib/px/service/client/version.rb +1 -1
- data/spec/px/service/client/base_spec.rb +242 -2
- data/spec/px/service/client/caching/caching_spec.rb +48 -44
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eed1491471134440a4e1ca1644420395f15079e4
|
4
|
+
data.tar.gz: 5f50c16950abc5f20cd7eb7d82deb75f5731782e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd65c7907b1870c02add4c35a4360df9487b8974b92b9bdfc2a0af941b6f1a2e44870b8b8c409d7235c72a103bc0d64d8828038bcc54457b2949c014b02f2051
|
7
|
+
data.tar.gz: cf39f661fb5c27d1643b036afd7d3b28028e9dc098186775dbb461a852bd9ae3b201b629685eb364e2e6ae5b768d29c3b1edb0a35f8952123e2fb784bbfd8c02
|
@@ -23,8 +23,9 @@ module Px::Service::Client::Caching
|
|
23
23
|
ActiveSupport::Notifications.instrument("store.caching", { url: url, policy_group: policy_group, expires_in: expires_in} ) do
|
24
24
|
real_expiry = real_cache_expiry(expires_in, refresh_window: refresh_window)
|
25
25
|
cache_client.multi do
|
26
|
-
|
27
|
-
cache_client.set(cache_key(:
|
26
|
+
data_json = data.is_a?(Hash) ? data.to_json : data
|
27
|
+
cache_client.set(cache_key(:data), data_json, real_expiry)
|
28
|
+
cache_client.set(cache_key(:meta), metadata, real_expiry)
|
28
29
|
end
|
29
30
|
end
|
30
31
|
end
|
@@ -59,7 +60,7 @@ module Px::Service::Client::Caching
|
|
59
60
|
real_expiry = real_cache_expiry(expires_in, refresh_window: refresh_window)
|
60
61
|
|
61
62
|
cache_client.touch(cache_key(:data), real_expiry)
|
62
|
-
cache_client.set(cache_key(:meta), metadata
|
63
|
+
cache_client.set(cache_key(:meta), metadata, real_expiry)
|
63
64
|
end
|
64
65
|
end
|
65
66
|
|
@@ -70,7 +71,7 @@ module Px::Service::Client::Caching
|
|
70
71
|
"url" => url,
|
71
72
|
"pg" => policy_group,
|
72
73
|
"expires_at" => expires_at,
|
73
|
-
}
|
74
|
+
}.to_json
|
74
75
|
end
|
75
76
|
|
76
77
|
def cache_key(type)
|
@@ -20,14 +20,41 @@ module Px::Service::Client
|
|
20
20
|
cattr_accessor :cache_client, :cache_logger
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
module ClassMethods
|
24
|
+
DefaultConfig = Struct.new(:cache_strategy, :cache_expiry, :max_page, :cache_options, :cache_client) do
|
25
|
+
def initialize
|
26
|
+
self.cache_strategy = :none
|
27
|
+
self.cache_expiry = 30.seconds
|
28
|
+
self.max_page = nil
|
29
|
+
self.cache_options = {}
|
30
|
+
self.cache_options[:policy_group] = 'general'
|
31
|
+
self.cache_client = nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Set the caching behaviour
|
37
|
+
def caching(&block)
|
38
|
+
@cache_config ||= DefaultConfig.new
|
39
|
+
yield(@cache_config) if block_given?
|
40
|
+
@cache_config
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def config
|
45
|
+
@cache_config || self.class.caching
|
46
|
+
end
|
47
|
+
|
48
|
+
def cache_request(url, strategy: nil, **options, &block)
|
49
|
+
strategy ||= config.cache_strategy
|
50
|
+
|
24
51
|
case strategy
|
25
52
|
when :first_resort
|
26
|
-
cache_first_resort(url, expires_in:
|
53
|
+
cache_first_resort(url, policy_group: config.cache_options[:policy_group], expires_in: config.cache_expiry, **options, &block)
|
27
54
|
when :last_resort
|
28
|
-
cache_last_resort(url, expires_in:
|
55
|
+
cache_last_resort(url, policy_group: config.cache_options[:policy_group], expires_in: config.cache_expiry, **options, &block)
|
29
56
|
else
|
30
|
-
no_cache(
|
57
|
+
no_cache(&block)
|
31
58
|
end
|
32
59
|
end
|
33
60
|
|
@@ -40,27 +67,34 @@ module Px::Service::Client
|
|
40
67
|
def cache_last_resort(url, policy_group: 'general', expires_in: nil, refresh_probability: 1, &block)
|
41
68
|
# Note we use a smaller refresh window here (technically, could even use 0)
|
42
69
|
# since we don't really need the "expired but not really expired" behaviour when caching as a last resort.
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
70
|
+
retry_response = block.call
|
71
|
+
|
72
|
+
Future.new do
|
73
|
+
begin
|
74
|
+
if retry_response.is_a?(Future)
|
75
|
+
resp = retry_response.value!.options
|
76
|
+
else
|
77
|
+
resp = retry_response
|
78
|
+
end
|
79
|
+
|
80
|
+
entry = CacheEntry.new(config.cache_client, url, policy_group, resp)
|
81
|
+
|
82
|
+
# Only store a new result if we roll a 0
|
83
|
+
r = rand(refresh_probability)
|
84
|
+
entry.store(expires_in, refresh_window: 1.minute) if r == 0
|
85
|
+
resp
|
86
|
+
rescue Px::Service::ServiceError => ex
|
87
|
+
cache_logger.error "Service responded with exception: #{ex.class.name}: #{ex.message}\n#{ex.backtrace.join('\n')}" if cache_logger
|
88
|
+
|
89
|
+
entry = CacheEntry.fetch(config.cache_client, url, policy_group)
|
90
|
+
if entry.nil?
|
91
|
+
# Re-raise the error, no cached response
|
92
|
+
raise ex
|
93
|
+
end
|
94
|
+
|
95
|
+
entry.touch(expires_in, refresh_window: 1.minute)
|
96
|
+
entry.data
|
60
97
|
end
|
61
|
-
|
62
|
-
entry.touch(expires_in, refresh_window: 1.minute)
|
63
|
-
entry.data
|
64
98
|
end
|
65
99
|
end
|
66
100
|
|
@@ -70,7 +104,7 @@ module Px::Service::Client
|
|
70
104
|
# has expired (but is still present) and the request fails, the cached value is still returned, as if this was
|
71
105
|
# cache_last_resort.
|
72
106
|
def cache_first_resort(url, policy_group: 'general', expires_in: nil, &block)
|
73
|
-
entry = CacheEntry.fetch(cache_client, url, policy_group)
|
107
|
+
entry = CacheEntry.fetch(config.cache_client, url, policy_group)
|
74
108
|
|
75
109
|
if entry
|
76
110
|
if entry.expired?
|
@@ -80,33 +114,52 @@ module Px::Service::Client
|
|
80
114
|
# don't also try to update the cache.
|
81
115
|
entry.touch(expires_in)
|
82
116
|
else
|
83
|
-
return entry.data
|
117
|
+
return Future.new { entry.data }
|
84
118
|
end
|
85
119
|
end
|
86
120
|
|
87
|
-
|
88
|
-
|
121
|
+
retry_response = block.call
|
122
|
+
|
123
|
+
Future.new do
|
124
|
+
begin
|
125
|
+
if retry_response.is_a?(Future)
|
126
|
+
resp = retry_response.value!.options
|
127
|
+
else
|
128
|
+
resp = retry_response
|
129
|
+
end
|
130
|
+
|
131
|
+
entry = CacheEntry.new(config.cache_client, url, policy_group, resp)
|
132
|
+
entry.store(expires_in)
|
133
|
+
resp
|
134
|
+
rescue Px::Service::ServiceError => ex
|
135
|
+
cache_logger.error "Service responded with exception: #{ex.class.name}: #{ex.message}\n#{ex.backtrace.join('\n')}" if cache_logger
|
136
|
+
|
137
|
+
entry = CacheEntry.fetch(config.cache_client, url, policy_group)
|
138
|
+
if entry.nil?
|
139
|
+
# Re-raise the error, no cached response
|
140
|
+
raise ex
|
141
|
+
end
|
142
|
+
|
143
|
+
# Set the entry to be expired again (but reset the refresh window). This allows the next call to try again
|
144
|
+
# (assuming the circuit breaker is reset) but keeps the value in the cache in the meantime
|
145
|
+
entry.touch(0.seconds)
|
146
|
+
entry.data
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
89
150
|
|
90
|
-
|
91
|
-
|
92
|
-
response
|
93
|
-
rescue Px::Service::ServiceError => ex
|
94
|
-
cache_logger.error "Service responded with exception: #{ex.class.name}: #{ex.message}\n#{ex.backtrace.join('\n')}" if cache_logger
|
151
|
+
def no_cache(&block)
|
152
|
+
retry_response = block.call
|
95
153
|
|
96
|
-
|
97
|
-
|
98
|
-
|
154
|
+
Future.new do
|
155
|
+
if retry_response.is_a?(Future)
|
156
|
+
resp = retry_response.value!.options
|
157
|
+
else
|
158
|
+
resp = retry_response
|
99
159
|
end
|
100
160
|
|
101
|
-
|
102
|
-
# (assuming the circuit breaker is reset) but keeps the value in the cache in the meantime
|
103
|
-
entry.touch(0.seconds)
|
104
|
-
entry.data
|
161
|
+
resp
|
105
162
|
end
|
106
163
|
end
|
107
|
-
|
108
|
-
def no_cache(url, &block)
|
109
|
-
block.call(url)
|
110
|
-
end
|
111
164
|
end
|
112
165
|
end
|
@@ -17,7 +17,9 @@ module Px::Service::Client
|
|
17
17
|
def do(request_or_future, retries: RetriableResponseFuture::DEFAULT_RETRIES)
|
18
18
|
response = request_or_future
|
19
19
|
if request_or_future.is_a?(Typhoeus::Request)
|
20
|
-
response = RetriableResponseFuture(request_or_future, retries: retries)
|
20
|
+
response = RetriableResponseFuture.new(request_or_future, retries: retries)
|
21
|
+
elsif !request_or_future.is_a?(RetriableResponseFuture)
|
22
|
+
return request_or_future
|
21
23
|
end
|
22
24
|
|
23
25
|
# Will automatically queue the request on the hydra
|
@@ -1,8 +1,19 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Px::Service::Client::Base do
|
4
|
-
|
5
|
-
let(:
|
4
|
+
let(:dalli_host) { ENV['PX_MEMCACHED_HOST'] }
|
5
|
+
let(:dalli_options) { { :namespace => "service-client-test", expires_in: 3600, compress: false, failover: false } }
|
6
|
+
let(:dalli) { Dalli::Client.new(dalli_host, dalli_options) }
|
7
|
+
|
8
|
+
subject {
|
9
|
+
Px::Service::Client::Base.include(Px::Service::Client::Caching).tap do |c|
|
10
|
+
c.caching do |config|
|
11
|
+
config.cache_client = dalli
|
12
|
+
end
|
13
|
+
end.new
|
14
|
+
}
|
15
|
+
|
16
|
+
let(:successful_response) do
|
6
17
|
Typhoeus::Response.new(
|
7
18
|
code: 200,
|
8
19
|
body: { status: 200, message: "Success"}.to_json,
|
@@ -45,5 +56,234 @@ describe Px::Service::Client::Base do
|
|
45
56
|
expect(resp.request.url).to include("one=a&two=b")
|
46
57
|
end
|
47
58
|
end
|
59
|
+
|
60
|
+
context "when the caching strategy is set" do
|
61
|
+
let(:multi) { Px::Service::Client::Multiplexer.new }
|
62
|
+
let(:request) { Typhoeus::Request.new(url, method: :get) }
|
63
|
+
let(:future) { Px::Service::Client::RetriableResponseFuture.new(request) }
|
64
|
+
|
65
|
+
before :each do
|
66
|
+
dalli.flush_all
|
67
|
+
Typhoeus.stub(url).and_return(response)
|
68
|
+
end
|
69
|
+
|
70
|
+
shared_examples_for 'a request that returns a cached response' do
|
71
|
+
let(:cache_entry) { Px::Service::Client::Caching::CacheEntry.new(dalli, url, 'general', future, Time.now + 1.year) }
|
72
|
+
|
73
|
+
before :each do
|
74
|
+
Typhoeus::Expectation.clear
|
75
|
+
Typhoeus.stub(url).and_return(successful_response)
|
76
|
+
|
77
|
+
req = subject.send(:make_request, 'get', url)
|
78
|
+
subject.cache_request(req.request.url, strategy: strategy) do
|
79
|
+
resp = nil
|
80
|
+
multi.context do
|
81
|
+
resp = multi.do(req)
|
82
|
+
end.run
|
83
|
+
|
84
|
+
resp
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'does not return a new response' do
|
89
|
+
req = subject.send(:make_request, 'get', url)
|
90
|
+
|
91
|
+
expect(Px::Service::Client::RetriableResponseFuture).to_not receive(:new)
|
92
|
+
subject.cache_request(req.request.url, strategy: strategy) do
|
93
|
+
resp = nil
|
94
|
+
multi.context do
|
95
|
+
resp = multi.do(req)
|
96
|
+
end.run
|
97
|
+
|
98
|
+
resp
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'returns the cached response' do
|
103
|
+
Typhoeus::Expectation.clear
|
104
|
+
Typhoeus.stub(url).and_return(response)
|
105
|
+
req = subject.send(:make_request, 'get', url)
|
106
|
+
subject.cache_request(req.request.url, strategy: strategy) do
|
107
|
+
resp = nil
|
108
|
+
|
109
|
+
multi.context do
|
110
|
+
resp = multi.do(req)
|
111
|
+
expect(resp).to eq(cache_entry.data)
|
112
|
+
end.run
|
113
|
+
|
114
|
+
resp
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'to first_resort' do
|
120
|
+
let(:strategy) { :first_resort }
|
121
|
+
let(:response) { successful_response }
|
122
|
+
|
123
|
+
it_behaves_like 'a request that returns a cached response'
|
124
|
+
|
125
|
+
context 'when the request fails' do
|
126
|
+
let(:response) do
|
127
|
+
Typhoeus::Response.new(
|
128
|
+
code: 500,
|
129
|
+
body: { status: 500, error: "Failed"}.to_json,
|
130
|
+
headers: { "Content-Type" => "application/json"} )
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'when no response is cached' do
|
134
|
+
it 'makes the request' do
|
135
|
+
called = false
|
136
|
+
req = subject.send(:make_request, 'get', url)
|
137
|
+
|
138
|
+
subject.cache_request(req.request.url, strategy: strategy) do
|
139
|
+
resp = nil
|
140
|
+
multi.context do
|
141
|
+
resp = multi.do(req)
|
142
|
+
called = true
|
143
|
+
end.run
|
144
|
+
|
145
|
+
resp
|
146
|
+
end
|
147
|
+
|
148
|
+
expect(called).to be_truthy
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'returns an error' do
|
152
|
+
req = subject.send(:make_request, 'get', url)
|
153
|
+
expect {
|
154
|
+
subject.cache_request(req.request.url, strategy: strategy) do
|
155
|
+
resp = nil
|
156
|
+
multi.context do
|
157
|
+
resp = multi.do(req)
|
158
|
+
end.run
|
159
|
+
|
160
|
+
resp
|
161
|
+
end.value!
|
162
|
+
}.to raise_error(Px::Service::ServiceError, 'Failed')
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
context 'when a response has been cached' do
|
167
|
+
it_behaves_like 'a request that returns a cached response'
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context 'to last_resort' do
|
173
|
+
let(:strategy) { :last_resort }
|
174
|
+
let(:response) { successful_response }
|
175
|
+
|
176
|
+
it 'makes the request' do
|
177
|
+
called = false
|
178
|
+
req = subject.send(:make_request, 'get', url)
|
179
|
+
|
180
|
+
subject.cache_request(req.request.url, strategy: strategy) do
|
181
|
+
resp = nil
|
182
|
+
multi.context do
|
183
|
+
resp = multi.do(req)
|
184
|
+
called = true
|
185
|
+
end.run
|
186
|
+
|
187
|
+
resp
|
188
|
+
end
|
189
|
+
|
190
|
+
expect(called).to be_truthy
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'when the request fails' do
|
194
|
+
let(:response) do
|
195
|
+
Typhoeus::Response.new(
|
196
|
+
code: 500,
|
197
|
+
body: { status: 500, error: "Failed"}.to_json,
|
198
|
+
headers: { "Content-Type" => "application/json"} )
|
199
|
+
end
|
200
|
+
|
201
|
+
context 'when no response is cached' do
|
202
|
+
it 'makes the request' do
|
203
|
+
called = false
|
204
|
+
req = subject.send(:make_request, 'get', url)
|
205
|
+
|
206
|
+
subject.cache_request(req.request.url, strategy: strategy) do
|
207
|
+
resp = nil
|
208
|
+
multi.context do
|
209
|
+
resp = multi.do(req)
|
210
|
+
called = true
|
211
|
+
end.run
|
212
|
+
|
213
|
+
resp
|
214
|
+
end
|
215
|
+
|
216
|
+
expect(called).to be_truthy
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'raises an error' do
|
220
|
+
req = subject.send(:make_request, 'get', url)
|
221
|
+
|
222
|
+
expect {
|
223
|
+
subject.cache_request(req.request.url, strategy: strategy) do
|
224
|
+
resp = nil
|
225
|
+
multi.context do
|
226
|
+
resp = multi.do(req)
|
227
|
+
end.run
|
228
|
+
|
229
|
+
resp
|
230
|
+
end.value!
|
231
|
+
}.to raise_error(Px::Service::ServiceError, 'Failed')
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
context 'when a response has been cached' do
|
236
|
+
before :each do
|
237
|
+
Typhoeus::Expectation.clear
|
238
|
+
Typhoeus.stub(url).and_return(successful_response)
|
239
|
+
|
240
|
+
req = subject.send(:make_request, 'get', url)
|
241
|
+
|
242
|
+
subject.cache_request(req.request.url, strategy: strategy) do
|
243
|
+
resp = nil
|
244
|
+
multi.context do
|
245
|
+
resp = multi.do(req)
|
246
|
+
end.run
|
247
|
+
|
248
|
+
resp
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'makes the request' do
|
253
|
+
called = false
|
254
|
+
req = subject.send(:make_request, 'get', url)
|
255
|
+
subject.cache_request(req.request.url, strategy: strategy) do
|
256
|
+
resp = nil
|
257
|
+
multi.context do
|
258
|
+
resp = multi.do(req)
|
259
|
+
called = true
|
260
|
+
end.run
|
261
|
+
|
262
|
+
resp
|
263
|
+
end
|
264
|
+
|
265
|
+
expect(called).to be_truthy
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'returns the cached response' do
|
269
|
+
Typhoeus::Expectation.clear
|
270
|
+
Typhoeus.stub(url).and_return(response)
|
271
|
+
req = subject.send(:make_request, 'get', url)
|
272
|
+
|
273
|
+
expect(subject.cache_request(req.request.url, strategy: strategy) do
|
274
|
+
resp = nil
|
275
|
+
multi.context do
|
276
|
+
resp = multi.do(req)
|
277
|
+
end.run
|
278
|
+
|
279
|
+
resp
|
280
|
+
end.value!['code']).to eq(200)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
48
287
|
end
|
49
288
|
end
|
289
|
+
|
@@ -2,32 +2,34 @@ require 'spec_helper'
|
|
2
2
|
require 'dalli'
|
3
3
|
|
4
4
|
describe Px::Service::Client::Caching do
|
5
|
+
let(:dalli_host) { ENV['PX_MEMCACHED_HOST'] }
|
6
|
+
let(:dalli_options) { { :namespace => "service-client-test", expires_in: 3600, compress: false, failover: false } }
|
7
|
+
let(:dalli) { Dalli::Client.new(dalli_host, dalli_options) }
|
8
|
+
|
5
9
|
subject {
|
6
10
|
Class.new.include(Px::Service::Client::Caching).tap do |c|
|
7
11
|
# Anonymous classes don't have a name. Stub out :name so that things work
|
8
12
|
allow(c).to receive(:name).and_return("Caching")
|
13
|
+
|
14
|
+
c.caching do |config|
|
15
|
+
config.cache_client = dalli
|
16
|
+
end
|
9
17
|
end.new
|
10
18
|
}
|
11
19
|
|
12
|
-
let(:
|
13
|
-
let(:
|
14
|
-
let(:
|
20
|
+
let (:url) { "http://search/foo?bar=baz" }
|
21
|
+
let(:response) { { "response" => ["foo", "bar"], "status" => 200 } }
|
22
|
+
let(:entry) { Px::Service::Client::Caching::CacheEntry.new(dalli, url, 'general', response) }
|
23
|
+
let(:strategy) { :none }
|
15
24
|
|
16
25
|
before :each do
|
17
26
|
dalli.flush_all
|
18
|
-
subject.cache_client = dalli
|
19
27
|
end
|
20
28
|
|
21
|
-
let (:url) { "http://search/foo?bar=baz"}
|
22
|
-
let (:response) {
|
23
|
-
{ "response" => ["foo", "bar"], "status" => 200 }
|
24
|
-
}
|
25
|
-
|
26
29
|
shared_examples_for "a successful request" do
|
27
30
|
it "should call the block" do
|
28
31
|
called = false
|
29
|
-
subject.cache_request(url, strategy: strategy) do
|
30
|
-
expect(u).to eq(url)
|
32
|
+
subject.cache_request(url, strategy: strategy) do
|
31
33
|
called = true
|
32
34
|
end
|
33
35
|
|
@@ -35,35 +37,35 @@ describe Px::Service::Client::Caching do
|
|
35
37
|
end
|
36
38
|
|
37
39
|
it "should return the block's return value" do
|
38
|
-
expect(subject.cache_request(url, strategy: strategy)
|
40
|
+
expect(subject.cache_request(url, strategy: strategy) do
|
41
|
+
response
|
42
|
+
end.value!).to eq(response)
|
39
43
|
end
|
40
44
|
end
|
41
45
|
|
42
46
|
shared_examples_for "a failed uncacheable request" do
|
43
47
|
it "should raise the exception raised by the block" do
|
44
|
-
expect{
|
48
|
+
expect {
|
45
49
|
subject.cache_request(url, strategy: strategy) do
|
46
50
|
# Px::Service::ServiceRequestError is not cachable
|
47
51
|
# and does not trigger a fallback to a cached response
|
48
52
|
raise Px::Service::ServiceRequestError.new("Error", 404)
|
49
|
-
end
|
53
|
+
end.value!
|
50
54
|
}.to raise_error(Px::Service::ServiceRequestError)
|
51
55
|
end
|
52
56
|
end
|
53
57
|
|
54
58
|
shared_examples_for "a request with no cached response" do
|
55
59
|
it "raises the exception" do
|
56
|
-
expect {
|
60
|
+
expect {
|
57
61
|
subject.cache_request(url, strategy: strategy) do
|
58
62
|
raise Px::Service::ServiceError.new("Error", 500)
|
59
|
-
end
|
63
|
+
end.value!
|
60
64
|
}.to raise_error(Px::Service::ServiceError)
|
61
65
|
end
|
62
66
|
end
|
63
67
|
|
64
68
|
context "when not caching" do
|
65
|
-
let(:strategy) { :none }
|
66
|
-
|
67
69
|
it_behaves_like "a successful request"
|
68
70
|
it_behaves_like "a failed uncacheable request"
|
69
71
|
end
|
@@ -76,22 +78,20 @@ describe Px::Service::Client::Caching do
|
|
76
78
|
|
77
79
|
context "when there is a cached response" do
|
78
80
|
before :each do
|
79
|
-
|
80
|
-
response
|
81
|
-
end
|
81
|
+
Px::Service::Client::Caching::CacheEntry.stub(:fetch).and_return(entry)
|
82
82
|
end
|
83
83
|
|
84
84
|
it "returns the cached response on failure" do
|
85
85
|
expect(subject.cache_request(url, strategy: strategy) do
|
86
|
-
raise Px::Service::ServiceError.new("Error", 500)
|
87
|
-
end).to eq(response)
|
86
|
+
Px::Service::Client::Future.new { raise Px::Service::ServiceError.new("Error", 500) }
|
87
|
+
end.value!).to eq(response)
|
88
88
|
end
|
89
89
|
|
90
90
|
it "does not returns the cached response on request error" do
|
91
91
|
expect {
|
92
92
|
subject.cache_request(url, strategy: strategy) do
|
93
|
-
raise Px::Service::ServiceRequestError.new("Error", 404)
|
94
|
-
end
|
93
|
+
Px::Service::Client::Future.new { raise Px::Service::ServiceRequestError.new("Error", 404) }
|
94
|
+
end.value!
|
95
95
|
}.to raise_error(Px::Service::ServiceRequestError)
|
96
96
|
end
|
97
97
|
|
@@ -99,7 +99,7 @@ describe Px::Service::Client::Caching do
|
|
99
99
|
expect(dalli).to receive(:touch).with(a_kind_of(String), a_kind_of(Fixnum))
|
100
100
|
|
101
101
|
subject.cache_request(url, strategy: strategy) do
|
102
|
-
raise Px::Service::ServiceError.new("Error", 500)
|
102
|
+
Px::Service::Client::Future.new { raise Px::Service::ServiceError.new("Error", 500) }
|
103
103
|
end
|
104
104
|
end
|
105
105
|
end
|
@@ -115,14 +115,13 @@ describe Px::Service::Client::Caching do
|
|
115
115
|
|
116
116
|
context "when there is a cached response" do
|
117
117
|
before :each do
|
118
|
-
|
119
|
-
|
120
|
-
end
|
118
|
+
Px::Service::Client::Caching::CacheEntry.stub(:fetch).and_return(entry)
|
119
|
+
entry.expires_at = DateTime.now + 1.day
|
121
120
|
end
|
122
121
|
|
123
122
|
it "does not invoke the block" do
|
124
123
|
called = false
|
125
|
-
subject.cache_request(url, strategy: strategy) do
|
124
|
+
subject.cache_request(url, strategy: strategy) do
|
126
125
|
called = true
|
127
126
|
end
|
128
127
|
|
@@ -130,17 +129,16 @@ describe Px::Service::Client::Caching do
|
|
130
129
|
end
|
131
130
|
|
132
131
|
it "returns the response" do
|
133
|
-
expect(subject.cache_request(url, strategy: strategy)
|
132
|
+
expect(subject.cache_request(url, strategy: strategy) do
|
133
|
+
nil
|
134
|
+
end.value!).to eq(response)
|
134
135
|
end
|
135
136
|
end
|
136
137
|
|
137
138
|
context "when there is an expired cached response" do
|
138
139
|
before :each do
|
139
|
-
|
140
|
-
|
141
|
-
response
|
142
|
-
end
|
143
|
-
end
|
140
|
+
Px::Service::Client::Caching::CacheEntry.stub(:fetch).and_return(entry)
|
141
|
+
entry.expires_at = DateTime.now - 1.day
|
144
142
|
end
|
145
143
|
|
146
144
|
let (:response) { { "value" => "response" } }
|
@@ -155,7 +153,9 @@ describe Px::Service::Client::Caching do
|
|
155
153
|
end
|
156
154
|
|
157
155
|
it "returns the new response" do
|
158
|
-
expect(subject.cache_request(url, strategy: strategy)
|
156
|
+
expect(subject.cache_request(url, strategy: strategy) do
|
157
|
+
response
|
158
|
+
end.value!).to eq(response)
|
159
159
|
end
|
160
160
|
|
161
161
|
it "updates the cache entry before making the request" do
|
@@ -166,7 +166,9 @@ describe Px::Service::Client::Caching do
|
|
166
166
|
called = false
|
167
167
|
expect(subject.cache_request(url, strategy: strategy) do
|
168
168
|
called = true
|
169
|
-
|
169
|
+
response
|
170
|
+
end.value!).to eq(response)
|
171
|
+
|
170
172
|
expect(called).to be_falsey
|
171
173
|
|
172
174
|
response
|
@@ -178,20 +180,22 @@ describe Px::Service::Client::Caching do
|
|
178
180
|
response
|
179
181
|
end
|
180
182
|
|
181
|
-
expect(subject.cache_request(url, strategy: strategy)
|
183
|
+
expect(subject.cache_request(url, strategy: strategy) do
|
184
|
+
nil
|
185
|
+
end.value).to eq(response)
|
182
186
|
end
|
183
187
|
|
184
188
|
it "returns the cached response on failure" do
|
185
189
|
expect(subject.cache_request(url, strategy: strategy) do
|
186
|
-
raise Px::Service::ServiceError.new("Error", 500)
|
187
|
-
end).to eq(response)
|
190
|
+
Px::Service::Client::Future.new { raise Px::Service::ServiceError.new("Error", 500) }
|
191
|
+
end.value!).to eq(response)
|
188
192
|
end
|
189
193
|
|
190
194
|
it "does not returns the cached response on request error" do
|
191
195
|
expect {
|
192
196
|
subject.cache_request(url, strategy: strategy) do
|
193
|
-
raise Px::Service::ServiceRequestError.new("Error", 404)
|
194
|
-
end
|
197
|
+
Px::Service::Client::Future.new { raise Px::Service::ServiceRequestError.new("Error", 404) }
|
198
|
+
end.value!
|
195
199
|
}.to raise_error(Px::Service::ServiceRequestError)
|
196
200
|
end
|
197
201
|
|
@@ -199,7 +203,7 @@ describe Px::Service::Client::Caching do
|
|
199
203
|
expect(dalli).to receive(:touch).with(a_kind_of(String), a_kind_of(Fixnum)).twice
|
200
204
|
|
201
205
|
subject.cache_request(url, strategy: strategy) do
|
202
|
-
raise Px::Service::ServiceError.new("Error", 500)
|
206
|
+
Px::Service::Client::Future.new { raise Px::Service::ServiceError.new("Error", 500) }
|
203
207
|
end
|
204
208
|
end
|
205
209
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: px-service-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Micacchi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: will_paginate
|
@@ -283,7 +283,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
283
283
|
version: '0'
|
284
284
|
requirements: []
|
285
285
|
rubyforge_project:
|
286
|
-
rubygems_version: 2.
|
286
|
+
rubygems_version: 2.4.6
|
287
287
|
signing_key:
|
288
288
|
specification_version: 4
|
289
289
|
summary: Common service client behaviours for Ruby applications
|