faraday-http-cache 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|