rack-cache 1.0.3 → 1.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack-cache might be problematic. Click here for more details.

data/CHANGES CHANGED
@@ -1,3 +1,25 @@
1
+ ## 1.1 / September 2011
2
+
3
+ * Allow (INM/IMS) validation requests through to backend on miss. Makes it
4
+ possible to use validation for private / uncacheable responses. A number of
5
+ people using Rails's stale?() helper reported that their validation logic was
6
+ never kicking in.
7
+
8
+ * Add rack env rack-cache.force-pass option to bypass rack-cache on
9
+ per request basis
10
+
11
+ * Fix an issue with memcache namespace not being set when using the
12
+ :namespace option instead of :prefix_key.
13
+
14
+ * Fix test failures due to MockResponse changes in recent Rack
15
+ version (issue #34)
16
+
17
+ ## 1.0.3 / August 2011
18
+
19
+ * Fix bug passing options to memcached and dalli
20
+
21
+ * Document cache_key
22
+
1
23
  ## 1.0.1 / April 2011
2
24
 
3
25
  * Added lib/rack-cache.rb to match package name for auto-requiring machinery.
data/Gemfile CHANGED
@@ -1,7 +1,2 @@
1
1
  source :rubygems
2
-
3
- gem 'bacon'
4
- gem 'memcached'
5
- gem 'dalli'
6
- gem 'rack', '>= 0.4'
7
- gem 'rdiscount'
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,22 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rack-cache (1.0.3)
5
+ rack (>= 0.4)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ bacon (1.1.0)
11
+ dalli (1.0.5)
12
+ memcached (1.3)
13
+ rack (1.3.2)
14
+
15
+ PLATFORMS
16
+ ruby
17
+
18
+ DEPENDENCIES
19
+ bacon
20
+ dalli
21
+ memcached
22
+ rack-cache!
data/Rakefile CHANGED
@@ -1,10 +1,15 @@
1
1
  require 'rake/clean'
2
2
 
3
- task :default => :test
3
+ task :default => [:setup, :test]
4
4
 
5
5
  CLEAN.include %w[coverage/ doc/api tags]
6
6
  CLOBBER.include %w[dist]
7
7
 
8
+ desc "Install gem dependencies"
9
+ task :setup do
10
+ sh "bundle check >/dev/null || bundle install", :verbose => false
11
+ end
12
+
8
13
  # SPECS =====================================================================
9
14
 
10
15
  desc 'Run specs with unit test style output'
data/doc/faq.markdown CHANGED
@@ -110,6 +110,13 @@ frameworks are based entirely on manual purge and do not support validation at
110
110
  the cache level.
111
111
 
112
112
 
113
+ <a class='hash' id='force-pass' href='#force-pass'>#</a>
114
+
115
+ ### Q: How do I bypass rack-cache on a per-request basis?
116
+
117
+ Set the `rack-cache.force-pass` variable in the rack environment to `true`.
118
+
119
+
113
120
  <a class='hash' id='efficient-validation' href='#efficient-validation'>#</a>
114
121
 
115
122
  ### Q: What does "Efficient Validation" mean?
@@ -61,7 +61,7 @@ module Rack::Cache
61
61
 
62
62
  response =
63
63
  if @request.get? || @request.head?
64
- if !@env['HTTP_EXPECT']
64
+ if !@env['HTTP_EXPECT'] && !@env['rack-cache.force-pass']
65
65
  lookup
66
66
  else
67
67
  pass
@@ -231,15 +231,13 @@ module Rack::Cache
231
231
  end
232
232
 
233
233
  # The cache missed or a reload is required. Forward the request to the
234
- # backend and determine whether the response should be stored.
234
+ # backend and determine whether the response should be stored. This allows
235
+ # conditional / validation requests through to the backend but performs no
236
+ # caching of the response when the backend returns a 304.
235
237
  def fetch
236
238
  # send no head requests because we want content
237
239
  @env['REQUEST_METHOD'] = 'GET'
238
240
 
239
- # avoid that the backend sends no content
240
- @env.delete('HTTP_IF_MODIFIED_SINCE')
241
- @env.delete('HTTP_IF_NONE_MATCH')
242
-
243
241
  response = forward
244
242
 
245
243
  # Mark the response as explicitly private if any of the private
@@ -58,7 +58,7 @@ module Rack::Cache
58
58
  end
59
59
 
60
60
  # Write the Rack response body immediately and return the SHA1 key.
61
- def write(body)
61
+ def write(body, ttl=nil)
62
62
  buf = []
63
63
  key, size = slurp(body) { |part| buf << part }
64
64
  @hash[key] = buf
@@ -119,7 +119,7 @@ module Rack::Cache
119
119
  nil
120
120
  end
121
121
 
122
- def write(body)
122
+ def write(body, ttl=nil)
123
123
  filename = ['buf', $$, Thread.current.object_id].join('-')
124
124
  temp_file = storage_path(filename)
125
125
  key, size =
@@ -227,10 +227,10 @@ module Rack::Cache
227
227
  data
228
228
  end
229
229
 
230
- def write(body)
230
+ def write(body, ttl=nil)
231
231
  buf = StringIO.new
232
232
  key, size = slurp(body){|part| buf.write(part) }
233
- [key, size] if cache.set(key, buf.string)
233
+ [key, size] if cache.set(key, buf.string, ttl)
234
234
  end
235
235
 
236
236
  def purge(key)
@@ -267,10 +267,10 @@ module Rack::Cache
267
267
  nil
268
268
  end
269
269
 
270
- def write(body)
270
+ def write(body, ttl=0)
271
271
  buf = StringIO.new
272
272
  key, size = slurp(body){|part| buf.write(part) }
273
- cache.set(key, buf.string, 0, false)
273
+ cache.set(key, buf.string, ttl, false)
274
274
  [key, size]
275
275
  end
276
276
 
@@ -315,10 +315,10 @@ module Rack::Cache
315
315
  end
316
316
  end
317
317
 
318
- def write(body)
318
+ def write(body, ttl=nil)
319
319
  buf = StringIO.new
320
320
  key, size = slurp(body){|part| buf.write(part) }
321
- cache.put(key, buf.string)
321
+ cache.put(key, buf.string, ttl)
322
322
  [key, size]
323
323
  end
324
324
 
@@ -57,7 +57,11 @@ module Rack::Cache
57
57
  # write the response body to the entity store if this is the
58
58
  # original response.
59
59
  if response.headers['X-Content-Digest'].nil?
60
- digest, size = entity_store.write(response.body)
60
+ if request.env['rack-cache.use_native_ttl'] && response.fresh?
61
+ digest, size = entity_store.write(response.body, response.ttl)
62
+ else
63
+ digest, size = entity_store.write(response.body)
64
+ end
61
65
  response.headers['X-Content-Digest'] = digest
62
66
  response.headers['Content-Length'] = size.to_s unless response.headers['Transfer-Encoding']
63
67
  response.body = entity_store.open(digest)
@@ -337,6 +341,7 @@ module Rack::Cache
337
341
  attr_reader :cache
338
342
 
339
343
  def initialize(server="localhost:11211", options={})
344
+ options[:prefix_key] ||= options.delete(:namespace) if options.key?(:namespace)
340
345
  @cache =
341
346
  if server.respond_to?(:stats)
342
347
  server
@@ -8,6 +8,8 @@ module Rack::Cache
8
8
  # below are stored in the Rack Environment as "rack-cache.<option>", where
9
9
  # <option> is the option name.
10
10
  module Options
11
+ extend self
12
+
11
13
  def self.option_accessor(key)
12
14
  name = option_name(key)
13
15
  define_method(key) { || options[name] }
@@ -95,6 +97,10 @@ module Rack::Cache
95
97
  # default for compliance with RFC 2616.
96
98
  option_accessor :allow_revalidate
97
99
 
100
+ # Specifies whether the underlying entity store's native expiration should
101
+ # be used.
102
+ option_accessor :use_native_ttl
103
+
98
104
  # The underlying options Hash. During initialization (or outside of a
99
105
  # request), this is a default values Hash. During a request, this is the
100
106
  # Rack environment Hash. The default values Hash is merged in underneath
@@ -134,7 +140,8 @@ module Rack::Cache
134
140
  'rack-cache.default_ttl' => 0,
135
141
  'rack-cache.private_headers' => ['Authorization', 'Cookie'],
136
142
  'rack-cache.allow_reload' => false,
137
- 'rack-cache.allow_revalidate' => false
143
+ 'rack-cache.allow_revalidate' => false,
144
+ 'rack-cache.use_native_ttl' => false,
138
145
  }
