rack-cache 1.0 → 1.0.1
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.
Potentially problematic release.
This version of rack-cache might be problematic. Click here for more details.
- data/CHANGES +19 -0
- data/Rakefile +8 -7
- data/doc/index.markdown +2 -2
- data/doc/storage.markdown +4 -4
- data/lib/rack-cache.rb +1 -0
- data/lib/rack/cache/context.rb +9 -1
- data/lib/rack/cache/entitystore.rb +3 -1
- data/lib/rack/cache/metastore.rb +2 -1
- data/lib/rack/cache/response.rb +1 -0
- data/rack-cache.gemspec +4 -3
- data/test/cache_test.rb +2 -2
- data/test/cachecontrol_test.rb +6 -6
- data/test/context_test.rb +3 -3
- data/test/entitystore_test.rb +11 -12
- data/test/key_test.rb +12 -12
- data/test/metastore_test.rb +47 -41
- data/test/options_test.rb +2 -2
- data/test/request_test.rb +3 -3
- data/test/response_test.rb +4 -4
- data/test/spec_setup.rb +23 -28
- data/test/storage_test.rb +4 -4
- metadata +6 -28
data/CHANGES
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
## 1.0.1 / April 2011
|
2
|
+
|
3
|
+
* Added lib/rack-cache.rb to match package name for auto-requiring machinery.
|
4
|
+
|
5
|
+
* Fixed a number of issues caused by Rack::Cache not closing the body received
|
6
|
+
from the application. Rack::Lock and other middleware use body.close to
|
7
|
+
signal the true end of request processing so failure to call this method
|
8
|
+
can result in strange issues (e.g.,
|
9
|
+
"ThreadError: deadlock; recursive locking")
|
10
|
+
|
11
|
+
* Fixed a bug where Rack::Cache would blow up writing the rack env to the meta
|
12
|
+
store when the env contained an all uppercase key whose value wasn't
|
13
|
+
marshalable. Passenger and some other stuff write such keys apparently.
|
14
|
+
|
15
|
+
* The test suite has moved from test-spec to bacon. This is a short term
|
16
|
+
solution to the problem of not being able to run tests under Ruby 1.9.x.
|
17
|
+
The test suite will be moved to basic Test::Unit style sometime in the
|
18
|
+
future.
|
19
|
+
|
1
20
|
## 1.0 / December 2010
|
2
21
|
|
3
22
|
* Rack::Cache is 1.0 and will now maintain semantic versioning <http://semver.org/>
|
data/Rakefile
CHANGED
@@ -7,20 +7,21 @@ CLOBBER.include %w[dist]
|
|
7
7
|
|
8
8
|
# SPECS =====================================================================
|
9
9
|
|
10
|
-
desc 'Run specs with story style output'
|
11
|
-
task :spec do
|
12
|
-
sh 'specrb --specdox -Ilib:test test/*_test.rb'
|
13
|
-
end
|
14
|
-
|
15
10
|
desc 'Run specs with unit test style output'
|
16
11
|
task :test => FileList['test/*_test.rb'] do |t|
|
17
12
|
suite = t.prerequisites
|
18
|
-
sh "
|
13
|
+
sh "bacon -q -I.:lib:test #{suite.join(' ')}", :verbose => false
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Run specs with story style output'
|
17
|
+
task :spec => FileList['test/*_test.rb'] do |t|
|
18
|
+
suite = t.prerequisites
|
19
|
+
sh "bacon -I.:lib:test #{suite.join(' ')}", :verbose => false
|
19
20
|
end
|
20
21
|
|
21
22
|
desc 'Generate test coverage report'
|
22
23
|
task :rcov do
|
23
|
-
sh "rcov -
|
24
|
+
sh "rcov -I.:lib:test test/*_test.rb"
|
24
25
|
end
|
25
26
|
|
26
27
|
# DOC =======================================================================
|
data/doc/index.markdown
CHANGED
@@ -12,8 +12,8 @@ for [Rack][]-based applications that produce freshness (`Expires`,
|
|
12
12
|
News
|
13
13
|
----
|
14
14
|
|
15
|
-
* Rack::Cache 0
|
16
|
-
[`CHANGES`](http://github.com/rtomayko/rack-cache/blob/
|
15
|
+
* Rack::Cache 1.0 was released on December 24, 2010. See the
|
16
|
+
[`CHANGES`](http://github.com/rtomayko/rack-cache/blob/1.0/CHANGES) file
|
17
17
|
for details.
|
18
18
|
* [How to use Rack::Cache with Rails 2.3](http://snippets.aktagon.com/snippets/302-How-to-setup-and-use-Rack-Cache-with-Rails-2-3-0-RC-1) - it's really easy.
|
19
19
|
* [RailsLab's Advanced HTTP Caching Screencast](http://railslab.newrelic.com/2009/02/26/episode-11-advanced-http-caching)
|
data/doc/storage.markdown
CHANGED
@@ -141,11 +141,11 @@ The URI must specify the host and port of a remote memcached daemon. The path
|
|
141
141
|
portion is an optional (but recommended) namespace that is prepended to each
|
142
142
|
cache key.
|
143
143
|
|
144
|
-
The memcached storage backend requires either the `
|
145
|
-
|
146
|
-
|
144
|
+
The memcached storage backend requires either the `dalli` or `memcached`
|
145
|
+
libraries. By default, the `dalli` library is used; require the `memcached`
|
146
|
+
library explicitly to use it instead.
|
147
147
|
|
148
|
-
gem install
|
148
|
+
gem install dalli
|
149
149
|
|
150
150
|
Memcached storage is reasonably fast and allows multiple backends to share a
|
151
151
|
single cache. It is also the only storage implementation that allows the cache
|
data/lib/rack-cache.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'rack/cache'
|
data/lib/rack/cache/context.rb
CHANGED
@@ -83,7 +83,10 @@ module Rack::Cache
|
|
83
83
|
|
84
84
|
# tidy up response a bit
|
85
85
|
response.not_modified! if not_modified?(response)
|
86
|
-
|
86
|
+
if @request.head?
|
87
|
+
response.body.close if response.body.respond_to?(:close)
|
88
|
+
response.body = []
|
89
|
+
end
|
87
90
|
response.to_a
|
88
91
|
end
|
89
92
|
|
@@ -212,6 +215,11 @@ module Rack::Cache
|
|
212
215
|
next unless value = response.headers[name]
|
213
216
|
entry.headers[name] = value
|
214
217
|
end
|
218
|
+
|
219
|
+
# even though it's empty, be sure to close the response body from upstream
|
220
|
+
# because middleware use close to signal end of response
|
221
|
+
response.body.close if response.body.respond_to?(:close)
|
222
|
+
|
215
223
|
response = entry
|
216
224
|
else
|
217
225
|
record :invalid
|
data/lib/rack/cache/metastore.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'digest/sha1'
|
3
3
|
require 'rack/utils'
|
4
|
+
require 'rack/cache/key'
|
4
5
|
|
5
6
|
module Rack::Cache
|
6
7
|
|
@@ -110,7 +111,7 @@ module Rack::Cache
|
|
110
111
|
# returned must be marshalable.
|
111
112
|
def persist_request(request)
|
112
113
|
env = request.env.dup
|
113
|
-
env.reject! { |key,val| key =~ /[^0-9A-Z_]/ }
|
114
|
+
env.reject! { |key,val| key =~ /[^0-9A-Z_]/ || !val.respond_to?(:to_str) }
|
114
115
|
env
|
115
116
|
end
|
116
117
|
|
data/lib/rack/cache/response.rb
CHANGED
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'
|
7
|
-
s.date = '
|
6
|
+
s.version = '1.0.1'
|
7
|
+
s.date = '2011-04-13'
|
8
8
|
|
9
9
|
s.description = "HTTP Caching for Rack"
|
10
10
|
s.summary = "HTTP Caching for Rack"
|
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
|
|
29
29
|
doc/storage.markdown
|
30
30
|
example/sinatra/app.rb
|
31
31
|
example/sinatra/views/index.erb
|
32
|
+
lib/rack-cache.rb
|
32
33
|
lib/rack/cache.rb
|
33
34
|
lib/rack/cache/appengine.rb
|
34
35
|
lib/rack/cache/cachecontrol.rb
|
@@ -61,7 +62,7 @@ Gem::Specification.new do |s|
|
|
61
62
|
s.extra_rdoc_files = %w[README COPYING TODO CHANGES]
|
62
63
|
s.add_dependency 'rack', '>= 0.4'
|
63
64
|
|
64
|
-
s.add_development_dependency '
|
65
|
+
s.add_development_dependency 'bacon'
|
65
66
|
s.add_development_dependency 'memcached'
|
66
67
|
s.add_development_dependency 'dalli'
|
67
68
|
|
data/test/cache_test.rb
CHANGED
@@ -25,7 +25,7 @@ describe 'Rack::Cache::new' do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'takes a block; executes it during initialization' do
|
28
|
-
state, object
|
28
|
+
state, object = 'not invoked', nil
|
29
29
|
instance =
|
30
30
|
Rack::Cache.new @app do |cache|
|
31
31
|
object = cache
|
@@ -33,6 +33,6 @@ describe 'Rack::Cache::new' do
|
|
33
33
|
cache.should.respond_to :set
|
34
34
|
end
|
35
35
|
state.should.equal 'invoked'
|
36
|
-
object.should.be instance
|
36
|
+
object.should.be.same_as instance
|
37
37
|
end
|
38
38
|
end
|
data/test/cachecontrol_test.rb
CHANGED
@@ -11,7 +11,7 @@ describe 'Rack::Cache::CacheControl' do
|
|
11
11
|
it 'takes a String and parses it into a Hash when created' do
|
12
12
|
cache_control = Rack::Cache::CacheControl.new('max-age=600, foo')
|
13
13
|
cache_control['max-age'].should.equal '600'
|
14
|
-
cache_control['foo'].should.be
|
14
|
+
cache_control['foo'].should.be.true
|
15
15
|
end
|
16
16
|
|
17
17
|
it 'takes a String with a single name=value pair' do
|
@@ -29,17 +29,17 @@ describe 'Rack::Cache::CacheControl' do
|
|
29
29
|
it 'takes a String with a single flag value' do
|
30
30
|
cache_control = Rack::Cache::CacheControl.new('no-cache')
|
31
31
|
cache_control.should.include 'no-cache'
|
32
|
-
cache_control['no-cache'].should.be
|
32
|
+
cache_control['no-cache'].should.be.true
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'takes a String with a bunch of all kinds of stuff' do
|
36
36
|
cache_control =
|
37
37
|
Rack::Cache::CacheControl.new('max-age=600,must-revalidate,min-fresh=3000,foo=bar,baz')
|
38
38
|
cache_control['max-age'].should.equal '600'
|
39
|
-
cache_control['must-revalidate'].should.be
|
39
|
+
cache_control['must-revalidate'].should.be.true
|
40
40
|
cache_control['min-fresh'].should.equal '3000'
|
41
41
|
cache_control['foo'].should.equal 'bar'
|
42
|
-
cache_control['baz'].should.be
|
42
|
+
cache_control['baz'].should.be.true
|
43
43
|
end
|
44
44
|
|
45
45
|
it 'strips leading and trailing spaces from header value' do
|
@@ -80,7 +80,7 @@ describe 'Rack::Cache::CacheControl' do
|
|
80
80
|
|
81
81
|
it 'responds to #max_age with nil when no max-age directive present' do
|
82
82
|
cache_control = Rack::Cache::CacheControl.new('public')
|
83
|
-
cache_control.max_age.should.be
|
83
|
+
cache_control.max_age.should.be.nil
|
84
84
|
end
|
85
85
|
|
86
86
|
it 'responds to #shared_max_age with an integer when s-maxage directive present' do
|
@@ -90,7 +90,7 @@ describe 'Rack::Cache::CacheControl' do
|
|
90
90
|
|
91
91
|
it 'responds to #shared_max_age with nil when no s-maxage directive present' do
|
92
92
|
cache_control = Rack::Cache::CacheControl.new('public')
|
93
|
-
cache_control.shared_max_age.should.be
|
93
|
+
cache_control.shared_max_age.should.be.nil
|
94
94
|
end
|
95
95
|
|
96
96
|
it 'responds to #public? truthfully when public directive present' do
|
data/test/context_test.rb
CHANGED
@@ -2,8 +2,8 @@ require "#{File.dirname(__FILE__)}/spec_setup"
|
|
2
2
|
require 'rack/cache/context'
|
3
3
|
|
4
4
|
describe 'Rack::Cache::Context' do
|
5
|
-
before
|
6
|
-
after
|
5
|
+
before { setup_cache_context }
|
6
|
+
after { teardown_cache_context }
|
7
7
|
|
8
8
|
it 'passes on non-GET/HEAD requests' do
|
9
9
|
respond_with 200
|
@@ -771,7 +771,7 @@ describe 'Rack::Cache::Context' do
|
|
771
771
|
end
|
772
772
|
|
773
773
|
describe 'with responses that include a Vary header' do
|
774
|
-
before
|
774
|
+
before do
|
775
775
|
count = 0
|
776
776
|
respond_with 200 do |req,res|
|
777
777
|
res['Vary'] = 'Accept User-Agent Foo'
|
data/test/entitystore_test.rb
CHANGED
@@ -8,8 +8,7 @@ class Object
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
shared 'A Rack::Cache::EntityStore Implementation' do
|
13
12
|
it 'responds to all required messages' do
|
14
13
|
%w[read open write exist?].each do |message|
|
15
14
|
@store.should.respond_to message
|
@@ -86,8 +85,8 @@ end
|
|
86
85
|
describe 'Rack::Cache::EntityStore' do
|
87
86
|
|
88
87
|
describe 'Heap' do
|
89
|
-
it_should_behave_like 'A Rack::Cache::EntityStore Implementation'
|
90
88
|
before { @store = Rack::Cache::EntityStore::Heap.new }
|
89
|
+
behaves_like 'A Rack::Cache::EntityStore Implementation'
|
91
90
|
it 'takes a Hash to ::new' do
|
92
91
|
@store = Rack::Cache::EntityStore::Heap.new('foo' => ['bar'])
|
93
92
|
@store.read('foo').should.equal 'bar'
|
@@ -98,7 +97,6 @@ describe 'Rack::Cache::EntityStore' do
|
|
98
97
|
end
|
99
98
|
|
100
99
|
describe 'Disk' do
|
101
|
-
it_should_behave_like 'A Rack::Cache::EntityStore Implementation'
|
102
100
|
before do
|
103
101
|
@temp_dir = create_temp_directory
|
104
102
|
@store = Rack::Cache::EntityStore::Disk.new(@temp_dir)
|
@@ -107,6 +105,8 @@ describe 'Rack::Cache::EntityStore' do
|
|
107
105
|
@store = nil
|
108
106
|
remove_entry_secure @temp_dir
|
109
107
|
end
|
108
|
+
behaves_like 'A Rack::Cache::EntityStore Implementation'
|
109
|
+
|
110
110
|
it 'takes a path to ::new and creates the directory' do
|
111
111
|
path = @temp_dir + '/foo'
|
112
112
|
@store = Rack::Cache::EntityStore::Disk.new(path)
|
@@ -177,20 +177,19 @@ describe 'Rack::Cache::EntityStore' do
|
|
177
177
|
|
178
178
|
need_memcached 'entity store tests' do
|
179
179
|
describe 'MemCached' do
|
180
|
-
it_should_behave_like 'A Rack::Cache::EntityStore Implementation'
|
181
180
|
before do
|
182
181
|
@store = Rack::Cache::EntityStore::MemCached.new($memcached)
|
183
182
|
end
|
184
183
|
after do
|
185
184
|
@store = nil
|
186
185
|
end
|
186
|
+
behaves_like 'A Rack::Cache::EntityStore Implementation'
|
187
187
|
end
|
188
188
|
end
|
189
189
|
|
190
190
|
|
191
191
|
need_dalli 'entity store tests' do
|
192
192
|
describe 'Dalli' do
|
193
|
-
it_should_behave_like 'A Rack::Cache::EntityStore Implementation'
|
194
193
|
before do
|
195
194
|
$dalli.flush_all
|
196
195
|
@store = Rack::Cache::EntityStore::Dalli.new($dalli)
|
@@ -198,26 +197,25 @@ describe 'Rack::Cache::EntityStore' do
|
|
198
197
|
after do
|
199
198
|
@store = nil
|
200
199
|
end
|
200
|
+
behaves_like 'A Rack::Cache::EntityStore Implementation'
|
201
201
|
end
|
202
202
|
end
|
203
|
-
|
203
|
+
|
204
204
|
need_java 'entity store testing' do
|
205
205
|
module Rack::Cache::AppEngine
|
206
206
|
module MC
|
207
207
|
class << (Service = {})
|
208
|
-
|
209
208
|
def contains(key); include?(key); end
|
210
209
|
def get(key); self[key]; end;
|
211
210
|
def put(key, value, ttl = nil)
|
212
211
|
self[key] = value
|
213
|
-
end
|
212
|
+
end
|
214
213
|
end
|
215
|
-
|
214
|
+
|
216
215
|
end
|
217
216
|
end
|
218
|
-
|
217
|
+
|
219
218
|
describe 'GAEStore' do
|
220
|
-
it_should_behave_like 'A Rack::Cache::EntityStore Implementation'
|
221
219
|
before do
|
222
220
|
puts Rack::Cache::AppEngine::MC::Service.inspect
|
223
221
|
@store = Rack::Cache::EntityStore::GAEStore.new
|
@@ -225,6 +223,7 @@ describe 'Rack::Cache::EntityStore' do
|
|
225
223
|
after do
|
226
224
|
@store = nil
|
227
225
|
end
|
226
|
+
behaves_like 'A Rack::Cache::EntityStore Implementation'
|
228
227
|
end
|
229
228
|
end
|
230
229
|
end
|
data/test/key_test.rb
CHANGED
@@ -2,6 +2,18 @@ require "#{File.dirname(__FILE__)}/spec_setup"
|
|
2
2
|
require 'rack/cache/key'
|
3
3
|
|
4
4
|
describe 'A Rack::Cache::Key' do
|
5
|
+
# Helper Methods =============================================================
|
6
|
+
|
7
|
+
def mock_request(*args)
|
8
|
+
uri, opts = args
|
9
|
+
env = Rack::MockRequest.env_for(uri, opts || {})
|
10
|
+
Rack::Cache::Request.new(env)
|
11
|
+
end
|
12
|
+
|
13
|
+
def new_key(request)
|
14
|
+
Rack::Cache::Key.call(request)
|
15
|
+
end
|
16
|
+
|
5
17
|
it "sorts params" do
|
6
18
|
request = mock_request('/test?z=last&a=first')
|
7
19
|
new_key(request).should.include('a=first&z=last')
|
@@ -35,16 +47,4 @@ describe 'A Rack::Cache::Key' do
|
|
35
47
|
request = mock_request('/test?x=y', "HTTP_HOST" => 'www2.example.org')
|
36
48
|
new_key(request).should.equal "http://www2.example.org/test?x=y"
|
37
49
|
end
|
38
|
-
|
39
|
-
# Helper Methods =============================================================
|
40
|
-
|
41
|
-
define_method :mock_request do |*args|
|
42
|
-
uri, opts = args
|
43
|
-
env = Rack::MockRequest.env_for(uri, opts || {})
|
44
|
-
Rack::Cache::Request.new(env)
|
45
|
-
end
|
46
|
-
|
47
|
-
define_method :new_key do |request|
|
48
|
-
Rack::Cache::Key.call(request)
|
49
|
-
end
|
50
50
|
end
|
data/test/metastore_test.rb
CHANGED
@@ -1,11 +1,44 @@
|
|
1
1
|
require "#{File.dirname(__FILE__)}/spec_setup"
|
2
2
|
require 'rack/cache/metastore'
|
3
|
+
require 'rack/cache/entitystore'
|
4
|
+
|
5
|
+
shared 'A Rack::Cache::MetaStore Implementation' do
|
6
|
+
|
7
|
+
###
|
8
|
+
# Helpers
|
9
|
+
|
10
|
+
def mock_request(uri, opts)
|
11
|
+
env = Rack::MockRequest.env_for(uri, opts || {})
|
12
|
+
Rack::Cache::Request.new(env)
|
13
|
+
end
|
14
|
+
|
15
|
+
def mock_response(status, headers, body)
|
16
|
+
headers ||= {}
|
17
|
+
body = Array(body).compact
|
18
|
+
Rack::Cache::Response.new(status, headers, body)
|
19
|
+
end
|
20
|
+
|
21
|
+
def slurp(body)
|
22
|
+
buf = ''
|
23
|
+
body.each { |part| buf << part }
|
24
|
+
buf
|
25
|
+
end
|
26
|
+
|
27
|
+
# Stores an entry for the given request args, returns a url encoded cache key
|
28
|
+
# for the request.
|
29
|
+
def store_simple_entry(*request_args)
|
30
|
+
path, headers = request_args
|
31
|
+
@request = mock_request(path || '/test', headers || {})
|
32
|
+
@response = mock_response(200, {'Cache-Control' => 'max-age=420'}, ['test'])
|
33
|
+
body = @response.body
|
34
|
+
cache_key = @store.store(@request, @response, @entity_store)
|
35
|
+
@response.body.should.not.be.same_as body
|
36
|
+
cache_key
|
37
|
+
end
|
3
38
|
|
4
|
-
describe_shared 'A Rack::Cache::MetaStore Implementation' do
|
5
39
|
before do
|
6
40
|
@request = mock_request('/', {})
|
7
41
|
@response = mock_response(200, {}, ['hello world'])
|
8
|
-
@entity_store = nil
|
9
42
|
end
|
10
43
|
after do
|
11
44
|
@store = nil
|
@@ -43,7 +76,7 @@ describe_shared 'A Rack::Cache::MetaStore Implementation' do
|
|
43
76
|
|
44
77
|
it 'returns nil from #purge' do
|
45
78
|
@store.write('/test', [[{},{}]])
|
46
|
-
@store.purge('/test').should.be
|
79
|
+
@store.purge('/test').should.be.nil
|
47
80
|
@store.read('/test').should.equal []
|
48
81
|
end
|
49
82
|
|
@@ -75,20 +108,12 @@ describe_shared 'A Rack::Cache::MetaStore Implementation' do
|
|
75
108
|
@store.cache_key(request).should == 'tset/'
|
76
109
|
end
|
77
110
|
|
78
|
-
|
79
|
-
|
80
|
-
# Stores an entry for the given request args, returns a url encoded cache key
|
81
|
-
# for the request.
|
82
|
-
define_method :store_simple_entry do |*request_args|
|
83
|
-
path, headers = request_args
|
84
|
-
@request = mock_request(path || '/test', headers || {})
|
85
|
-
@response = mock_response(200, {'Cache-Control' => 'max-age=420'}, ['test'])
|
86
|
-
body = @response.body
|
87
|
-
cache_key = @store.store(@request, @response, @entity_store)
|
88
|
-
@response.body.should.not.be body
|
89
|
-
cache_key
|
111
|
+
it 'does not blow up when given a non-marhsalable object with an ALL_CAPS key' do
|
112
|
+
store_simple_entry('/bad', { 'SOME_THING' => Proc.new {} })
|
90
113
|
end
|
91
114
|
|
115
|
+
# Abstract methods ===========================================================
|
116
|
+
|
92
117
|
it 'stores a cache entry' do
|
93
118
|
cache_key = store_simple_entry
|
94
119
|
@store.read(cache_key).should.not.be.empty
|
@@ -206,39 +231,19 @@ describe_shared 'A Rack::Cache::MetaStore Implementation' do
|
|
206
231
|
|
207
232
|
@store.read(key).length.should.equal 2
|
208
233
|
end
|
209
|
-
|
210
|
-
# Helper Methods =============================================================
|
211
|
-
|
212
|
-
define_method :mock_request do |uri,opts|
|
213
|
-
env = Rack::MockRequest.env_for(uri, opts || {})
|
214
|
-
Rack::Cache::Request.new(env)
|
215
|
-
end
|
216
|
-
|
217
|
-
define_method :mock_response do |status,headers,body|
|
218
|
-
headers ||= {}
|
219
|
-
body = Array(body).compact
|
220
|
-
Rack::Cache::Response.new(status, headers, body)
|
221
|
-
end
|
222
|
-
|
223
|
-
define_method :slurp do |body|
|
224
|
-
buf = ''
|
225
|
-
body.each {|part| buf << part }
|
226
|
-
buf
|
227
|
-
end
|
228
234
|
end
|
229
235
|
|
230
236
|
|
231
237
|
describe 'Rack::Cache::MetaStore' do
|
232
238
|
describe 'Heap' do
|
233
|
-
it_should_behave_like 'A Rack::Cache::MetaStore Implementation'
|
234
239
|
before do
|
235
240
|
@store = Rack::Cache::MetaStore::Heap.new
|
236
241
|
@entity_store = Rack::Cache::EntityStore::Heap.new
|
237
242
|
end
|
243
|
+
behaves_like 'A Rack::Cache::MetaStore Implementation'
|
238
244
|
end
|
239
245
|
|
240
246
|
describe 'Disk' do
|
241
|
-
it_should_behave_like 'A Rack::Cache::MetaStore Implementation'
|
242
247
|
before do
|
243
248
|
@temp_dir = create_temp_directory
|
244
249
|
@store = Rack::Cache::MetaStore::Disk.new("#{@temp_dir}/meta")
|
@@ -247,29 +252,30 @@ describe 'Rack::Cache::MetaStore' do
|
|
247
252
|
after do
|
248
253
|
remove_entry_secure @temp_dir
|
249
254
|
end
|
255
|
+
behaves_like 'A Rack::Cache::MetaStore Implementation'
|
250
256
|
end
|
251
257
|
|
252
258
|
need_memcached 'metastore tests' do
|
253
259
|
describe 'MemCached' do
|
254
|
-
|
255
|
-
before :each do
|
260
|
+
before do
|
256
261
|
@temp_dir = create_temp_directory
|
257
262
|
$memcached.flush
|
258
263
|
@store = Rack::Cache::MetaStore::MemCached.new($memcached)
|
259
264
|
@entity_store = Rack::Cache::EntityStore::Heap.new
|
260
265
|
end
|
266
|
+
behaves_like 'A Rack::Cache::MetaStore Implementation'
|
261
267
|
end
|
262
268
|
end
|
263
269
|
|
264
270
|
need_dalli 'metastore tests' do
|
265
271
|
describe 'Dalli' do
|
266
|
-
|
267
|
-
before :each do
|
272
|
+
before do
|
268
273
|
@temp_dir = create_temp_directory
|
269
274
|
$dalli.flush_all
|
270
275
|
@store = Rack::Cache::MetaStore::Dalli.new($dalli)
|
271
276
|
@entity_store = Rack::Cache::EntityStore::Heap.new
|
272
277
|
end
|
278
|
+
behaves_like 'A Rack::Cache::MetaStore Implementation'
|
273
279
|
end
|
274
280
|
end
|
275
281
|
|
@@ -289,12 +295,12 @@ describe 'Rack::Cache::MetaStore' do
|
|
289
295
|
end
|
290
296
|
|
291
297
|
describe 'GAEStore' do
|
292
|
-
it_should_behave_like 'A Rack::Cache::MetaStore Implementation'
|
293
298
|
before :each do
|
294
299
|
Rack::Cache::AppEngine::MC::Service.clear
|
295
300
|
@store = Rack::Cache::MetaStore::GAEStore.new
|
296
301
|
@entity_store = Rack::Cache::EntityStore::Heap.new
|
297
302
|
end
|
303
|
+
behaves_like 'A Rack::Cache::MetaStore Implementation'
|
298
304
|
end
|
299
305
|
|
300
306
|
end
|
data/test/options_test.rb
CHANGED
@@ -59,13 +59,13 @@ describe 'Rack::Cache::Options' do
|
|
59
59
|
it 'allows the meta store to be configured' do
|
60
60
|
@options.should.respond_to :metastore
|
61
61
|
@options.should.respond_to :metastore=
|
62
|
-
@options.metastore.should.not.be
|
62
|
+
@options.metastore.should.not.be.nil
|
63
63
|
end
|
64
64
|
|
65
65
|
it 'allows the entity store to be configured' do
|
66
66
|
@options.should.respond_to :entitystore
|
67
67
|
@options.should.respond_to :entitystore=
|
68
|
-
@options.entitystore.should.not.be
|
68
|
+
@options.entitystore.should.not.be.nil
|
69
69
|
end
|
70
70
|
|
71
71
|
it 'allows log verbosity to be configured' do
|
data/test/request_test.rb
CHANGED
@@ -4,16 +4,16 @@ require 'rack/cache/request'
|
|
4
4
|
describe 'Rack::Cache::Request' do
|
5
5
|
it 'is marked as no_cache when the Cache-Control header includes the no-cache directive' do
|
6
6
|
request = Rack::Cache::Request.new('HTTP_CACHE_CONTROL' => 'public, no-cache')
|
7
|
-
|
7
|
+
request.should.be.no_cache
|
8
8
|
end
|
9
9
|
|
10
10
|
it 'is marked as no_cache when request should not be loaded from cache' do
|
11
11
|
request = Rack::Cache::Request.new('HTTP_PRAGMA' => 'no-cache')
|
12
|
-
|
12
|
+
request.should.be.no_cache
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'is not marked as no_cache when neither no-cache directive is specified' do
|
16
16
|
request = Rack::Cache::Request.new('HTTP_CACHE_CONTROL' => 'public')
|
17
|
-
|
17
|
+
request.should.not.be.no_cache
|
18
18
|
end
|
19
19
|
end
|
data/test/response_test.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
require "#{File.dirname(__FILE__)}/spec_setup"
|
2
2
|
|
3
3
|
describe 'Rack::Cache::Response' do
|
4
|
-
before
|
4
|
+
before do
|
5
5
|
@now = Time.httpdate(Time.now.httpdate)
|
6
6
|
@one_hour_ago = Time.httpdate((Time.now - (60**2)).httpdate)
|
7
7
|
@one_hour_later = Time.httpdate((Time.now + (60**2)).httpdate)
|
8
8
|
@res = Rack::Cache::Response.new(200, {'Date' => @now.httpdate}, [])
|
9
9
|
end
|
10
10
|
|
11
|
-
after
|
11
|
+
after do
|
12
12
|
@now, @res, @one_hour_ago = nil
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'marks Rack tuples with string typed statuses as cacheable' do
|
16
16
|
@res = Rack::Cache::Response.new('200',{'Date' => @now.httpdate},[])
|
17
17
|
@res.headers['Expires'] = @one_hour_later.httpdate
|
18
|
-
@res.
|
18
|
+
@res.should.be.cacheable
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'responds to #to_a with a Rack response tuple' do
|
@@ -60,7 +60,7 @@ describe 'Rack::Cache::Response' do
|
|
60
60
|
end
|
61
61
|
it 'uses the current time when no Date header present' do
|
62
62
|
@res = Rack::Cache::Response.new(200, {}, [])
|
63
|
-
@res.date.should.be.close Time.now, 1
|
63
|
+
@res.date.to_i.should.be.close Time.now.to_i, 1
|
64
64
|
end
|
65
65
|
it 'returns the correct date when the header is modified directly' do
|
66
66
|
@res = Rack::Cache::Response.new(200, { 'Date' => @one_hour_ago.httpdate }, [])
|
data/test/spec_setup.rb
CHANGED
@@ -2,13 +2,13 @@ require 'pp'
|
|
2
2
|
require 'tmpdir'
|
3
3
|
require 'stringio'
|
4
4
|
|
5
|
-
[
|
5
|
+
[STDOUT, STDERR].each { |io| io.sync = true }
|
6
6
|
|
7
7
|
begin
|
8
|
-
require '
|
8
|
+
require 'bacon'
|
9
9
|
rescue LoadError => boom
|
10
10
|
require 'rubygems' rescue nil
|
11
|
-
require '
|
11
|
+
require 'bacon'
|
12
12
|
end
|
13
13
|
|
14
14
|
# Set the MEMCACHED environment variable as follows to enable testing
|
@@ -19,17 +19,24 @@ $dalli = nil
|
|
19
19
|
|
20
20
|
def have_memcached?(server=ENV['MEMCACHED'])
|
21
21
|
return $memcached unless $memcached.nil?
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
|
23
|
+
# silence warnings from memcached
|
24
|
+
begin
|
25
|
+
v, $VERBOSE = $VERBOSE, nil
|
26
|
+
require 'memcached'
|
27
|
+
ensure
|
28
|
+
$VERBOSE = v
|
29
|
+
end
|
30
|
+
|
25
31
|
$memcached = Memcached.new(server)
|
26
32
|
$memcached.set('ping', '')
|
27
33
|
true
|
28
34
|
rescue LoadError => boom
|
35
|
+
warn "memcached library not available. related tests will be skipped."
|
29
36
|
$memcached = false
|
30
37
|
false
|
31
38
|
rescue => boom
|
32
|
-
|
39
|
+
warn "memcached not working. related tests will be skipped."
|
33
40
|
$memcached = false
|
34
41
|
false
|
35
42
|
end
|
@@ -43,10 +50,11 @@ def have_dalli?(server=ENV['MEMCACHED'])
|
|
43
50
|
$dalli.set('ping', '')
|
44
51
|
true
|
45
52
|
rescue LoadError => boom
|
53
|
+
warn "dalli library not available. related tests will be skipped."
|
46
54
|
$dalli = false
|
47
55
|
false
|
48
56
|
rescue => boom
|
49
|
-
|
57
|
+
warn "dalli not working. related tests will be skipped."
|
50
58
|
$dalli = false
|
51
59
|
false
|
52
60
|
end
|
@@ -54,27 +62,15 @@ end
|
|
54
62
|
have_dalli?
|
55
63
|
|
56
64
|
def need_dalli(forwhat)
|
57
|
-
if have_dalli?
|
58
|
-
yield
|
59
|
-
else
|
60
|
-
STDERR.puts "skipping Dalli #{forwhat}"
|
61
|
-
end
|
65
|
+
yield if have_dalli?
|
62
66
|
end
|
63
67
|
|
64
68
|
def need_memcached(forwhat)
|
65
|
-
if have_memcached?
|
66
|
-
yield
|
67
|
-
else
|
68
|
-
STDERR.puts "skipping memcached #{forwhat}"
|
69
|
-
end
|
69
|
+
yield if have_memcached?
|
70
70
|
end
|
71
71
|
|
72
72
|
def need_java(forwhat)
|
73
|
-
if RUBY_PLATFORM =~ /java/
|
74
|
-
yield
|
75
|
-
else
|
76
|
-
STDERR.puts "skipping app engine #{forwhat}"
|
77
|
-
end
|
73
|
+
yield if RUBY_PLATFORM =~ /java/
|
78
74
|
end
|
79
75
|
|
80
76
|
|
@@ -84,7 +80,6 @@ $LOAD_PATH.unshift File.dirname(__FILE__)
|
|
84
80
|
|
85
81
|
require 'rack/cache'
|
86
82
|
|
87
|
-
|
88
83
|
# Methods for constructing downstream applications / response
|
89
84
|
# generators.
|
90
85
|
module CacheContextHelpers
|
@@ -128,18 +123,18 @@ module CacheContextHelpers
|
|
128
123
|
|
129
124
|
def teardown_cache_context
|
130
125
|
@app, @cache_template, @cache, @caches, @called,
|
131
|
-
@request, @response, @responses, @cache_config = nil
|
126
|
+
@request, @response, @responses, @cache_config, @cache_prototype = nil
|
132
127
|
end
|
133
128
|
|
134
129
|
# A basic response with 200 status code and a tiny body.
|
135
|
-
def respond_with(status=200, headers={}, body=['Hello World'])
|
130
|
+
def respond_with(status=200, headers={}, body=['Hello World'], &bk)
|
136
131
|
called = false
|
137
132
|
@app =
|
138
133
|
lambda do |env|
|
139
134
|
called = true
|
140
135
|
response = Rack::Response.new(body, status, headers)
|
141
136
|
request = Rack::Request.new(env)
|
142
|
-
|
137
|
+
bk.call(request, response) if bk
|
143
138
|
response.finish
|
144
139
|
end
|
145
140
|
@app.meta_def(:called?) { called }
|
@@ -210,7 +205,7 @@ module TestHelpers
|
|
210
205
|
|
211
206
|
end
|
212
207
|
|
213
|
-
class
|
208
|
+
class Bacon::Context
|
214
209
|
include TestHelpers
|
215
210
|
include CacheContextHelpers
|
216
211
|
end
|
data/test/storage_test.rb
CHANGED
@@ -15,7 +15,7 @@ describe 'Rack::Cache::Storage' do
|
|
15
15
|
end
|
16
16
|
it "returns an existing MetaStore instance for URI that exists" do
|
17
17
|
store = @storage.resolve_metastore_uri('heap:/')
|
18
|
-
@storage.resolve_metastore_uri('heap:/').should.be store
|
18
|
+
@storage.resolve_metastore_uri('heap:/').should.be.same_as store
|
19
19
|
end
|
20
20
|
it "creates a new EntityStore for URI if none exists" do
|
21
21
|
@storage.resolve_entitystore_uri('heap:/').
|
@@ -23,14 +23,14 @@ describe 'Rack::Cache::Storage' do
|
|
23
23
|
end
|
24
24
|
it "returns an existing EntityStore instance for URI that exists" do
|
25
25
|
store = @storage.resolve_entitystore_uri('heap:/')
|
26
|
-
@storage.resolve_entitystore_uri('heap:/').should.be store
|
26
|
+
@storage.resolve_entitystore_uri('heap:/').should.be.same_as store
|
27
27
|
end
|
28
28
|
it "clears all URI -> store mappings with #clear" do
|
29
29
|
meta = @storage.resolve_metastore_uri('heap:/')
|
30
30
|
entity = @storage.resolve_entitystore_uri('heap:/')
|
31
31
|
@storage.clear
|
32
|
-
@storage.resolve_metastore_uri('heap:/').should.not.be meta
|
33
|
-
@storage.resolve_entitystore_uri('heap:/').should.not.be entity
|
32
|
+
@storage.resolve_metastore_uri('heap:/').should.not.be.same_as meta
|
33
|
+
@storage.resolve_entitystore_uri('heap:/').should.not.be.same_as entity
|
34
34
|
end
|
35
35
|
|
36
36
|
describe 'Heap Store URIs' do
|
metadata
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 0
|
9
|
-
version: "1.0"
|
4
|
+
prerelease:
|
5
|
+
version: 1.0.1
|
10
6
|
platform: ruby
|
11
7
|
authors:
|
12
8
|
- Ryan Tomayko
|
@@ -14,7 +10,7 @@ autorequire:
|
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
12
|
|
17
|
-
date:
|
13
|
+
date: 2011-04-13 00:00:00 +01:00
|
18
14
|
default_executable:
|
19
15
|
dependencies:
|
20
16
|
- !ruby/object:Gem::Dependency
|
@@ -25,24 +21,17 @@ dependencies:
|
|
25
21
|
requirements:
|
26
22
|
- - ">="
|
27
23
|
- !ruby/object:Gem::Version
|
28
|
-
hash: 3
|
29
|
-
segments:
|
30
|
-
- 0
|
31
|
-
- 4
|
32
24
|
version: "0.4"
|
33
25
|
type: :runtime
|
34
26
|
version_requirements: *id001
|
35
27
|
- !ruby/object:Gem::Dependency
|
36
|
-
name:
|
28
|
+
name: bacon
|
37
29
|
prerelease: false
|
38
30
|
requirement: &id002 !ruby/object:Gem::Requirement
|
39
31
|
none: false
|
40
32
|
requirements:
|
41
33
|
- - ">="
|
42
34
|
- !ruby/object:Gem::Version
|
43
|
-
hash: 3
|
44
|
-
segments:
|
45
|
-
- 0
|
46
35
|
version: "0"
|
47
36
|
type: :development
|
48
37
|
version_requirements: *id002
|
@@ -54,9 +43,6 @@ dependencies:
|
|
54
43
|
requirements:
|
55
44
|
- - ">="
|
56
45
|
- !ruby/object:Gem::Version
|
57
|
-
hash: 3
|
58
|
-
segments:
|
59
|
-
- 0
|
60
46
|
version: "0"
|
61
47
|
type: :development
|
62
48
|
version_requirements: *id003
|
@@ -68,9 +54,6 @@ dependencies:
|
|
68
54
|
requirements:
|
69
55
|
- - ">="
|
70
56
|
- !ruby/object:Gem::Version
|
71
|
-
hash: 3
|
72
|
-
segments:
|
73
|
-
- 0
|
74
57
|
version: "0"
|
75
58
|
type: :development
|
76
59
|
version_requirements: *id004
|
@@ -101,6 +84,7 @@ files:
|
|
101
84
|
- doc/storage.markdown
|
102
85
|
- example/sinatra/app.rb
|
103
86
|
- example/sinatra/views/index.erb
|
87
|
+
- lib/rack-cache.rb
|
104
88
|
- lib/rack/cache.rb
|
105
89
|
- lib/rack/cache/appengine.rb
|
106
90
|
- lib/rack/cache/cachecontrol.rb
|
@@ -144,23 +128,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
144
128
|
requirements:
|
145
129
|
- - ">="
|
146
130
|
- !ruby/object:Gem::Version
|
147
|
-
hash: 3
|
148
|
-
segments:
|
149
|
-
- 0
|
150
131
|
version: "0"
|
151
132
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
152
133
|
none: false
|
153
134
|
requirements:
|
154
135
|
- - ">="
|
155
136
|
- !ruby/object:Gem::Version
|
156
|
-
hash: 3
|
157
|
-
segments:
|
158
|
-
- 0
|
159
137
|
version: "0"
|
160
138
|
requirements: []
|
161
139
|
|
162
140
|
rubyforge_project:
|
163
|
-
rubygems_version: 1.
|
141
|
+
rubygems_version: 1.6.0
|
164
142
|
signing_key:
|
165
143
|
specification_version: 2
|
166
144
|
summary: HTTP Caching for Rack
|