faraday-http-cache 0.3.0 → 0.4.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/LICENSE +1 -1
- data/README.md +32 -15
- data/lib/faraday/http_cache.rb +58 -18
- data/lib/faraday/http_cache/response.rb +38 -10
- data/lib/faraday/http_cache/storage.rb +92 -21
- data/spec/json_spec.rb +3 -0
- data/spec/middleware_spec.rb +64 -20
- data/spec/response_spec.rb +47 -16
- data/spec/spec_helper.rb +1 -7
- data/spec/storage_spec.rb +35 -9
- data/spec/support/test_app.rb +4 -3
- metadata +10 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f18d30f7f544974ede44d28d9bba250e746a9b2e
|
4
|
+
data.tar.gz: 29cfe8e36786c6a0a47d3d9f1b3cbdb2388f1a19
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 14809735e62b3f547d9a8a9b8e35ce1be0f44a962650a2c196d4dece1c372829184766cc979ec1bf8cad3cfd4011dd086aebefd74e1f11847b684e277853bf8e
|
7
|
+
data.tar.gz: a1cdb3e8286c86b03f61dfb6516affbe2b743707b4b7c7d68a8c45e34df55bfd9dcd9e3c7e8290d8f965f3ee36af762a8be2fd751730a6842244a00534c05eac
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -15,26 +15,30 @@ gem 'faraday-http-cache'
|
|
15
15
|
|
16
16
|
## Usage and configuration
|
17
17
|
|
18
|
-
You have to use the middleware in the Faraday instance that you want to
|
18
|
+
You have to use the middleware in the Faraday instance that you want to,
|
19
|
+
along with a suitable `store` to cache the responses. You can use the new
|
19
20
|
shortcut using a symbol or passing the middleware class
|
20
21
|
|
21
22
|
```ruby
|
22
23
|
client = Faraday.new do |builder|
|
23
|
-
builder.use :http_cache
|
24
|
+
builder.use :http_cache, store: Rails.cache
|
24
25
|
# or
|
25
|
-
builder.use Faraday::HttpCache
|
26
|
+
builder.use Faraday::HttpCache, store: Rails.cache
|
26
27
|
|
27
28
|
builder.adapter Faraday.default_adapter
|
28
29
|
end
|
29
30
|
```
|
30
31
|
|
31
|
-
The middleware
|
32
|
-
|
32
|
+
The middleware accepts a `store` option for the cache backend responsible for recording
|
33
|
+
the API responses that should be stored. Stores should respond to `write` and `read`,
|
34
|
+
just like an object from the `ActiveSupport::Cache` API.
|
33
35
|
|
34
36
|
```ruby
|
35
37
|
# Connect the middleware to a Memcache instance.
|
38
|
+
store = ActiveSupport::Cache.lookup_store(:mem_cache_store, ['localhost:11211'])
|
39
|
+
|
36
40
|
client = Faraday.new do |builder|
|
37
|
-
builder.use :http_cache, store:
|
41
|
+
builder.use :http_cache, store: store
|
38
42
|
builder.adapter Faraday.default_adapter
|
39
43
|
end
|
40
44
|
|
@@ -44,16 +48,16 @@ client = Faraday.new do |builder|
|
|
44
48
|
builder.adapter Faraday.default_adapter
|
45
49
|
end
|
46
50
|
```
|
51
|
+
The default store provided is a simple in memory cache that lives on the client
|
52
|
+
instance, so it's important to configure a proper one for your production environment.
|
47
53
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
MultiJson is used for serialization by default. If you expect to be dealing
|
52
|
-
with images, you can use [Marshal][marshal] instead.
|
54
|
+
the stdlib `JSON` module is used for serialization by default.
|
55
|
+
If you expect to be dealing with images, you can use [Marshal][marshal] instead, or
|
56
|
+
if you want to use another json library like `oj` or `yajl-ruby`.
|
53
57
|
|
54
58
|
```ruby
|
55
59
|
client = Faraday.new do |builder|
|
56
|
-
builder.use :http_cache, serializer: Marshal
|
60
|
+
builder.use :http_cache, store: Rails.cache, serializer: Marshal
|
57
61
|
builder.adapter Faraday.default_adapter
|
58
62
|
end
|
59
63
|
```
|
@@ -65,7 +69,7 @@ operations:
|
|
65
69
|
|
66
70
|
```ruby
|
67
71
|
client = Faraday.new do |builder|
|
68
|
-
builder.use :http_cache, logger: Rails.logger
|
72
|
+
builder.use :http_cache, store: Rails.cache, logger: Rails.logger
|
69
73
|
builder.adapter Faraday.default_adapter
|
70
74
|
end
|
71
75
|
|
@@ -91,10 +95,23 @@ The middleware will use the following headers to make caching decisions:
|
|
91
95
|
|
92
96
|
The `max-age`, `must-revalidate`, `proxy-revalidate` and `s-maxage` directives are checked.
|
93
97
|
|
94
|
-
|
98
|
+
### Shared vs. non-shared caches
|
99
|
+
|
100
|
+
By default, the middleware acts as a "shared cache" per RFC 2616. This means it does not cache
|
101
|
+
responses with `Cache-Control: private`. This behavior can be changed by passing in the
|
102
|
+
`:shared_cache` configuration option:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
client = Faraday.new do |builder|
|
106
|
+
builder.use :http_cache, shared_cache: false
|
107
|
+
builder.adapter Faraday.default_adapter
|
108
|
+
end
|
109
|
+
|
110
|
+
client.get('http://site/api/some-private-resource') # => will be cached
|
111
|
+
```
|
95
112
|
|
96
113
|
## License
|
97
114
|
|
98
|
-
Copyright (c) 2012-
|
115
|
+
Copyright (c) 2012-2014 Plataformatec. See LICENSE file.
|
99
116
|
|
100
117
|
[marshal]: http://www.ruby-doc.org/core-2.0/Marshal.html
|
data/lib/faraday/http_cache.rb
CHANGED
@@ -1,8 +1,4 @@
|
|
1
1
|
require 'faraday'
|
2
|
-
require 'multi_json'
|
3
|
-
|
4
|
-
require 'active_support/core_ext/hash/slice'
|
5
|
-
require 'active_support/deprecation'
|
6
2
|
|
7
3
|
require 'faraday/http_cache/storage'
|
8
4
|
require 'faraday/http_cache/response'
|
@@ -21,13 +17,13 @@ module Faraday
|
|
21
17
|
#
|
22
18
|
# # Using the middleware with a simple client:
|
23
19
|
# client = Faraday.new do |builder|
|
24
|
-
# builder.user :http_cache
|
20
|
+
# builder.user :http_cache, store: my_store_backend
|
25
21
|
# builder.adapter Faraday.default_adapter
|
26
22
|
# end
|
27
23
|
#
|
28
24
|
# # Attach a Logger to the middleware.
|
29
25
|
# client = Faraday.new do |builder|
|
30
|
-
# builder.use :http_cache, logger: my_logger_instance
|
26
|
+
# builder.use :http_cache, logger: my_logger_instance, store: my_store_backend
|
31
27
|
# builder.adapter Faraday.default_adapter
|
32
28
|
# end
|
33
29
|
#
|
@@ -38,14 +34,21 @@ module Faraday
|
|
38
34
|
#
|
39
35
|
# # Use Marshal for serialization
|
40
36
|
# client = Faraday.new do |builder|
|
41
|
-
# builder.use :http_cache, serializer: Marshal
|
37
|
+
# builder.use :http_cache, store: Rails.cache, serializer: Marshal
|
42
38
|
# end
|
43
39
|
class HttpCache < Faraday::Middleware
|
40
|
+
# Internal: valid options for the 'initialize' configuration Hash.
|
41
|
+
VALID_OPTIONS = [:store, :serializer, :logger, :store_options, :shared_cache]
|
44
42
|
|
45
43
|
# Public: Initializes a new HttpCache middleware.
|
46
44
|
#
|
47
|
-
# app
|
48
|
-
#
|
45
|
+
# app - the next endpoint on the 'Faraday' stack.
|
46
|
+
# args - aditional options to setup the logger and the storage.
|
47
|
+
# :logger - A logger object.
|
48
|
+
# :serializer - A serializer that should respond to 'dump' and 'load'.
|
49
|
+
# :shared_cache - A flag to mark the middleware as a shared cache or not.
|
50
|
+
# :store - A cache store that should respond to 'read' and 'write'.
|
51
|
+
# :store_options - Deprecated: additional options to setup the cache store.
|
49
52
|
#
|
50
53
|
# Examples:
|
51
54
|
#
|
@@ -56,20 +59,25 @@ module Faraday
|
|
56
59
|
# Faraday:HttpCache.new(app, logger: my_logger, serializer: Marshal)
|
57
60
|
#
|
58
61
|
# # Initialize the middleware with a FileStore at the 'tmp' dir.
|
59
|
-
#
|
62
|
+
# store = ActiveSupport::Cache.lookup_store(:file_store, ['tmp'])
|
63
|
+
# Faraday::HttpCache.new(app, store: store)
|
60
64
|
#
|
61
65
|
# # Initialize the middleware with a MemoryStore and logger
|
62
|
-
#
|
66
|
+
# store = ActiveSupport::Cache.lookup_store
|
67
|
+
# Faraday::HttpCache.new(app, store: store, logger: my_logger)
|
63
68
|
def initialize(app, *args)
|
64
69
|
super(app)
|
65
70
|
@logger = nil
|
71
|
+
@shared_cache = true
|
66
72
|
if args.first.is_a? Hash
|
67
73
|
options = args.first
|
68
74
|
@logger = options[:logger]
|
75
|
+
@shared_cache = options.fetch(:shared_cache, true)
|
69
76
|
else
|
70
77
|
options = parse_deprecated_options(*args)
|
71
78
|
end
|
72
79
|
|
80
|
+
assert_valid_options!(options)
|
73
81
|
@storage = Storage.new(options)
|
74
82
|
end
|
75
83
|
|
@@ -107,6 +115,12 @@ module Faraday
|
|
107
115
|
end
|
108
116
|
end
|
109
117
|
|
118
|
+
# Internal: Should this cache instance act like a "shared cache" according
|
119
|
+
# to the the definition in RFC 2616?
|
120
|
+
def shared_cache?
|
121
|
+
@shared_cache
|
122
|
+
end
|
123
|
+
|
110
124
|
private
|
111
125
|
# Internal: Receive the deprecated arguments to initialize the old API
|
112
126
|
# and returns a Hash compatible with the new API
|
@@ -147,7 +161,7 @@ module Faraday
|
|
147
161
|
def parse_deprecated_options(*args)
|
148
162
|
options = {}
|
149
163
|
if args.length > 0
|
150
|
-
|
164
|
+
Kernel.warn('DEPRECATION WARNING: This API is deprecated, refer to the documentation for the new one', caller)
|
151
165
|
end
|
152
166
|
|
153
167
|
options[:store] = args.shift
|
@@ -156,7 +170,8 @@ module Faraday
|
|
156
170
|
hash_params = args.first
|
157
171
|
options[:serializer] = hash_params.delete(:serializer)
|
158
172
|
|
159
|
-
@logger = hash_params
|
173
|
+
@logger = hash_params[:logger]
|
174
|
+
@shared_cache = hash_params.fetch(:shared_cache, true)
|
160
175
|
end
|
161
176
|
|
162
177
|
options[:store_options] = args
|
@@ -243,7 +258,7 @@ module Faraday
|
|
243
258
|
#
|
244
259
|
# Returns nothing.
|
245
260
|
def store(response)
|
246
|
-
if response.
|
261
|
+
if shared_cache? ? response.cacheable_in_shared_cache? : response.cacheable_in_private_cache?
|
247
262
|
trace :store
|
248
263
|
@storage.write(@request, response)
|
249
264
|
else
|
@@ -271,7 +286,13 @@ module Faraday
|
|
271
286
|
# Returns a 'Hash' containing the ':status', ':body' and 'response_headers'
|
272
287
|
# entries.
|
273
288
|
def create_response(env)
|
274
|
-
env.to_hash
|
289
|
+
hash = env.to_hash
|
290
|
+
|
291
|
+
{
|
292
|
+
status: hash[:status],
|
293
|
+
body: hash[:body],
|
294
|
+
response_headers: hash[:response_headers]
|
295
|
+
}
|
275
296
|
end
|
276
297
|
|
277
298
|
# Internal: Creates a new 'Hash' containing the request information.
|
@@ -281,9 +302,13 @@ module Faraday
|
|
281
302
|
# Returns a 'Hash' containing the ':method', ':url' and 'request_headers'
|
282
303
|
# entries.
|
283
304
|
def create_request(env)
|
284
|
-
|
285
|
-
|
286
|
-
|
305
|
+
hash = env.to_hash
|
306
|
+
|
307
|
+
{
|
308
|
+
method: hash[:method],
|
309
|
+
url: hash[:url],
|
310
|
+
request_headers: hash[:request_headers].dup
|
311
|
+
}
|
287
312
|
end
|
288
313
|
|
289
314
|
# Internal: Logs the trace info about the incoming request
|
@@ -299,6 +324,21 @@ module Faraday
|
|
299
324
|
line = "HTTP Cache: [#{method} #{path}] #{@trace.join(', ')}"
|
300
325
|
@logger.debug(line)
|
301
326
|
end
|
327
|
+
|
328
|
+
# Internal: Checks if the given 'options' Hash contains only
|
329
|
+
# valid keys. Please see the 'VALID_OPTIONS' constant for the
|
330
|
+
# acceptable keys.
|
331
|
+
#
|
332
|
+
# Raises an 'ArgumentError'.
|
333
|
+
#
|
334
|
+
# Returns nothing.
|
335
|
+
def assert_valid_options!(options)
|
336
|
+
options.each_key do |key|
|
337
|
+
unless VALID_OPTIONS.include?(key)
|
338
|
+
raise ArgumentError.new("Unknown option: #{key}. Valid options are :#{VALID_OPTIONS.join(', ')}")
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
302
342
|
end
|
303
343
|
end
|
304
344
|
|
@@ -59,17 +59,29 @@ module Faraday
|
|
59
59
|
@payload[:status] == 304
|
60
60
|
end
|
61
61
|
|
62
|
-
# Internal: Checks if the response can be cached by the client
|
63
|
-
#
|
64
|
-
# status code and it's
|
62
|
+
# Internal: Checks if the response can be cached by the client when the
|
63
|
+
# client is acting as a shared cache per RFC 2616. This is validated by
|
64
|
+
# the 'Cache-Control' directives, the response status code and it's
|
65
|
+
# freshness or validation status.
|
66
|
+
#
|
67
|
+
# Returns false if the 'Cache-Control' says that we can't store the
|
68
|
+
# response, or it can be stored in private caches only, or if isn't fresh
|
69
|
+
# or it can't be revalidated with the origin server. Otherwise, returns
|
70
|
+
# true.
|
71
|
+
def cacheable_in_shared_cache?
|
72
|
+
cacheable?(true)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Internal: Checks if the response can be cached by the client when the
|
76
|
+
# client is acting as a private cache per RFC 2616. This is validated by
|
77
|
+
# the 'Cache-Control' directives, the response status code and it's
|
78
|
+
# freshness or validation status.
|
65
79
|
#
|
66
80
|
# Returns false if the 'Cache-Control' says that we can't store the
|
67
81
|
# response, or if isn't fresh or it can't be revalidated with the origin
|
68
82
|
# server. Otherwise, returns true.
|
69
|
-
def
|
70
|
-
|
71
|
-
|
72
|
-
cacheable_status_code? && (validateable? || fresh?)
|
83
|
+
def cacheable_in_private_cache?
|
84
|
+
cacheable?(false)
|
73
85
|
end
|
74
86
|
|
75
87
|
# Internal: Gets the response age in seconds.
|
@@ -124,7 +136,12 @@ module Faraday
|
|
124
136
|
# Returns a 'Hash'.
|
125
137
|
def serializable_hash
|
126
138
|
prepare_to_cache
|
127
|
-
|
139
|
+
|
140
|
+
{
|
141
|
+
status: @payload[:status],
|
142
|
+
body: @payload[:body],
|
143
|
+
response_headers: @payload[:response_headers]
|
144
|
+
}
|
128
145
|
end
|
129
146
|
|
130
147
|
private
|
@@ -137,6 +154,15 @@ module Faraday
|
|
137
154
|
headers.key?('Last-Modified') || headers.key?('ETag')
|
138
155
|
end
|
139
156
|
|
157
|
+
# Internal: The logic behind cacheable_in_private_cache? and
|
158
|
+
# cacheable_in_shared_cache? The logic is the same except for the
|
159
|
+
# treatment of the private Cache-Control directive.
|
160
|
+
def cacheable?(shared_cache)
|
161
|
+
return false if (cache_control.private? && shared_cache) || cache_control.no_store?
|
162
|
+
|
163
|
+
cacheable_status_code? && (validateable? || fresh?)
|
164
|
+
end
|
165
|
+
|
140
166
|
# Internal: Validates the response status against the
|
141
167
|
# `CACHEABLE_STATUS_CODES' constant.
|
142
168
|
#
|
@@ -177,12 +203,14 @@ module Faraday
|
|
177
203
|
@payload[:response_headers]
|
178
204
|
end
|
179
205
|
|
180
|
-
# Internal: Prepares the response headers
|
206
|
+
# Internal: Prepares the response headers to be cached.
|
181
207
|
#
|
182
|
-
# It removes the
|
208
|
+
# It removes the 'Age' header if present to allow cached responses
|
183
209
|
# to continue aging while cached. It also normalizes the 'max-age'
|
184
210
|
# related headers if the 'Age' header is provided to ensure accuracy
|
185
211
|
# once the 'Age' header is removed.
|
212
|
+
#
|
213
|
+
# Returns nothing.
|
186
214
|
def prepare_to_cache
|
187
215
|
if headers.key? 'Age'
|
188
216
|
cache_control.normalize_max_ages(headers['Age'].to_i)
|
@@ -1,10 +1,9 @@
|
|
1
|
+
require 'json'
|
1
2
|
require 'digest/sha1'
|
2
|
-
require 'active_support/cache'
|
3
|
-
require 'active_support/core_ext/hash/keys'
|
4
3
|
|
5
4
|
module Faraday
|
6
5
|
class HttpCache < Faraday::Middleware
|
7
|
-
# Internal: A Wrapper around
|
6
|
+
# Internal: A Wrapper around an ActiveSupport::CacheStore to store responses.
|
8
7
|
#
|
9
8
|
# Examples
|
10
9
|
# # Creates a new Storage using a MemCached backend from ActiveSupport.
|
@@ -16,20 +15,25 @@ module Faraday
|
|
16
15
|
# # Creates a new Storage using Marshal for serialization.
|
17
16
|
# Faraday::HttpCache::Storage.new(:memory_store, serializer: Marshal)
|
18
17
|
class Storage
|
19
|
-
attr_reader :cache
|
20
|
-
|
21
18
|
# Internal: Initialize a new Storage object with a cache backend.
|
22
19
|
#
|
23
|
-
# options
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
20
|
+
# options - Storage options (default: {}).
|
21
|
+
# :logger - A Logger object to be used to emit warnings.
|
22
|
+
# :store - An cache store object that should
|
23
|
+
# respond to 'dump' and 'load'.
|
24
|
+
# :serializer - A serializer object that should
|
25
|
+
# respond to 'dump' and 'load'.
|
26
|
+
# :store_options - An array containg the options for
|
27
|
+
# the cache store.
|
29
28
|
def initialize(options = {})
|
30
|
-
|
31
|
-
@serializer = options[:serializer] ||
|
32
|
-
@
|
29
|
+
@cache = options[:store] || MemoryStore.new
|
30
|
+
@serializer = options[:serializer] || JSON
|
31
|
+
@logger = options[:logger]
|
32
|
+
if @cache.is_a? Symbol
|
33
|
+
@cache = lookup_store(@cache, options[:store_options])
|
34
|
+
end
|
35
|
+
assert_valid_store!
|
36
|
+
notify_memory_store_usage
|
33
37
|
end
|
34
38
|
|
35
39
|
# Internal: Writes a response with a key based on the given request.
|
@@ -42,7 +46,7 @@ module Faraday
|
|
42
46
|
def write(request, response)
|
43
47
|
key = cache_key_for(request)
|
44
48
|
value = @serializer.dump(response.serializable_hash)
|
45
|
-
cache.write(key, value)
|
49
|
+
@cache.write(key, value)
|
46
50
|
end
|
47
51
|
|
48
52
|
# Internal: Reads a key based on the given request from the underlying cache.
|
@@ -53,11 +57,14 @@ module Faraday
|
|
53
57
|
# :request_headers - The custom headers for the request.
|
54
58
|
# klass - The Class to be instantiated with the recovered informations.
|
55
59
|
def read(request, klass = Faraday::HttpCache::Response)
|
56
|
-
|
57
|
-
|
60
|
+
cache_key = cache_key_for(request)
|
61
|
+
found = @cache.read(cache_key)
|
62
|
+
|
63
|
+
if found
|
64
|
+
payload = @serializer.load(found).each_with_object({}) do |(key,value), hash|
|
65
|
+
hash[key.to_sym] = value
|
66
|
+
end
|
58
67
|
|
59
|
-
if value
|
60
|
-
payload = @serializer.load(value).symbolize_keys
|
61
68
|
klass.new(payload)
|
62
69
|
end
|
63
70
|
end
|
@@ -71,8 +78,72 @@ module Faraday
|
|
71
78
|
#
|
72
79
|
# Returns the encoded String.
|
73
80
|
def cache_key_for(request)
|
74
|
-
|
75
|
-
|
81
|
+
cache_keys = request.each_with_object([]) do |(key, value), parts|
|
82
|
+
parts << [key.to_s, value]
|
83
|
+
end
|
84
|
+
|
85
|
+
Digest::SHA1.hexdigest(@serializer.dump(cache_keys.sort))
|
86
|
+
end
|
87
|
+
|
88
|
+
# Internal: Logs a warning when the 'cache' implementation
|
89
|
+
# isn't suitable for production use.
|
90
|
+
#
|
91
|
+
# Returns nothing.
|
92
|
+
def notify_memory_store_usage
|
93
|
+
return if @logger.nil?
|
94
|
+
|
95
|
+
kind = @cache.class.name.split('::').last.sub('Store', '').downcase
|
96
|
+
if kind == 'memory'
|
97
|
+
@logger.warn 'HTTP Cache: using a MemoryStore is not advised as the cache might not be persisted across multiple processes or connection instances.'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Internal: Creates a cache store from 'ActiveSupport' with a set of options.
|
102
|
+
#
|
103
|
+
# store - A 'Symbol' with the store name.
|
104
|
+
# options - Additional options for the cache store.
|
105
|
+
#
|
106
|
+
# Returns an 'ActiveSupport::Cache' store.
|
107
|
+
def lookup_store(store, options)
|
108
|
+
if @logger
|
109
|
+
@logger.warn "Passing a Symbol as the 'store' is deprecated, please pass the cache store instead."
|
110
|
+
end
|
111
|
+
|
112
|
+
begin
|
113
|
+
require 'active_support/cache'
|
114
|
+
ActiveSupport::Cache.lookup_store(store, options)
|
115
|
+
rescue LoadError => e
|
116
|
+
puts "You're missing the 'activesupport' gem. Add it to your Gemfile, bundle it and try again"
|
117
|
+
raise e
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Internal: Checks if the given cache object supports the
|
122
|
+
# expect API ('read' and 'write').
|
123
|
+
#
|
124
|
+
# Raises an 'ArgumentError'.
|
125
|
+
#
|
126
|
+
# Returns nothing.
|
127
|
+
def assert_valid_store!
|
128
|
+
unless @cache.respond_to?(:read) && @cache.respond_to?(:write)
|
129
|
+
raise ArgumentError.new("#{@cache.inspect} is not a valid cache store as it does not responds to 'read' and 'write'.")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Internal: A Hash based store to be used by the 'Storage' class
|
135
|
+
# when a 'store' is not provided for the middleware setup.
|
136
|
+
class MemoryStore
|
137
|
+
def initialize
|
138
|
+
@cache = {}
|
139
|
+
end
|
140
|
+
|
141
|
+
def read(key)
|
142
|
+
@cache[key]
|
143
|
+
end
|
144
|
+
|
145
|
+
def write(key, value)
|
146
|
+
@cache[key] = value
|
76
147
|
end
|
77
148
|
end
|
78
149
|
end
|
data/spec/json_spec.rb
CHANGED
@@ -12,6 +12,9 @@ describe Faraday::HttpCache do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
it 'works fine with other middlewares' do
|
15
|
+
if Faraday::VERSION == '0.9.0'
|
16
|
+
pending 'faraday_middleware is not compatible with faraday 0.9'
|
17
|
+
end
|
15
18
|
client.get('clear')
|
16
19
|
expect(client.get('json').body['count']).to eq(1)
|
17
20
|
expect(client.get('json').body['count']).to eq(1)
|
data/spec/middleware_spec.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Faraday::HttpCache do
|
4
|
-
let(:logger) { double('a Logger object', debug: nil) }
|
4
|
+
let(:logger) { double('a Logger object', debug: nil, warn: nil) }
|
5
|
+
let(:options) { { logger: logger } }
|
5
6
|
|
6
7
|
let(:client) do
|
7
8
|
Faraday.new(url: ENV['FARADAY_SERVER']) do |stack|
|
8
|
-
stack.use Faraday::HttpCache,
|
9
|
+
stack.use Faraday::HttpCache, options
|
9
10
|
adapter = ENV['FARADAY_ADAPTER']
|
10
11
|
stack.headers['X-Faraday-Adapter'] = adapter
|
11
12
|
stack.adapter adapter.to_sym
|
@@ -36,14 +37,32 @@ describe Faraday::HttpCache do
|
|
36
37
|
client.get('broken')
|
37
38
|
end
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
describe 'when acting as a shared cache' do
|
41
|
+
let(:options) { { logger: logger, shared_cache: true } }
|
42
|
+
|
43
|
+
it 'does not cache requests with a private cache control' do
|
44
|
+
client.get('private')
|
45
|
+
expect(client.get('private').body).to eq('2')
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'logs that a private response is invalid' do
|
49
|
+
expect(logger).to receive(:debug).with('HTTP Cache: [GET /private] miss, invalid')
|
50
|
+
client.get('private')
|
51
|
+
end
|
42
52
|
end
|
43
53
|
|
44
|
-
|
45
|
-
|
46
|
-
|
54
|
+
describe 'when acting as a private cache' do
|
55
|
+
let(:options) { { logger: logger, shared_cache: false } }
|
56
|
+
|
57
|
+
it 'does cache requests with a private cache control' do
|
58
|
+
client.get('private')
|
59
|
+
expect(client.get('private').body).to eq('1')
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'logs that a private response is stored' do
|
63
|
+
expect(logger).to receive(:debug).with('HTTP Cache: [GET /private] miss, store')
|
64
|
+
client.get('private')
|
65
|
+
end
|
47
66
|
end
|
48
67
|
|
49
68
|
it 'does not cache requests with a explicit no-store directive' do
|
@@ -152,42 +171,67 @@ describe Faraday::HttpCache do
|
|
152
171
|
expect(first_vary).not_to eql(second_vary)
|
153
172
|
end
|
154
173
|
|
174
|
+
it 'raises an error when misconfigured' do
|
175
|
+
expect {
|
176
|
+
client = Faraday.new(url: ENV['FARADAY_SERVER']) do |stack|
|
177
|
+
stack.use Faraday::HttpCache, i_have_no_idea: true
|
178
|
+
end
|
179
|
+
|
180
|
+
client.get('get')
|
181
|
+
}.to raise_error(ArgumentError)
|
182
|
+
end
|
183
|
+
|
155
184
|
describe 'Configuration options' do
|
156
185
|
let(:app) { double('it is an app!') }
|
157
186
|
|
158
187
|
it 'uses the options to create a Cache Store' do
|
159
|
-
|
160
|
-
|
188
|
+
store = double(read: nil, write: nil)
|
189
|
+
|
190
|
+
expect(Faraday::HttpCache::Storage).to receive(:new).with(store: store)
|
191
|
+
Faraday::HttpCache.new(app, store: store)
|
161
192
|
end
|
162
193
|
|
163
194
|
it 'accepts a Hash option' do
|
164
|
-
expect(ActiveSupport::Cache).to receive(:lookup_store).with(:memory_store, [{ size: 1024 }])
|
195
|
+
expect(ActiveSupport::Cache).to receive(:lookup_store).with(:memory_store, [{ size: 1024 }]).and_call_original
|
165
196
|
Faraday::HttpCache.new(app, store: :memory_store, store_options: [size: 1024])
|
166
197
|
end
|
167
198
|
|
168
199
|
it 'consumes the "logger" key' do
|
169
|
-
expect(ActiveSupport::Cache).to receive(:lookup_store).with(:memory_store, nil)
|
200
|
+
expect(ActiveSupport::Cache).to receive(:lookup_store).with(:memory_store, nil).and_call_original
|
170
201
|
Faraday::HttpCache.new(app, store: :memory_store, logger: logger)
|
171
202
|
end
|
172
203
|
|
204
|
+
describe '#shared_cache?' do
|
205
|
+
it 'is true by default' do
|
206
|
+
expect(Faraday::HttpCache.new(app).shared_cache?).to eq(true)
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'is true when configured to true' do
|
210
|
+
expect(Faraday::HttpCache.new(app, shared_cache: true).shared_cache?).to eq(true)
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'is false when configured to be false' do
|
214
|
+
expect(Faraday::HttpCache.new(app, shared_cache: false).shared_cache?).to eq(false)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
173
218
|
context 'with deprecated options format' do
|
219
|
+
before do
|
220
|
+
allow(Kernel).to receive(:warn)
|
221
|
+
end
|
222
|
+
|
174
223
|
it 'uses the options to create a Cache Store' do
|
175
|
-
expect(ActiveSupport::Cache).to receive(:lookup_store).with(:file_store, ['tmp'])
|
224
|
+
expect(ActiveSupport::Cache).to receive(:lookup_store).with(:file_store, ['tmp']).and_call_original
|
176
225
|
Faraday::HttpCache.new(app, :file_store, 'tmp')
|
177
226
|
end
|
178
227
|
|
179
228
|
it 'accepts a Hash option' do
|
180
|
-
expect(ActiveSupport::Cache).to receive(:lookup_store).with(:memory_store, [{ size: 1024 }])
|
229
|
+
expect(ActiveSupport::Cache).to receive(:lookup_store).with(:memory_store, [{ size: 1024 }]).and_call_original
|
181
230
|
Faraday::HttpCache.new(app, :memory_store, size: 1024)
|
182
231
|
end
|
183
232
|
|
184
|
-
it 'consumes the "logger" key' do
|
185
|
-
expect(ActiveSupport::Cache).to receive(:lookup_store).with(:memory_store, [{}])
|
186
|
-
Faraday::HttpCache.new(app, :memory_store, logger: logger)
|
187
|
-
end
|
188
|
-
|
189
233
|
it 'warns the user about the deprecated options' do
|
190
|
-
expect(
|
234
|
+
expect(Kernel).to receive(:warn)
|
191
235
|
|
192
236
|
Faraday::HttpCache.new(app, :memory_store, logger: logger)
|
193
237
|
end
|
data/spec/response_spec.rb
CHANGED
@@ -1,25 +1,56 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Faraday::HttpCache::Response do
|
4
|
-
describe '
|
4
|
+
describe 'cacheable_in_shared_cache?' do
|
5
5
|
it 'the response is not cacheable if the response is marked as private' do
|
6
|
-
headers = { 'Cache-Control' => 'private' }
|
7
|
-
response = Faraday::HttpCache::Response.new(response_headers: headers)
|
6
|
+
headers = { 'Cache-Control' => 'private, max-age=400' }
|
7
|
+
response = Faraday::HttpCache::Response.new(status: 200, response_headers: headers)
|
8
8
|
|
9
|
-
expect(response).not_to
|
9
|
+
expect(response).not_to be_cacheable_in_shared_cache
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'the response is not cacheable if it should not be stored' do
|
13
|
-
headers = { 'Cache-Control' => 'no-store' }
|
14
|
-
response = Faraday::HttpCache::Response.new(response_headers: headers)
|
13
|
+
headers = { 'Cache-Control' => 'no-store, max-age=400' }
|
14
|
+
response = Faraday::HttpCache::Response.new(status: 200, response_headers: headers)
|
15
|
+
|
16
|
+
expect(response).not_to be_cacheable_in_shared_cache
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'the response is not cacheable when the status code is not acceptable' do
|
20
|
+
headers = { 'Cache-Control' => 'max-age=400' }
|
21
|
+
response = Faraday::HttpCache::Response.new(status: 503, response_headers: headers)
|
22
|
+
expect(response).not_to be_cacheable_in_shared_cache
|
23
|
+
end
|
24
|
+
|
25
|
+
[200, 203, 300, 301, 302, 404, 410].each do |status|
|
26
|
+
it "the response is cacheable if the status code is #{status} and the response is fresh" do
|
27
|
+
headers = { 'Cache-Control' => 'max-age=400' }
|
28
|
+
response = Faraday::HttpCache::Response.new(status: status, response_headers: headers)
|
29
|
+
|
30
|
+
expect(response).to be_cacheable_in_shared_cache
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'cacheable_in_private_cache?' do
|
36
|
+
it 'the response is cacheable if the response is marked as private' do
|
37
|
+
headers = { 'Cache-Control' => 'private, max-age=400' }
|
38
|
+
response = Faraday::HttpCache::Response.new(status: 200, response_headers: headers)
|
39
|
+
|
40
|
+
expect(response).to be_cacheable_in_private_cache
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'the response is not cacheable if it should not be stored' do
|
44
|
+
headers = { 'Cache-Control' => 'no-store, max-age=400' }
|
45
|
+
response = Faraday::HttpCache::Response.new(status: 200, response_headers: headers)
|
15
46
|
|
16
|
-
expect(response).not_to
|
47
|
+
expect(response).not_to be_cacheable_in_private_cache
|
17
48
|
end
|
18
49
|
|
19
50
|
it 'the response is not cacheable when the status code is not acceptable' do
|
20
51
|
headers = { 'Cache-Control' => 'max-age=400' }
|
21
52
|
response = Faraday::HttpCache::Response.new(status: 503, response_headers: headers)
|
22
|
-
expect(response).not_to
|
53
|
+
expect(response).not_to be_cacheable_in_private_cache
|
23
54
|
end
|
24
55
|
|
25
56
|
[200, 203, 300, 301, 302, 404, 410].each do |status|
|
@@ -27,14 +58,14 @@ describe Faraday::HttpCache::Response do
|
|
27
58
|
headers = { 'Cache-Control' => 'max-age=400' }
|
28
59
|
response = Faraday::HttpCache::Response.new(status: status, response_headers: headers)
|
29
60
|
|
30
|
-
expect(response).to
|
61
|
+
expect(response).to be_cacheable_in_private_cache
|
31
62
|
end
|
32
63
|
end
|
33
64
|
end
|
34
65
|
|
35
66
|
describe 'freshness' do
|
36
67
|
it 'is fresh if the response still has some time to live' do
|
37
|
-
date = 200.
|
68
|
+
date = (Time.now - 200).httpdate
|
38
69
|
headers = { 'Cache-Control' => 'max-age=400', 'Date' => date }
|
39
70
|
response = Faraday::HttpCache::Response.new(response_headers: headers)
|
40
71
|
|
@@ -42,7 +73,7 @@ describe Faraday::HttpCache::Response do
|
|
42
73
|
end
|
43
74
|
|
44
75
|
it 'is not fresh when the ttl has expired' do
|
45
|
-
date = 500.
|
76
|
+
date = (Time.now - 500).httpdate
|
46
77
|
headers = { 'Cache-Control' => 'max-age=400', 'Date' => date }
|
47
78
|
response = Faraday::HttpCache::Response.new(response_headers: headers)
|
48
79
|
|
@@ -108,7 +139,7 @@ describe Faraday::HttpCache::Response do
|
|
108
139
|
end
|
109
140
|
|
110
141
|
it 'calculates the time from the "Date" header' do
|
111
|
-
date = 3.
|
142
|
+
date = (Time.now - 3).httpdate
|
112
143
|
response = Faraday::HttpCache::Response.new(response_headers: { 'Date' => date })
|
113
144
|
expect(response.age).to eq(3)
|
114
145
|
end
|
@@ -121,7 +152,7 @@ describe Faraday::HttpCache::Response do
|
|
121
152
|
|
122
153
|
describe 'time to live calculation' do
|
123
154
|
it 'returns the time to live based on the max age limit' do
|
124
|
-
date = 200.
|
155
|
+
date = (Time.now - 200).httpdate
|
125
156
|
headers = { 'Cache-Control' => 'max-age=400', 'Date' => date }
|
126
157
|
response = Faraday::HttpCache::Response.new(response_headers: headers)
|
127
158
|
expect(response.ttl).to eq(200)
|
@@ -160,9 +191,9 @@ describe Faraday::HttpCache::Response do
|
|
160
191
|
headers = {
|
161
192
|
'Age' => 6,
|
162
193
|
'Cache-Control' => 'public, max-age=40',
|
163
|
-
'Date' => 38.
|
164
|
-
'Expires' => 37.
|
165
|
-
'Last-Modified' => 300.
|
194
|
+
'Date' => (Time.now - 38).httpdate,
|
195
|
+
'Expires' => (Time.now - 37).httpdate,
|
196
|
+
'Last-Modified' => (Time.now - 300).httpdate
|
166
197
|
}
|
167
198
|
response = Faraday::HttpCache::Response.new(response_headers: headers)
|
168
199
|
expect(response).to be_fresh
|
data/spec/spec_helper.rb
CHANGED
@@ -3,9 +3,7 @@ require 'socket'
|
|
3
3
|
|
4
4
|
require 'faraday-http-cache'
|
5
5
|
require 'faraday_middleware'
|
6
|
-
require 'active_support/
|
7
|
-
require 'active_support/core_ext/numeric/time'
|
8
|
-
require 'json'
|
6
|
+
require 'active_support/cache'
|
9
7
|
|
10
8
|
require 'support/test_app'
|
11
9
|
require 'support/test_server'
|
@@ -18,14 +16,10 @@ ENV['FARADAY_ADAPTER'] ||= 'net_http'
|
|
18
16
|
server.start
|
19
17
|
|
20
18
|
RSpec.configure do |config|
|
21
|
-
config.treat_symbols_as_metadata_keys_with_true_values = true
|
22
19
|
config.run_all_when_everything_filtered = true
|
23
|
-
config.filter_run :focus
|
24
20
|
config.order = 'random'
|
25
21
|
|
26
22
|
config.after(:suite) do
|
27
23
|
server.stop
|
28
24
|
end
|
29
25
|
end
|
30
|
-
|
31
|
-
ActiveSupport::Deprecation.silenced = true
|
data/spec/storage_spec.rb
CHANGED
@@ -13,9 +13,34 @@ describe Faraday::HttpCache::Storage do
|
|
13
13
|
subject { storage }
|
14
14
|
|
15
15
|
describe 'Cache configuration' do
|
16
|
-
it '
|
17
|
-
expect(
|
18
|
-
Faraday::HttpCache::Storage.new
|
16
|
+
it 'uses a MemoryStore by default' do
|
17
|
+
expect(Faraday::HttpCache::MemoryStore).to receive(:new).and_call_original
|
18
|
+
Faraday::HttpCache::Storage.new
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'emits a warning when using a MemoryStore' do
|
22
|
+
logger = double
|
23
|
+
expect(logger).to receive(:warn).with(/using a MemoryStore is not advised/)
|
24
|
+
Faraday::HttpCache::Storage.new(logger: logger)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'lookups an ActiveSupport cache store if a Symbol is given' do
|
28
|
+
expect(ActiveSupport::Cache).to receive(:lookup_store).with(:file_store, ['/tmp']).and_call_original
|
29
|
+
Faraday::HttpCache::Storage.new(store: :file_store, store_options: ['/tmp'])
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'emits a warning when doing the lookup of an ActiveSupport cache store' do
|
33
|
+
logger = double
|
34
|
+
expect(logger).to receive(:warn).with(/Passing a Symbol as the 'store' is deprecated/)
|
35
|
+
Faraday::HttpCache::Storage.new(store: :file_store, logger: logger)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'raises an error when the given store is not valid' do
|
39
|
+
wrong = double
|
40
|
+
|
41
|
+
expect {
|
42
|
+
Faraday::HttpCache::Storage.new(store: wrong)
|
43
|
+
}.to raise_error(ArgumentError)
|
19
44
|
end
|
20
45
|
end
|
21
46
|
|
@@ -29,7 +54,7 @@ describe Faraday::HttpCache::Storage do
|
|
29
54
|
end
|
30
55
|
|
31
56
|
context 'with default serializer' do
|
32
|
-
let(:serialized) {
|
57
|
+
let(:serialized) { JSON.dump(response.serializable_hash) }
|
33
58
|
let(:cache_key) { '503ac9f7180ca1cdec49e8eb73a9cc0b47c27325' }
|
34
59
|
it_behaves_like 'serialization'
|
35
60
|
end
|
@@ -63,9 +88,9 @@ describe Faraday::HttpCache::Storage do
|
|
63
88
|
headers = {
|
64
89
|
'Age' => 6,
|
65
90
|
'Cache-Control' => 'public, max-age=40',
|
66
|
-
'Date' => 38.
|
67
|
-
'Expires' => 37.
|
68
|
-
'Last-Modified' => 300.
|
91
|
+
'Date' => (Time.now - 38).httpdate,
|
92
|
+
'Expires' => (Time.now - 37).httpdate,
|
93
|
+
'Last-Modified' => (Time.now - 300).httpdate
|
69
94
|
}
|
70
95
|
response = Faraday::HttpCache::Response.new(response_headers: headers)
|
71
96
|
expect(response).to be_fresh
|
@@ -78,9 +103,10 @@ describe Faraday::HttpCache::Storage do
|
|
78
103
|
|
79
104
|
it 'is fresh until cached and that 1 second elapses then the response is no longer fresh' do
|
80
105
|
headers = {
|
81
|
-
'Date' => 39.
|
82
|
-
'Expires' => 40.
|
106
|
+
'Date' => (Time.now - 39).httpdate,
|
107
|
+
'Expires' => (Time.now + 40).httpdate,
|
83
108
|
}
|
109
|
+
|
84
110
|
response = Faraday::HttpCache::Response.new(response_headers: headers)
|
85
111
|
expect(response).to be_fresh
|
86
112
|
subject.write(request, response)
|
data/spec/support/test_app.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'sinatra/base'
|
2
|
+
require 'json'
|
2
3
|
|
3
4
|
class TestApp < Sinatra::Base
|
4
5
|
|
@@ -8,7 +9,7 @@ class TestApp < Sinatra::Base
|
|
8
9
|
|
9
10
|
set :counter, 0
|
10
11
|
set :requests, 0
|
11
|
-
set :yesterday, 1.
|
12
|
+
set :yesterday, (Date.today - 1).httpdate
|
12
13
|
|
13
14
|
get '/ping' do
|
14
15
|
'PONG'
|
@@ -21,7 +22,7 @@ class TestApp < Sinatra::Base
|
|
21
22
|
end
|
22
23
|
|
23
24
|
get '/json' do
|
24
|
-
json =
|
25
|
+
json = JSON.dump(count: increment_counter.to_i)
|
25
26
|
[200, { 'Cache-Control' => 'max-age=400', 'Content-Type' => 'application/json' }, json]
|
26
27
|
end
|
27
28
|
|
@@ -44,7 +45,7 @@ class TestApp < Sinatra::Base
|
|
44
45
|
end
|
45
46
|
|
46
47
|
get '/private' do
|
47
|
-
[200, { 'Cache-Control' => 'private' }, increment_counter]
|
48
|
+
[200, { 'Cache-Control' => 'private, max-age=100' }, increment_counter]
|
48
49
|
end
|
49
50
|
|
50
51
|
get '/dontstore' do
|
metadata
CHANGED
@@ -1,71 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: faraday-http-cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lucas Mazza
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: activesupport
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - '>='
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '3.0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - '>='
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '3.0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: faraday
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
30
16
|
requirements:
|
31
|
-
- - ~>
|
17
|
+
- - "~>"
|
32
18
|
- !ruby/object:Gem::Version
|
33
19
|
version: '0.8'
|
34
20
|
type: :runtime
|
35
21
|
prerelease: false
|
36
22
|
version_requirements: !ruby/object:Gem::Requirement
|
37
23
|
requirements:
|
38
|
-
- - ~>
|
24
|
+
- - "~>"
|
39
25
|
- !ruby/object:Gem::Version
|
40
26
|
version: '0.8'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: multi_json
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ~>
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '1.3'
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ~>
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '1.3'
|
55
27
|
description: Middleware to handle HTTP caching
|
56
28
|
email:
|
57
|
-
-
|
29
|
+
- opensource@plataformatec.com.br
|
58
30
|
executables: []
|
59
31
|
extensions: []
|
60
32
|
extra_rdoc_files: []
|
61
33
|
files:
|
62
34
|
- LICENSE
|
63
35
|
- README.md
|
36
|
+
- lib/faraday-http-cache.rb
|
37
|
+
- lib/faraday/http_cache.rb
|
64
38
|
- lib/faraday/http_cache/cache_control.rb
|
65
39
|
- lib/faraday/http_cache/response.rb
|
66
40
|
- lib/faraday/http_cache/storage.rb
|
67
|
-
- lib/faraday/http_cache.rb
|
68
|
-
- lib/faraday-http-cache.rb
|
69
41
|
- spec/binary_spec.rb
|
70
42
|
- spec/cache_control_spec.rb
|
71
43
|
- spec/json_spec.rb
|
@@ -86,17 +58,17 @@ require_paths:
|
|
86
58
|
- lib
|
87
59
|
required_ruby_version: !ruby/object:Gem::Requirement
|
88
60
|
requirements:
|
89
|
-
- -
|
61
|
+
- - ">="
|
90
62
|
- !ruby/object:Gem::Version
|
91
63
|
version: '0'
|
92
64
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
65
|
requirements:
|
94
|
-
- -
|
66
|
+
- - ">="
|
95
67
|
- !ruby/object:Gem::Version
|
96
68
|
version: '0'
|
97
69
|
requirements: []
|
98
70
|
rubyforge_project:
|
99
|
-
rubygems_version: 2.0
|
71
|
+
rubygems_version: 2.2.0
|
100
72
|
signing_key:
|
101
73
|
specification_version: 4
|
102
74
|
summary: A Faraday middleware that stores and validates cache expiration.
|