139
146
  self.options = options
140
147
  end
data/rack-cache.gemspec CHANGED
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
3
3
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
4
 
5
5
  s.name = 'rack-cache'
6
- s.version = '1.0.3'
7
- s.date = '2011-08-27'
6
+ s.version = '1.1'
7
+ s.date = '2011-09-18'
8
8
 
9
9
  s.description = "HTTP Caching for Rack"
10
10
  s.summary = "HTTP Caching for Rack"
@@ -17,6 +17,7 @@ Gem::Specification.new do |s|
17
17
  CHANGES
18
18
  COPYING
19
19
  Gemfile
20
+ Gemfile.lock
20
21
  README
21
22
  Rakefile
22
23
  TODO
data/test/context_test.rb CHANGED
@@ -15,6 +15,16 @@ describe 'Rack::Cache::Context' do
15
15
  response.headers.should.not.include 'Age'
16
16
  end
17
17
 
18
+ it 'passes on rack-cache.force-pass' do
19
+ respond_with 200
20
+ get '/', {"rack-cache.force-pass" => true}
21
+
22
+ app.should.be.called
23
+ response.should.be.ok
24
+ cache.trace.should == [:pass]
25
+ response.headers.should.not.include 'Age'
26
+ end
27
+
18
28
  %w[post put delete].each do |request_method|
