faraday-http-cache 2.7.0 → 3.0.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/README.md +9 -5
- data/lib/faraday/http_cache/cache_control.rb +19 -8
- data/lib/faraday/http_cache/response.rb +16 -0
- data/lib/faraday/http_cache/strategies/base_strategy.rb +9 -2
- data/lib/faraday/http_cache/version.rb +1 -1
- data/lib/faraday/http_cache.rb +42 -5
- data/spec/cache_control_spec.rb +27 -0
- data/spec/http_cache_spec.rb +76 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/strategies/by_url_spec.rb +14 -0
- data/spec/strategies/by_vary_spec.rb +14 -0
- data/spec/support/json_gadget.rb +14 -0
- data/spec/support/test_app.rb +8 -0
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8308a71aed66c20d85c6bc345b40c36f63d6362b562e2ce21ba11c002c25929a
|
|
4
|
+
data.tar.gz: 707c84e7c6b8d0f54f7f55142649db9623f0031197d0fe08f195ab96d9e2fe49
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7cfea8ce8b26348fd53e4295aed88e46f3ac5f628d6b2ee65e0c939a034c789009e9bd403c1037a8c86b4c6f7f39fa3555d4acafc27db4c4a7219abb2c335917
|
|
7
|
+
data.tar.gz: 037b527d2ba46be6c9f961ba417fcc585f9bda4daf21eb01fd820f4c902b597a2f32df90863dc213ed0916fa1788a142a6b106d46c2bb5afd2314fb75767b6a2
|
data/README.md
CHANGED
|
@@ -62,8 +62,10 @@ you might see errors like:
|
|
|
62
62
|
Response could not be serialized: "\xC3" from ASCII-8BIT to UTF-8. Try using Marshal to serialize.
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
For full unicode support, or if you expect to be dealing with images, you can use
|
|
66
|
-
|
|
65
|
+
For full unicode support, or if you expect to be dealing with images, you can use another json
|
|
66
|
+
library like `oj` or `yajl-ruby`, or the stdlib [Marshal][marshal]. Only pick Marshal when you fully
|
|
67
|
+
trust the cache store: `Marshal.load` will instantiate any object found in the data, while the
|
|
68
|
+
default `JSON` serializer parses entries into plain hashes and never instantiates classes.
|
|
67
69
|
|
|
68
70
|
```ruby
|
|
69
71
|
client = Faraday.new do |builder|
|
|
@@ -214,9 +216,11 @@ The `max-age`, `must-revalidate`, `proxy-revalidate`, `s-maxage` and
|
|
|
214
216
|
|
|
215
217
|
### Shared vs. non-shared caches
|
|
216
218
|
|
|
217
|
-
By default, the middleware acts as a "shared cache" per RFC
|
|
218
|
-
responses with `Cache-Control: private
|
|
219
|
-
|
|
219
|
+
By default, the middleware acts as a "shared cache" per RFC 9111. This means it does not cache
|
|
220
|
+
responses with `Cache-Control: private`, and it only stores and reuses responses to requests that
|
|
221
|
+
carried an `Authorization` header when the response explicitly allows it with `public`,
|
|
222
|
+
`must-revalidate` or `s-maxage` (RFC 9111 section 3.5). This behavior can be changed by passing in
|
|
223
|
+
the `:shared_cache` configuration option:
|
|
220
224
|
|
|
221
225
|
```ruby
|
|
222
226
|
client = Faraday.new do |builder|
|
|
@@ -34,9 +34,9 @@ module Faraday
|
|
|
34
34
|
|
|
35
35
|
# Internal: Gets the 'max-age' directive as an Integer.
|
|
36
36
|
#
|
|
37
|
-
# Returns nil if the 'max-age' directive isn't present.
|
|
37
|
+
# Returns nil if the 'max-age' directive isn't present or has no value.
|
|
38
38
|
def max_age
|
|
39
|
-
|
|
39
|
+
integer_directive('max-age')
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# Internal: Gets the 'max-age' directive as an Integer.
|
|
@@ -45,16 +45,16 @@ module Faraday
|
|
|
45
45
|
# if present to account for having to remove static age header when caching responses
|
|
46
46
|
def normalize_max_ages(age)
|
|
47
47
|
if age > 0
|
|
48
|
-
@directives['max-age'] =
|
|
49
|
-
@directives['s-maxage'] =
|
|
48
|
+
@directives['max-age'] = max_age - age if max_age
|
|
49
|
+
@directives['s-maxage'] = shared_max_age - age if shared_max_age
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
# Internal: Gets the 's-maxage' directive as an Integer.
|
|
54
54
|
#
|
|
55
|
-
# Returns nil if the 's-maxage' directive isn't present.
|
|
55
|
+
# Returns nil if the 's-maxage' directive isn't present or has no value.
|
|
56
56
|
def shared_max_age
|
|
57
|
-
|
|
57
|
+
integer_directive('s-maxage')
|
|
58
58
|
end
|
|
59
59
|
alias s_maxage shared_max_age
|
|
60
60
|
|
|
@@ -70,9 +70,10 @@ module Faraday
|
|
|
70
70
|
|
|
71
71
|
# Internal: Gets the 'stale-while-revalidate' directive as an Integer.
|
|
72
72
|
#
|
|
73
|
-
# Returns nil if the 'stale-while-revalidate' directive isn't present
|
|
73
|
+
# Returns nil if the 'stale-while-revalidate' directive isn't present or
|
|
74
|
+
# has no value.
|
|
74
75
|
def stale_while_revalidate
|
|
75
|
-
|
|
76
|
+
integer_directive('stale-while-revalidate')
|
|
76
77
|
end
|
|
77
78
|
|
|
78
79
|
# Internal: Gets the String representation for the cache directives.
|
|
@@ -98,6 +99,16 @@ module Faraday
|
|
|
98
99
|
|
|
99
100
|
private
|
|
100
101
|
|
|
102
|
+
# Internal: Reads a directive whose value must be an integer.
|
|
103
|
+
# A directive given without a value (a bare 'max-age') is parsed as
|
|
104
|
+
# true; treat it as absent instead of calling to_i on it.
|
|
105
|
+
#
|
|
106
|
+
# Returns the Integer value, or nil.
|
|
107
|
+
def integer_directive(name)
|
|
108
|
+
value = @directives[name]
|
|
109
|
+
value.to_i unless value.nil? || value == true
|
|
110
|
+
end
|
|
111
|
+
|
|
101
112
|
# Internal: Parses the Cache Control string to a Hash.
|
|
102
113
|
# Existing whitespace will be removed and the string is split on commas.
|
|
103
114
|
# For each part everything before a '=' will be treated as the key
|
|
@@ -98,6 +98,22 @@ module Faraday
|
|
|
98
98
|
cacheable?(false)
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
+
# Internal: Checks if a shared cache may reuse this response for requests
|
|
102
|
+
# other than the one that carried an 'Authorization' header.
|
|
103
|
+
#
|
|
104
|
+
# RFC 9111 section 3.5: a shared cache must not use a cached response to
|
|
105
|
+
# a request with an 'Authorization' header to satisfy any subsequent
|
|
106
|
+
# request unless the response carries a 'Cache-Control' directive that
|
|
107
|
+
# explicitly allows it. The directives with that effect are
|
|
108
|
+
# 'must-revalidate', 'public' and 's-maxage'.
|
|
109
|
+
#
|
|
110
|
+
# Returns true if one of those directives is present.
|
|
111
|
+
def shared_cache_authorized?
|
|
112
|
+
cache_control.public? ||
|
|
113
|
+
cache_control.must_revalidate? ||
|
|
114
|
+
!cache_control.shared_max_age.nil?
|
|
115
|
+
end
|
|
116
|
+
|
|
101
117
|
# Internal: Gets the response age in seconds.
|
|
102
118
|
#
|
|
103
119
|
# Returns the 'Age' header if present, or subtracts the response 'date'
|
|
@@ -29,7 +29,9 @@ module Faraday
|
|
|
29
29
|
# @option options [Faraday::HttpCache::MemoryStore, nil] :store - a cache
|
|
30
30
|
# store object that should respond to 'read', 'write', and 'delete'.
|
|
31
31
|
# @option options [#dump#load] :serializer - an object that should
|
|
32
|
-
# respond to 'dump' and 'load'.
|
|
32
|
+
# respond to 'dump' and 'load'. 'load' must never instantiate classes
|
|
33
|
+
# named by the data, since the cached entries contain response headers
|
|
34
|
+
# sent by the origin server.
|
|
33
35
|
# @option options [Logger, nil] :logger - an object to be used to emit warnings.
|
|
34
36
|
def initialize(options = {})
|
|
35
37
|
@cache = options[:store] || Faraday::HttpCache::MemoryStore.new
|
|
@@ -80,7 +82,12 @@ module Faraday
|
|
|
80
82
|
end
|
|
81
83
|
|
|
82
84
|
def deserialize_object(object)
|
|
83
|
-
|
|
85
|
+
# JSON.load enables create_additions, so a `json_class` key in the
|
|
86
|
+
# entry would instantiate that class. Response headers are stored
|
|
87
|
+
# verbatim, which lets an origin server plant such a key. JSON.parse
|
|
88
|
+
# only ever builds plain Ruby objects.
|
|
89
|
+
loaded = @serializer.equal?(::JSON) ? ::JSON.parse(object) : @serializer.load(object)
|
|
90
|
+
loaded.transform_keys(&:to_sym)
|
|
84
91
|
end
|
|
85
92
|
|
|
86
93
|
def warn(message)
|
data/lib/faraday/http_cache.rb
CHANGED
|
@@ -196,7 +196,7 @@ module Faraday
|
|
|
196
196
|
def process(env)
|
|
197
197
|
entry = @strategy.read(@request)
|
|
198
198
|
|
|
199
|
-
return fetch(env) if entry.nil?
|
|
199
|
+
return fetch(env) if entry.nil? || !reusable?(entry)
|
|
200
200
|
|
|
201
201
|
if entry.fresh? && !@request.no_cache?
|
|
202
202
|
response = entry.to_response(env)
|
|
@@ -270,7 +270,7 @@ module Faraday
|
|
|
270
270
|
#
|
|
271
271
|
# Returns nothing.
|
|
272
272
|
def store(response)
|
|
273
|
-
if
|
|
273
|
+
if storable?(response)
|
|
274
274
|
trace :store
|
|
275
275
|
@strategy.write(@request, response)
|
|
276
276
|
else
|
|
@@ -278,10 +278,47 @@ module Faraday
|
|
|
278
278
|
end
|
|
279
279
|
end
|
|
280
280
|
|
|
281
|
+
# Internal: Checks if the response may be stored by this cache instance.
|
|
282
|
+
# A shared cache also refuses responses to requests that carried an
|
|
283
|
+
# 'Authorization' header unless the response explicitly allows it
|
|
284
|
+
# (RFC 9111 section 3.5), so what is never stored is never served to
|
|
285
|
+
# another caller.
|
|
286
|
+
#
|
|
287
|
+
# response - a 'Faraday::HttpCache::Response' instance.
|
|
288
|
+
#
|
|
289
|
+
# Returns true or false.
|
|
290
|
+
def storable?(response)
|
|
291
|
+
return response.cacheable_in_private_cache? unless shared_cache?
|
|
292
|
+
return false if authorization_bearing? && !response.shared_cache_authorized?
|
|
293
|
+
|
|
294
|
+
response.cacheable_in_shared_cache?
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# Internal: Checks if a stored entry may be served for the current request.
|
|
298
|
+
# Entries written by earlier versions of this middleware may be responses
|
|
299
|
+
# to authenticated requests that a shared cache must not reuse; such an
|
|
300
|
+
# entry is treated as a miss and replaced.
|
|
301
|
+
#
|
|
302
|
+
# entry - a 'Faraday::HttpCache::Response' read from the strategy.
|
|
303
|
+
#
|
|
304
|
+
# Returns true or false.
|
|
305
|
+
def reusable?(entry)
|
|
306
|
+
return true unless shared_cache? && authorization_bearing?
|
|
307
|
+
|
|
308
|
+
entry.shared_cache_authorized?
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# Internal: Checks if the current request carries an 'Authorization' header.
|
|
312
|
+
def authorization_bearing?
|
|
313
|
+
!@request.headers['Authorization'].nil?
|
|
314
|
+
end
|
|
315
|
+
|
|
281
316
|
def delete(request, response)
|
|
282
|
-
|
|
283
|
-
headers
|
|
284
|
-
|
|
317
|
+
# A response cut short by a timeout or a reset can arrive without
|
|
318
|
+
# headers; there is still an entry to invalidate for the request URL.
|
|
319
|
+
headers = response.headers || {}
|
|
320
|
+
%w[Location Content-Location].each do |header|
|
|
321
|
+
url = headers[header]
|
|
285
322
|
@strategy.delete(url) if url
|
|
286
323
|
end
|
|
287
324
|
|
data/spec/cache_control_spec.rb
CHANGED
|
@@ -52,6 +52,23 @@ describe Faraday::HttpCache::CacheControl do
|
|
|
52
52
|
expect(cache_control.shared_max_age).to eq(600)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
it 'responds to #max_age with nil when the max-age directive has no value' do
|
|
56
|
+
cache_control = Faraday::HttpCache::CacheControl.new('public, max-age')
|
|
57
|
+
expect(cache_control.max_age).to be_nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'responds to #shared_max_age with nil when the s-maxage directive has no value' do
|
|
61
|
+
cache_control = Faraday::HttpCache::CacheControl.new('public, s-maxage')
|
|
62
|
+
expect(cache_control.shared_max_age).to be_nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'normalizes max ages without raising when a directive has no value' do
|
|
66
|
+
cache_control = Faraday::HttpCache::CacheControl.new('max-age, s-maxage=600')
|
|
67
|
+
cache_control.normalize_max_ages(100)
|
|
68
|
+
expect(cache_control.max_age).to be_nil
|
|
69
|
+
expect(cache_control.shared_max_age).to eq(500)
|
|
70
|
+
end
|
|
71
|
+
|
|
55
72
|
it 'responds to #shared_max_age with nil when no s-maxage directive present' do
|
|
56
73
|
cache_control = Faraday::HttpCache::CacheControl.new('public')
|
|
57
74
|
expect(cache_control.shared_max_age).to be_nil
|
|
@@ -116,4 +133,14 @@ describe Faraday::HttpCache::CacheControl do
|
|
|
116
133
|
cache_control = Faraday::HttpCache::CacheControl.new('public, max-age=60')
|
|
117
134
|
expect(cache_control.stale_while_revalidate).to be_nil
|
|
118
135
|
end
|
|
136
|
+
|
|
137
|
+
it 'responds to #stale_while_revalidate with 0 when directive is invalid true string value' do
|
|
138
|
+
cache_control = Faraday::HttpCache::CacheControl.new('public, max-age=60, stale-while-revalidate=true')
|
|
139
|
+
expect(cache_control.stale_while_revalidate).to eq(0)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it 'responds to #stale_while_revalidate with nil when directive has no integer assignment' do
|
|
143
|
+
cache_control = Faraday::HttpCache::CacheControl.new('public, max-age=60, stale-while-revalidate')
|
|
144
|
+
expect(cache_control.stale_while_revalidate).to be_nil
|
|
145
|
+
end
|
|
119
146
|
end
|
data/spec/http_cache_spec.rb
CHANGED
|
@@ -96,6 +96,33 @@ describe Faraday::HttpCache do
|
|
|
96
96
|
client.get('broken')
|
|
97
97
|
end
|
|
98
98
|
|
|
99
|
+
it 'still expires the request URL when the response has no headers' do
|
|
100
|
+
store = Faraday::HttpCache::MemoryStore.new
|
|
101
|
+
cached = Faraday.new(url: ENV['FARADAY_SERVER']) do |stack|
|
|
102
|
+
stack.use Faraday::HttpCache, store: store
|
|
103
|
+
stack.adapter ENV['FARADAY_ADAPTER'].to_sym
|
|
104
|
+
end
|
|
105
|
+
# Mimics an adapter that gives up mid-response: the env is completed
|
|
106
|
+
# with no status worth the name and no response headers at all.
|
|
107
|
+
headerless_adapter = Class.new(Faraday::Adapter) do
|
|
108
|
+
def call(env)
|
|
109
|
+
super
|
|
110
|
+
env.status = 0
|
|
111
|
+
env.response_headers = nil
|
|
112
|
+
env.response.finish(env)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
broken = Faraday.new(url: ENV['FARADAY_SERVER']) do |stack|
|
|
116
|
+
stack.use Faraday::HttpCache, store: store
|
|
117
|
+
stack.adapter headerless_adapter
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
cached.get('get')
|
|
121
|
+
broken.post('get')
|
|
122
|
+
|
|
123
|
+
expect(cached.get('get').body).to eq('2')
|
|
124
|
+
end
|
|
125
|
+
|
|
99
126
|
it 'expires entries for the "Location" header' do
|
|
100
127
|
client.get('get')
|
|
101
128
|
client.post('delete-with-location')
|
|
@@ -121,6 +148,48 @@ describe Faraday::HttpCache do
|
|
|
121
148
|
expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [GET /private] miss, uncacheable') }
|
|
122
149
|
client.get('private')
|
|
123
150
|
end
|
|
151
|
+
|
|
152
|
+
describe 'responses to requests with an "Authorization" header' do
|
|
153
|
+
def get_as(user, path = 'authenticated')
|
|
154
|
+
client.get(path) { |request| request.headers['Authorization'] = "Bearer #{user}" }
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it 'does not serve one caller the response cached for another' do
|
|
158
|
+
alice = get_as('alice')
|
|
159
|
+
bob = get_as('bob')
|
|
160
|
+
|
|
161
|
+
expect(alice.body).to eq('1:Bearer alice')
|
|
162
|
+
expect(bob.body).to eq('2:Bearer bob')
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it 'logs that the response is uncacheable' do
|
|
166
|
+
expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [GET /authenticated] miss, uncacheable') }
|
|
167
|
+
get_as('alice')
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it 'caches responses that are explicitly marked as public' do
|
|
171
|
+
get_as('alice', 'authenticated-public')
|
|
172
|
+
bob = get_as('bob', 'authenticated-public')
|
|
173
|
+
|
|
174
|
+
expect(bob.body).to eq('1:Bearer alice')
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it 'does not serve entries stored before the authorization check existed' do
|
|
178
|
+
store = Faraday::HttpCache::MemoryStore.new
|
|
179
|
+
clients = [false, true].map do |shared|
|
|
180
|
+
Faraday.new(url: ENV['FARADAY_SERVER']) do |stack|
|
|
181
|
+
stack.use Faraday::HttpCache, store: store, shared_cache: shared
|
|
182
|
+
stack.adapter ENV['FARADAY_ADAPTER'].to_sym
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
private_client, shared_client = clients
|
|
186
|
+
|
|
187
|
+
private_client.get('authenticated') { |request| request.headers['Authorization'] = 'Bearer alice' }
|
|
188
|
+
bob = shared_client.get('authenticated') { |request| request.headers['Authorization'] = 'Bearer bob' }
|
|
189
|
+
|
|
190
|
+
expect(bob.body).to eq('2:Bearer bob')
|
|
191
|
+
end
|
|
192
|
+
end
|
|
124
193
|
end
|
|
125
194
|
|
|
126
195
|
describe 'when acting as a private cache' do
|
|
@@ -135,6 +204,13 @@ describe Faraday::HttpCache do
|
|
|
135
204
|
expect(logger).to receive(:debug) { |&block| expect(block.call).to eq('HTTP Cache: [GET /private] miss, store') }
|
|
136
205
|
client.get('private')
|
|
137
206
|
end
|
|
207
|
+
|
|
208
|
+
it 'caches responses to requests with an "Authorization" header' do
|
|
209
|
+
client.get('authenticated') { |request| request.headers['Authorization'] = 'Bearer alice' }
|
|
210
|
+
bob = client.get('authenticated') { |request| request.headers['Authorization'] = 'Bearer bob' }
|
|
211
|
+
|
|
212
|
+
expect(bob.body).to eq('1:Bearer alice')
|
|
213
|
+
end
|
|
138
214
|
end
|
|
139
215
|
|
|
140
216
|
it 'does not cache responses with a explicit no-store directive' do
|
data/spec/spec_helper.rb
CHANGED
|
@@ -16,6 +16,20 @@ describe Faraday::HttpCache::Strategies::ByUrl do
|
|
|
16
16
|
let(:strategy) { described_class.new(store: cache) }
|
|
17
17
|
subject { strategy }
|
|
18
18
|
|
|
19
|
+
describe 'deserializing entries' do
|
|
20
|
+
let(:response) { double(serializable_hash: { response_headers: { 'json_class' => 'JsonGadget' } }) }
|
|
21
|
+
|
|
22
|
+
before { JsonGadget.invocations.clear }
|
|
23
|
+
|
|
24
|
+
it 'never instantiates classes named by the cached data' do
|
|
25
|
+
strategy.write(request, response)
|
|
26
|
+
cached = strategy.read(request)
|
|
27
|
+
|
|
28
|
+
expect(JsonGadget.invocations).to be_empty
|
|
29
|
+
expect(cached.payload[:response_headers]['json_class']).to eq('JsonGadget')
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
19
33
|
describe 'Cache configuration' do
|
|
20
34
|
it 'uses a MemoryStore by default' do
|
|
21
35
|
expect(Faraday::HttpCache::MemoryStore).to receive(:new).and_call_original
|
|
@@ -23,6 +23,20 @@ describe Faraday::HttpCache::Strategies::ByVary do
|
|
|
23
23
|
let(:strategy) { described_class.new(store: cache) }
|
|
24
24
|
subject { strategy }
|
|
25
25
|
|
|
26
|
+
describe 'deserializing entries' do
|
|
27
|
+
let(:response_payload) { { response_headers: { 'Vary' => vary, 'json_class' => 'JsonGadget' } } }
|
|
28
|
+
|
|
29
|
+
before { JsonGadget.invocations.clear }
|
|
30
|
+
|
|
31
|
+
it 'never instantiates classes named by the cached data' do
|
|
32
|
+
strategy.write(request, response)
|
|
33
|
+
cached = strategy.read(request)
|
|
34
|
+
|
|
35
|
+
expect(JsonGadget.invocations).to be_empty
|
|
36
|
+
expect(cached.payload[:response_headers]['json_class']).to eq('JsonGadget')
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
26
40
|
describe 'storing responses' do
|
|
27
41
|
shared_examples 'A strategy with serialization' do
|
|
28
42
|
it 'writes the response object to the underlying cache' do
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# A class that records every attempt to build it through JSON.load's
|
|
4
|
+
# create_additions hook, so specs can assert cached entries never do that.
|
|
5
|
+
class JsonGadget
|
|
6
|
+
def self.invocations
|
|
7
|
+
@invocations ||= []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.json_create(attributes)
|
|
11
|
+
invocations << attributes
|
|
12
|
+
new
|
|
13
|
+
end
|
|
14
|
+
end
|
data/spec/support/test_app.rb
CHANGED
|
@@ -85,6 +85,14 @@ class TestApp < Sinatra::Base
|
|
|
85
85
|
halt 405
|
|
86
86
|
end
|
|
87
87
|
|
|
88
|
+
get '/authenticated' do
|
|
89
|
+
[200, { 'Cache-Control' => 'max-age=200' }, "#{increment_counter}:#{env['HTTP_AUTHORIZATION']}"]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
get '/authenticated-public' do
|
|
93
|
+
[200, { 'Cache-Control' => 'public, max-age=200' }, "#{increment_counter}:#{env['HTTP_AUTHORIZATION']}"]
|
|
94
|
+
end
|
|
95
|
+
|
|
88
96
|
get '/private' do
|
|
89
97
|
[200, { 'Cache-Control' => 'private, max-age=100' }, increment_counter]
|
|
90
98
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: faraday-http-cache
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lucas Mazza
|
|
@@ -59,6 +59,7 @@ files:
|
|
|
59
59
|
- spec/strategies/by_url_spec.rb
|
|
60
60
|
- spec/strategies/by_vary_spec.rb
|
|
61
61
|
- spec/support/empty.png
|
|
62
|
+
- spec/support/json_gadget.rb
|
|
62
63
|
- spec/support/test_app.rb
|
|
63
64
|
- spec/support/test_server.rb
|
|
64
65
|
- spec/validation_spec.rb
|
|
@@ -73,14 +74,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
73
74
|
requirements:
|
|
74
75
|
- - ">="
|
|
75
76
|
- !ruby/object:Gem::Version
|
|
76
|
-
version: 3.
|
|
77
|
+
version: 3.3.0
|
|
77
78
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
79
|
requirements:
|
|
79
80
|
- - ">="
|
|
80
81
|
- !ruby/object:Gem::Version
|
|
81
82
|
version: '0'
|
|
82
83
|
requirements: []
|
|
83
|
-
rubygems_version:
|
|
84
|
+
rubygems_version: 3.6.9
|
|
84
85
|
specification_version: 4
|
|
85
86
|
summary: A Faraday middleware that stores and validates cache expiration.
|
|
86
87
|
test_files:
|
|
@@ -97,6 +98,7 @@ test_files:
|
|
|
97
98
|
- spec/strategies/by_url_spec.rb
|
|
98
99
|
- spec/strategies/by_vary_spec.rb
|
|
99
100
|
- spec/support/empty.png
|
|
101
|
+
- spec/support/json_gadget.rb
|
|
100
102
|
- spec/support/test_app.rb
|
|
101
103
|
- spec/support/test_server.rb
|
|
102
104
|
- spec/validation_spec.rb
|