faraday-http-cache 0.2.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 75eed5459ea58ad83130acdab34d7acf40967eaa
4
- data.tar.gz: f5847df239db40584db36489818143e31d821222
3
+ metadata.gz: 80c7adcab041a8cfdcc1bfa6ac4a8ddd30661d9b
4
+ data.tar.gz: 688398ca6e4d77ac6769f66925c3108d5257ecdd
5
5
  SHA512:
6
- metadata.gz: f95af2c373abe3e7b3055c10fcd92b2208b01c3725da597e9b07d9e56c0ee477e7028852f883b6a7870f02ba8a65bcbd8c338e05a0b2535cd9093a63cfdc7682
7
- data.tar.gz: 8abb220a303052b25b83c4e16f40c77c4dbc6e4bd87ee33e18be28d108fde5ef2600e2baf5f5802a37e30650a89f1e8f35c7d63ffc67799ce49b5b0dc961b89f
6
+ metadata.gz: 789666e8549d1cb08978ae0870d97333e333541f0a774ec7bc10563dce42d3b4f9cf4ec425d16d03d13ae81d5420f5f5ecb8e71962525370b0aa9b39edae0f7b
7
+ data.tar.gz: 936fd149b8b358248bdb868fd347c7a53bc5115dae097a9ef6137680afc8d2584bf2f6066f328664132cc0191a9e199a813d31ee803ee1b5bdd8af02f9f8c514
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Faraday Http Cache
2
2
 