19
29
  it "invalidates on #{request_method} requests" do
20
30
  respond_with 200
@@ -88,8 +98,8 @@ describe 'Rack::Cache::Context' do
88
98
  'HTTP_IF_MODIFIED_SINCE' => timestamp
89
99
  app.should.be.called
90
100
  response.status.should.equal 304
91
- response.headers.should.not.include 'Content-Length'
92
- response.headers.should.not.include 'Content-Type'
101
+ response.original_headers.should.not.include 'Content-Length'
102
+ response.original_headers.should.not.include 'Content-Type'
93
103
  response.body.should.empty
94
104
  cache.trace.should.include :miss
95
105
  cache.trace.should.include :store
@@ -107,8 +117,8 @@ describe 'Rack::Cache::Context' do
107
117
  'HTTP_IF_NONE_MATCH' => '12345'
108
118
  app.should.be.called
109
119
  response.status.should.equal 304
110
- response.headers.should.not.include 'Content-Length'
111
- response.headers.should.not.include 'Content-Type'
120
+ response.original_headers.should.not.include 'Content-Length'
121
+ response.original_headers.should.not.include 'Content-Type'
112
122
  response.headers.should.include 'ETag'
113
123
  response.body.should.empty
114
124
  cache.trace.should.include :miss
@@ -24,6 +24,15 @@ shared 'A Rack::Cache::EntityStore Implementation' do
24
24
  data.should.equal 'My wild love went riding,'
25
25
  end
26
26
 
27
+ it 'takes a ttl parameter for #write' do
28
+ key, size = @store.write(['My wild love went riding,'], 0)
29
+ key.should.not.be.nil
30
+ key.should.be.sha_like
31
+
32
+ data = @store.read(key)
33
+ data.should.equal 'My wild love went riding,'
34
+ end
35
+
27
36
  it 'correctly determines whether cached body exists for key with #exist?' do
28
37
  key, size = @store.write(['She rode to the devil,'])
29
38
  @store.should.exist key
@@ -185,10 +194,20 @@ describe 'Rack::Cache::EntityStore' do
185
194
  end
186
195
  behaves_like 'A Rack::Cache::EntityStore Implementation'
187
196
  end
188
-
189
- it 'passes options from uri' do
190
- memcached = Rack::Cache::EntityStore::Dalli.resolve URI.parse("memcached://#{ENV['MEMCACHED']}?show_backtraces=true")
191
- memcached.cache.instance_variable_get(:@options)[:show_backtraces].should.equal true
197
+
198
+ describe 'options parsing' do
199
+ before do
200
+ uri = URI.parse("memcached://#{ENV['MEMCACHED']}/obj_ns1?show_backtraces=true")
201
+ @memcached_metastore = Rack::Cache::MetaStore::MemCached.resolve uri
202
+ end
203
+
204
+ it 'passes options from uri' do
205
+ @memcached_metastore.cache.instance_variable_get(:@options)[:show_backtraces].should.equal true
206
+ end
207
+
208
+ it 'takes namespace into account' do
209
+ @memcached_metastore.cache.instance_variable_get(:@options)[:prefix_key].should.equal 'obj_ns1'
210
+ end
192
211
  end
193
212
  end
194
213
 
@@ -203,10 +222,20 @@ describe 'Rack::Cache::EntityStore' do
203
222
  @store = nil
204
223
  end
205
224
  behaves_like 'A Rack::Cache::EntityStore Implementation'
206
-
225
+ end
226
+
227
+ describe 'options parsing' do
228
+ before do
229
+ uri = URI.parse("memcached://#{ENV['MEMCACHED']}/obj_ns1?show_backtraces=true")
230
+ @dalli_metastore = Rack::Cache::MetaStore::Dalli.resolve uri
231
+ end
232
+
207
233
  it 'passes options from uri' do
208
- dalli = Rack::Cache::EntityStore::Dalli.resolve URI.parse("memcached://#{ENV['MEMCACHED']}?compression=true")
209
- dalli.cache.instance_variable_get(:@options)[:compression].should.equal true
234
+ @dalli_metastore.cache.instance_variable_get(:@options)[:show_backtraces].should.equal true
235
+ end
236
+
237
+ it 'takes namespace into account' do
238
+ @dalli_metastore.cache.instance_variable_get(:@options)[:namespace].should.equal 'obj_ns1'
210
239
  end
211
240
  end
212
241
  end
@@ -265,10 +265,20 @@ describe 'Rack::Cache::MetaStore' do
265
265
  end
266
266
  behaves_like 'A Rack::Cache::MetaStore Implementation'
267
267
  end
268
-
269
- it 'passes options from uri' do
270
- memcached = Rack::Cache::EntityStore::Dalli.resolve URI.parse("memcached://#{ENV['MEMCACHED']}?show_backtraces=true")
271
- memcached.cache.instance_variable_get(:@options)[:show_backtraces].should.equal true
268
+
269
+ describe 'options parsing' do
270
+ before do
271
+ uri = URI.parse("memcached://#{ENV['MEMCACHED']}/meta_ns1?show_backtraces=true")
272
+ @memcached_metastore = Rack::Cache::MetaStore::MemCached.resolve uri
273
+ end
274
+
275
+ it 'passes options from uri' do
276
+ @memcached_metastore.cache.instance_variable_get(:@options)[:show_backtraces].should.equal true
277
+ end
278
+
279
+ it 'takes namespace into account' do
280
+ @memcached_metastore.cache.instance_variable_get(:@options)[:prefix_key].should.equal 'meta_ns1'
281
+ end
272
282
  end
273
283
  end
274
284
 
@@ -282,10 +292,20 @@ describe 'Rack::Cache::MetaStore' do
282
292
  end
283
293
  behaves_like 'A Rack::Cache::MetaStore Implementation'
284
294
  end
285
-
286
- it 'passes options from uri' do
287
- dalli = Rack::Cache::EntityStore::Dalli.resolve URI.parse("memcached://#{ENV['MEMCACHED']}?compression=true")
288
- dalli.cache.instance_variable_get(:@options)[:compression].should.equal true
295
+
296
+ describe 'options parsing' do
297
+ before do
298
+ uri = URI.parse("memcached://#{ENV['MEMCACHED']}/meta_ns1?show_backtraces=true")
299
+ @dalli_metastore = Rack::Cache::MetaStore::Dalli.resolve uri
300
+ end
301
+
302
+ it 'passes options from uri' do
303
+ @dalli_metastore.cache.instance_variable_get(:@options)[:show_backtraces].should.equal true
304
+ end
305
+
306
+ it 'takes namespace into account' do
307
+ @dalli_metastore.cache.instance_variable_get(:@options)[:namespace].should.equal 'meta_ns1'
308
+ end
289
309
  end
290
310
  end
291
311
 