3
- [![Build Status](https://secure.travis-ci.org/plataformatec/faraday-http-cache.png)](https://travis-ci.org/plataformatec/faraday-http-cache)
3
+ [![Build Status](https://secure.travis-ci.org/plataformatec/faraday-http-cache.png?branch=master)](https://travis-ci.org/plataformatec/faraday-http-cache)
4
4
 
5
5
  a [Faraday](https://github.com/lostisland/faraday) middleware that respects HTTP cache,
6
6
  by checking expiration and validation of the stored responses.
@@ -34,13 +34,13 @@ endpoints, and any extra configuration option will be used to setup the cache st
34
34
  ```ruby
35
35
  # Connect the middleware to a Memcache instance.
36
36
  client = Faraday.new do |builder|
37
- builder.use :http_cache, :mem_cache_store, "localhost:11211"
37
+ builder.use :http_cache, store: :mem_cache_store, store_options: ['localhost:11211']
38
38
  builder.adapter Faraday.default_adapter
39
39
  end
40
40
 
41
41
  # Or use the Rails.cache instance inside your Rails app.
42
42
  client = Faraday.new do |builder|
43
- builder.use :http_cache, Rails.cache
43
+ builder.use :http_cache, store: Rails.cache
44
44
  builder.adapter Faraday.default_adapter
45
45
  end
46
46
  ```
@@ -48,6 +48,16 @@ end
48
48
  The default store provided by ActiveSupport is the `MemoryStore` one, so it's important to
49
49
  configure a proper one for your production environment.
50
50
 
51
+ MultiJson is used for serialization by default. If you expect to be dealing
52
+ with images, you can use [Marshal][marshal] instead.
53
+
54
+ ```ruby
55
+ client = Faraday.new do |builder|
56
+ builder.use :http_cache, serializer: Marshal
57
+ builder.adapter Faraday.default_adapter
58
+ end
59
+ ```
60
+
51
61
  ### Logging
52
62
 
53
63
  You can provide a `:logger` option that will be receive debug informations based on the middleware
@@ -55,7 +65,7 @@ operations:
55
65
 
56
66
  ```ruby
57
67
  client = Faraday.new do |builder|
58
- builder.use :http_cache, :logger => Rails.logger
68
+ builder.use :http_cache, logger: Rails.logger
59
69
  builder.adapter Faraday.default_adapter
60
70
  end
61
71
 
@@ -66,11 +76,25 @@ client.get('http://site/api/users')
66
76
  ## See it live
67
77
 
68
78
  You can clone this repository, install it's dependencies with Bundler (run `bundle install`) and
69
- execute the `examples/twitter.rb` file to see a sample of the middleware usage - it's issuing
70
- requests to the Twitter API and caching them, so the rate limit isn't reduced on every request by
71
- the client object. After sleeping for 5 minutes the cache will expire and the client will hit the
72
- Twitter API again.
79
+ execute the files under the `examples` directory to see a sample of the middleware usage.
80
+
81
+ ## What get's cached?
82
+
83
+ The middleware will use the following headers to make caching decisions:
84
+ - Cache-Control
85
+ - Age
86
+ - Last-Modified
87
+ - ETag
88
+ - Expires
89
+
90
+ ### Cache-Control
91
+
92
+ The `max-age`, `must-revalidate`, `proxy-revalidate` and `s-maxage` directives are checked.
93
+
94
+ Note: private caches are ignored.
73
95
 
74
96
  ## License
75
97
 
76
98
  Copyright (c) 2012-2013 Plataformatec. See LICENSE file.
99
+
100
+ [marshal]: http://www.ruby-doc.org/core-2.0/Marshal.html
@@ -2,6 +2,7 @@ require 'faraday'
2
2
  require 'multi_json'
3
3
 
4
4
  require 'active_support/core_ext/hash/slice'
5
+ require 'active_support/deprecation'
5
6
 
6
7
  require 'faraday/http_cache/storage'
7
8
  require 'faraday/http_cache/response'
@@ -26,13 +27,18 @@ module Faraday
26
27
  #
27
28
  # # Attach a Logger to the middleware.
28
29
  # client = Faraday.new do |builder|
29
- # builder.use :http_cache, :logger => my_logger_instance
30
+ # builder.use :http_cache, logger: my_logger_instance
30
31
  # builder.adapter Faraday.default_adapter
31
32
  # end
32
33
  #
33
34
  # # Provide an existing CacheStore (for instance, from a Rails app)
34
35
  # client = Faraday.new do |builder|
35
- # builder.use :http_cache, Rails.cache
36
+ # builder.use :http_cache, store: Rails.cache
37
+ # end
38
+ #
39
+ # # Use Marshal for serialization
40
+ # client = Faraday.new do |builder|
41
+ # builder.use :http_cache, serializer: Marshal
36
42
  # end
37
43
  class HttpCache < Faraday::Middleware
38
44
 
@@ -44,23 +50,27 @@ module Faraday
44
50
  # Examples:
45
51
  #
46
52
  # # Initialize the middleware with a logger.
47
- # Faraday::HttpCache.new(app, :logger => my_logger)
53
+ # Faraday::HttpCache.new(app, logger: my_logger)
54
+ #
55
+ # # Initialize the middleware with a logger and Marshal as a serializer
56
+ # Faraday:HttpCache.new(app, logger: my_logger, serializer: Marshal)
48
57
  #
49
58
  # # Initialize the middleware with a FileStore at the 'tmp' dir.
50
- # Faraday::HttpCache.new(app, :file_store, 'tmp')
51
- def initialize(app, *arguments)
59
+ # Faraday::HttpCache.new(app, store: :file_store, store_options: ['tmp'])
60
+ #
61
+ # # Initialize the middleware with a MemoryStore and logger
62
+ # Faraday::HttpCache.new(app, store: :memory_store, logger: my_logger, store_options: [size: 1024])
63
+ def initialize(app, *args)
52
64
  super(app)
53
-
54
- if arguments.last.is_a? Hash
55
- options = arguments.pop
56
- @logger = options.delete(:logger)
65
+ @logger = nil
66
+ if args.first.is_a? Hash
67
+ options = args.first
68
+ @logger = options[:logger]
57
69
  else
58
- options = arguments
70
+ options = parse_deprecated_options(*args)
59
71
  end
60
72
 
61
- store = arguments.shift
62
-
63
- @storage = Storage.new(store, options)
73
+ @storage = Storage.new(options)
64
74
  end
65
75
 
66
76
  # Public: Process the request into a duplicate of this instance to
@@ -98,6 +108,61 @@ module Faraday
98
108
  end
99
109
 
100
110
  private
111
+ # Internal: Receive the deprecated arguments to initialize the old API
112
+ # and returns a Hash compatible with the new API
113
+ #
114
+ # Examples:
115
+ #
116
+ # parse_deprecated_options(Rails.cache)
117
+ # # => { store: Rails.cache }
118
+ #
119
+ # parse_deprecated_options(:mem_cache_store)
120
+ # # => { store: :mem_cache_store }
121
+ #
122
+ # parse_deprecated_options(:mem_cache_store, logger: Rails.logger)
123
+ # # => { store: :mem_cache_store, logger: Rails.logger }
124
+ #
125
+ # parse_deprecated_options(:mem_cache_store, 'localhost:11211')
126
+ # # => { store: :mem_cache_store, store_options: ['localhost:11211] }
127
+ #
128
+ # parse_deprecated_options(:mem_cache_store, logger: Rails.logger, serializer: Marshal)
129
+ # # => { store: :mem_cache_store, logger: Rails.logger, serializer: Marshal }
130
+ #
131
+ # parse_deprecated_options(serializer: Marshal)
132
+ # # => { serializer: Marshal }
133
+ #
134
+ # parse_deprecated_options(:file_store, { serializer: Marshal }, 'tmp')
135
+ # # => { store: :file_store, serializer: Marshal, store_options: ['tmp'] }
136
+ #
137
+ # parse_deprecated_options(:memory_store, size: 1024)
138
+ # # => { store: :memory_store, store_options: [size: 1024] }
139
+ #
140
+ # Returns a hash with the following keys:
141
+ # - store
142
+ # - serializer
143
+ # - logger
144
+ # - store_options
145
+ #
146
+ # In order to check what each key means, check `Storage#initialize` description.
147
+ def parse_deprecated_options(*args)
148
+ options = {}
149
+ if args.length > 0
150
+ ActiveSupport::Deprecation.warn('This api is deprecated, refer to the documentation for the new one', caller)
151
+ end
152
+
153
+ options[:store] = args.shift
154
+
155
+ if args.first.is_a? Hash
156
+ hash_params = args.first
157
+ options[:serializer] = hash_params.delete(:serializer)
158
+
159
+ @logger = hash_params.delete(:logger)
160
+ end
161
+
162
+ options[:store_options] = args
163
+ options
164
+ end
165
+
101
166
  # Internal: Validates if the current request method is valid for caching.
102
167
  #
103
168
  # Returns true if the method is ':get' or ':head'.
@@ -147,13 +212,13 @@ module Faraday
147
212
  headers['If-Modified-Since'] = entry.last_modified if entry.last_modified
148
213
  headers['If-None-Match'] = entry.etag if entry.etag
149
214
 
150
- @app.call(env).on_complete do |env|
151
- response = Response.new(env)
215
+ @app.call(env).on_complete do |requested_env|
216
+ response = Response.new(requested_env)
152
217
  if response.not_modified?
153
218
  trace :valid
154
219
  updated_payload = entry.payload
155
220
  updated_payload[:response_headers].update(response.payload[:response_headers])
156
- env.update(updated_payload)
221
+ requested_env.update(updated_payload)
157
222
  response = Response.new(updated_payload)
158
223
  end
159
224
  store(response)
@@ -193,8 +258,8 @@ module Faraday
193
258
  # Returns the fresh 'Faraday::Response' instance.
194
259
  def fetch(env)
195
260
  trace :miss
196
- @app.call(env).on_complete do |env|
197
- response = Response.new(create_response(env))
261
+ @app.call(env).on_complete do |fresh_env|
262
+ response = Response.new(create_response(fresh_env))
198
263
  store(response)
199
264
  end
200
265
  end
@@ -238,7 +303,7 @@ module Faraday
238
303
  end
239
304
 
240
305
  if Faraday.respond_to?(:register_middleware)
241
- Faraday.register_middleware :http_cache => Faraday::HttpCache
306
+ Faraday.register_middleware http_cache: Faraday::HttpCache
242
307
  elsif Faraday::Middleware.respond_to?(:register_middleware)
243
- Faraday::Middleware.register_middleware :http_cache => Faraday::HttpCache
308
+ Faraday::Middleware.register_middleware http_cache: Faraday::HttpCache
244
309
  end
@@ -177,10 +177,12 @@ module Faraday
177
177
  @payload[:response_headers]
178
178
  end
179
179
 
180
- # Internal: prepares the response headers ready to be cached
180
+ # Internal: Prepares the response headers ready to be cached.
181
181
  #
182
- # removes the age header if present to allow cached responses to continue aging while cached
183
- # also normalizes the max age headers if age header provided to ensure accuracy once age header removed
182
+ # It removes the age header if present to allow cached responses
183
+ # to continue aging while cached. It also normalizes the 'max-age'
184
+ # related headers if the 'Age' header is provided to ensure accuracy
185
+ # once the 'Age' header is removed.
184
186
  def prepare_to_cache
185
187
  if headers.key? 'Age'
186
188
  cache_control.normalize_max_ages(headers['Age'].to_i)
@@ -188,7 +190,6 @@ module Faraday
188
190
  headers['Cache-Control'] = cache_control.to_s
189
191
  end
190
192
  end
191
-
192
193
  end
193
194
  end
194
195
  end
@@ -12,15 +12,24 @@ module Faraday
12
12
  #
13
13
  # # Reuse some other instance of a ActiveSupport::CacheStore object.
14
14
  # Faraday::HttpCache::Storage.new(Rails.cache)
15
+ #
16
+ # # Creates a new Storage using Marshal for serialization.
17
+ # Faraday::HttpCache::Storage.new(:memory_store, serializer: Marshal)
15
18
  class Storage
16
19
  attr_reader :cache
17
20
 
18
21
  # Internal: Initialize a new Storage object with a cache backend.
19
22
  #
20
- # store - An ActiveSupport::CacheStore identifier (default: nil).
21
- # options - The Hash options for the CacheStore backend (default: {}).
22
- def initialize(store = nil, options = {})
23
- @cache = ActiveSupport::Cache.lookup_store(store, options)
23
+ # options - Storage options (default: {}).
24
+ # :store - An ActiveSupport::CacheStore identifier.
25
+ # :serializer - A serializer class for the body.
26
+ # Should respond to #dump and #load.
27
+ # :store_options - An array containg the options for
28
+ # the cache store
29
+ def initialize(options = {})
30
+ store = options[:store]
31
+ @serializer = options[:serializer] || MultiJson
32
+ @cache = ActiveSupport::Cache.lookup_store(store, options[:store_options])
24
33
  end
25
34
 
26
35
  # Internal: Writes a response with a key based on the given request.
@@ -32,7 +41,7 @@ module Faraday
32
41
  # response - The Faraday::HttpCache::Response instance to be stored.
33
42
  def write(request, response)
34
43
  key = cache_key_for(request)
35
- value = MultiJson.dump(response.serializable_hash)
44
+ value = @serializer.dump(response.serializable_hash)
36
45
  cache.write(key, value)
37
46
  end
38
47
 
@@ -48,7 +57,7 @@ module Faraday
48
57
  value = cache.read(key)
49
58
 
50
59
  if value
51
- payload = MultiJson.load(value).symbolize_keys
60
+ payload = @serializer.load(value).symbolize_keys
52
61
  klass.new(payload)
53
62
  end
54
63
  end
@@ -63,7 +72,7 @@ module Faraday
63
72
  # Returns the encoded String.
64
73
  def cache_key_for(request)
65
74
  array = request.stringify_keys.to_a.sort
66
- Digest::SHA1.hexdigest(MultiJson.dump(array))
75
+ Digest::SHA1.hexdigest(@serializer.dump(array))
67
76
  end
68
77
  end
69
78
  end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Faraday::HttpCache do
4
+ let(:client) do
5
+ Faraday.new(url: ENV['FARADAY_SERVER']) do |stack|
6
+ stack.use :http_cache, serializer: Marshal
7
+ adapter = ENV['FARADAY_ADAPTER']
8
+ stack.headers['X-Faraday-Adapter'] = adapter
9
+ stack.adapter adapter.to_sym
10
+ end
11
+ end
12
+ let(:data) { IO.binread File.expand_path('../support/empty.png', __FILE__) }
13
+
14
+ it 'works fine with binary data' do
15
+ expect(client.get('image').body).to eq data
16
+ expect(client.get('image').body).to eq data
17
+ end
18
+ end
@@ -2,106 +2,106 @@ require 'spec_helper'
2
2
 
3
3
  describe Faraday::HttpCache::CacheControl do
4
4
  it 'takes a String with multiple name=value pairs' do
5
- instance = Faraday::HttpCache::CacheControl.new('max-age=600, max-stale=300, min-fresh=570')
6
- instance.max_age.should == 600
5
+ cache_control = Faraday::HttpCache::CacheControl.new('max-age=600, max-stale=300, min-fresh=570')
6
+ expect(cache_control.max_age).to eq(600)
7
7
  end
8
8
 
9
9
  it 'takes a String with a single flag value' do
10
- instance = Faraday::HttpCache::CacheControl.new('no-cache')
11
- instance.should be_no_cache
10
+ cache_control = Faraday::HttpCache::CacheControl.new('no-cache')
11
+ expect(cache_control).to be_no_cache
12
12
  end
13
13
 
14
14
  it 'takes a String with a bunch of all kinds of stuff' do
15
- instance =
15
+ cache_control =
16
16
  Faraday::HttpCache::CacheControl.new('max-age=600,must-revalidate,min-fresh=3000,foo=bar,baz')
17
- instance.max_age.should == 600
18
- instance.should be_must_revalidate
17
+ expect(cache_control.max_age).to eq(600)
18
+ expect(cache_control).to be_must_revalidate
19
19
  end
20
20
 
21
21
  it 'strips leading and trailing spaces' do
22
- instance = Faraday::HttpCache::CacheControl.new(' public, max-age = 600 ')
23
- instance.should be_public
24
- instance.max_age.should == 600
22
+ cache_control = Faraday::HttpCache::CacheControl.new(' public, max-age = 600 ')
23
+ expect(cache_control).to be_public
24
+ expect(cache_control.max_age).to eq(600)
25
25
  end
26
26
 
27
27
  it 'ignores blank segments' do
28
- instance = Faraday::HttpCache::CacheControl.new('max-age=600,,s-maxage=300')
29
- instance.max_age.should == 600
30
- instance.shared_max_age.should == 300
28
+ cache_control = Faraday::HttpCache::CacheControl.new('max-age=600,,s-maxage=300')
29
+ expect(cache_control.max_age).to eq(600)
30
+ expect(cache_control.shared_max_age).to eq(300)
31
31
  end
32
32
 
33
33
  it 'sorts alphabetically with boolean directives before value directives' do
34
- instance = Faraday::HttpCache::CacheControl.new('foo=bar, z, x, y, bling=baz, zoom=zib, b, a')
35
- instance.to_s.should == 'a, b, x, y, z, bling=baz, foo=bar, zoom=zib'
34
+ cache_control = Faraday::HttpCache::CacheControl.new('foo=bar, z, x, y, bling=baz, zoom=zib, b, a')
35
+ expect(cache_control.to_s).to eq('a, b, x, y, z, bling=baz, foo=bar, zoom=zib')
36
36
  end
37
37
 
38
38
  it 'responds to #max_age with an integer when max-age directive present' do
39
- instance = Faraday::HttpCache::CacheControl.new('public, max-age=600')
40
- instance.max_age.should == 600
39
+ cache_control = Faraday::HttpCache::CacheControl.new('public, max-age=600')
40
+ expect(cache_control.max_age).to eq(600)
41
41
  end
42
42
 
43
43
  it 'responds to #max_age with nil when no max-age directive present' do
44
- instance = Faraday::HttpCache::CacheControl.new('public')
45
- instance.max_age.should be_nil
44
+ cache_control = Faraday::HttpCache::CacheControl.new('public')
45
+ expect(cache_control.max_age).to be_nil
46
46
  end
47
47
 
48
48
  it 'responds to #shared_max_age with an integer when s-maxage directive present' do
49
- instance = Faraday::HttpCache::CacheControl.new('public, s-maxage=600')
50
- instance.shared_max_age.should == 600
49
+ cache_control = Faraday::HttpCache::CacheControl.new('public, s-maxage=600')
50
+ expect(cache_control.shared_max_age).to eq(600)
51
51
  end
52
52
 
53
53
  it 'responds to #shared_max_age with nil when no s-maxage directive present' do
54
- instance = Faraday::HttpCache::CacheControl.new('public')
55
- instance.shared_max_age.should be_nil
54
+ cache_control = Faraday::HttpCache::CacheControl.new('public')
55
+ expect(cache_control.shared_max_age).to be_nil
56
56
  end
57
57
 
58
58
  it 'responds to #public? truthfully when public directive present' do
59
- instance = Faraday::HttpCache::CacheControl.new('public')
60
- instance.should be_public
59
+ cache_control = Faraday::HttpCache::CacheControl.new('public')
60
+ expect(cache_control).to be_public
61
61
  end
62
62
 
63
63
  it 'responds to #public? non-truthfully when no public directive present' do
64
- instance = Faraday::HttpCache::CacheControl.new('private')
65
- instance.should_not be_public
64
+ cache_control = Faraday::HttpCache::CacheControl.new('private')
65
+ expect(cache_control).not_to be_public
66
66
  end
67
67
 
68
68
  it 'responds to #private? truthfully when private directive present' do
69
- instance = Faraday::HttpCache::CacheControl.new('private')
70
- instance.should be_private
69
+ cache_control = Faraday::HttpCache::CacheControl.new('private')
70
+ expect(cache_control).to be_private
71
71
  end
72
72
 
73
73
  it 'responds to #private? non-truthfully when no private directive present' do
74
- instance = Faraday::HttpCache::CacheControl.new('public')
75
- instance.should_not be_private
74
+ cache_control = Faraday::HttpCache::CacheControl.new('public')
75
+ expect(cache_control).not_to be_private
76
76
  end
77
77
 
78
78
  it 'responds to #no_cache? truthfully when no-cache directive present' do
79
- instance = Faraday::HttpCache::CacheControl.new('no-cache')
80
- instance.should be_no_cache
79
+ cache_control = Faraday::HttpCache::CacheControl.new('no-cache')
80
+ expect(cache_control).to be_no_cache
81
81
  end
82
82
 
83
83
  it 'responds to #no_cache? non-truthfully when no no-cache directive present' do
84
- instance = Faraday::HttpCache::CacheControl.new('max-age=600')
85
- instance.should_not be_no_cache
84
+ cache_control = Faraday::HttpCache::CacheControl.new('max-age=600')
85
+ expect(cache_control).not_to be_no_cache
86
86
  end
87
87
 
88
88
  it 'responds to #must_revalidate? truthfully when must-revalidate directive present' do
89
- instance = Faraday::HttpCache::CacheControl.new('must-revalidate')
90
- instance.should be_must_revalidate
89
+ cache_control = Faraday::HttpCache::CacheControl.new('must-revalidate')
90
+ expect(cache_control).to be_must_revalidate
91
91
  end
92
92
 
93
93
  it 'responds to #must_revalidate? non-truthfully when no must-revalidate directive present' do
94
- instance = Faraday::HttpCache::CacheControl.new('max-age=600')
95
- instance.should_not be_no_cache
94
+ cache_control = Faraday::HttpCache::CacheControl.new('max-age=600')
95
+ expect(cache_control).not_to be_must_revalidate
96
96
  end
97
97
 
98
98
  it 'responds to #proxy_revalidate? truthfully when proxy-revalidate directive present' do
99
- instance = Faraday::HttpCache::CacheControl.new('proxy-revalidate')
100
- instance.should be_proxy_revalidate
99
+ cache_control = Faraday::HttpCache::CacheControl.new('proxy-revalidate')
100
+ expect(cache_control).to be_proxy_revalidate
101
101
  end
102
102
 
103
103
  it 'responds to #proxy_revalidate? non-truthfully when no proxy-revalidate directive present' do
104
- instance = Faraday::HttpCache::CacheControl.new('max-age=600')
105
- instance.should_not be_no_cache
104
+ cache_control = Faraday::HttpCache::CacheControl.new('max-age=600')
105
+ expect(cache_control).not_to be_no_cache
106
106
  end
107
107
  end