metadata CHANGED
@@ -1,73 +1,94 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: rack-cache
3
- version: !ruby/object:Gem::Version
4
- version: 1.0.3
3
+ version: !ruby/object:Gem::Version
4
+ hash: 13
5
5
  prerelease:
6
+ segments:
7
+ - 1
8
+ - 1
9
+ version: "1.1"
6
10
  platform: ruby
7
- authors:
11
+ authors:
8
12
  - Ryan Tomayko
9
13
  autorequire:
10
14
  bindir: bin
11
15
  cert_chain: []
12
- date: 2011-08-27 00:00:00.000000000Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
16
+
17
+ date: 2011-09-18 00:00:00 Z
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
15
20
  name: rack
16
- requirement: &70303071454760 !ruby/object:Gem::Requirement
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
17
23
  none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0.4'
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ hash: 3
28
+ segments:
29
+ - 0
30
+ - 4
31
+ version: "0.4"
22
32
  type: :runtime
23
- prerelease: false
24
- version_requirements: *70303071454760
25
- - !ruby/object:Gem::Dependency
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
26
35
  name: bacon
27
- requirement: &70303071454380 !ruby/object:Gem::Requirement
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
28
38
  none: false
29
- requirements:
30
- - - ! '>='
31
- - !ruby/object:Gem::Version
32
- version: '0'
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
33
46
  type: :development
34
- prerelease: false
35
- version_requirements: *70303071454380
36
- - !ruby/object:Gem::Dependency
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
37
49
  name: memcached
38
- requirement: &70303071453920 !ruby/object:Gem::Requirement
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
39
52
  none: false
40
- requirements:
41
- - - ! '>='
42
- - !ruby/object:Gem::Version
43
- version: '0'
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
44
60
  type: :development
45
- prerelease: false
46
- version_requirements: *70303071453920
47
- - !ruby/object:Gem::Dependency
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
48
63
  name: dalli
49
- requirement: &70303071453500 !ruby/object:Gem::Requirement
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
50
66
  none: false
51
- requirements:
52
- - - ! '>='
53
- - !ruby/object:Gem::Version
54
- version: '0'
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
55
74
  type: :development
56
- prerelease: false
57
- version_requirements: *70303071453500
75
+ version_requirements: *id004
58
76
  description: HTTP Caching for Rack
59
77
  email: r@tomayko.com
60
78
  executables: []
79
+
61
80
  extensions: []
62
- extra_rdoc_files:
81
+
82
+ extra_rdoc_files:
63
83
  - README
64
84
  - COPYING
65
85
  - TODO
66
86
  - CHANGES
67
- files:
87
+ files:
68
88
  - CHANGES
69
89
  - COPYING
70
90
  - Gemfile
91
+ - Gemfile.lock
71
92
  - README
72
93
  - Rakefile
73
94
  - TODO
@@ -108,35 +129,43 @@ files:
108
129
  - test/storage_test.rb
109
130
  homepage: http://tomayko.com/src/rack-cache/
110
131
  licenses: []
132
+
111
133
  post_install_message:
112
- rdoc_options:
134
+ rdoc_options:
113
135
  - --line-numbers
114
136
  - --inline-source
115
137
  - --title
116
138
  - Rack::Cache
117
139
  - --main
118
140
  - Rack::Cache
119
- require_paths:
141
+ require_paths:
120
142
  - lib
121
- required_ruby_version: !ruby/object:Gem::Requirement
143
+ required_ruby_version: !ruby/object:Gem::Requirement
122
144
  none: false
123
- requirements:
124
- - - ! '>='
125
- - !ruby/object:Gem::Version
126
- version: '0'
127
- required_rubygems_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ hash: 3
149
+ segments:
150
+ - 0
151
+ version: "0"
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
153
  none: false
129
- requirements:
130
- - - ! '>='
131
- - !ruby/object:Gem::Version
132
- version: '0'
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ hash: 3
158
+ segments:
159
+ - 0
160
+ version: "0"
133
161
  requirements: []
162
+
134
163
  rubyforge_project:
135
164
  rubygems_version: 1.8.6
136
165
  signing_key:
137
166
  specification_version: 2
138
167
  summary: HTTP Caching for Rack
139
- test_files:
168
+ test_files:
140
169
  - test/cache_test.rb
141
170
  - test/cachecontrol_test.rb
142
171
  - test/context_test.rb