rtomayko-rack-cache 0.2.0 → 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.
- data/CHANGES +45 -10
- data/README +14 -9
- data/Rakefile +10 -4
- data/TODO +11 -21
- data/doc/configuration.markdown +8 -0
- data/doc/index.markdown +17 -10
- data/doc/layout.html.erb +1 -0
- data/doc/server.ru +34 -0
- data/lib/rack/cache/config/default.rb +1 -2
- data/lib/rack/cache/core.rb +30 -2
- data/lib/rack/cache/entitystore.rb +29 -6
- data/lib/rack/cache/headers.rb +68 -20
- data/lib/rack/cache/metastore.rb +11 -11
- data/lib/rack/cache/options.rb +11 -1
- data/lib/rack/cache/response.rb +2 -2
- data/rack-cache.gemspec +3 -2
- data/test/cache_test.rb +3 -3
- data/test/context_test.rb +205 -65
- data/test/core_test.rb +8 -8
- data/test/entitystore_test.rb +23 -11
- data/test/environment_headers_test.rb +10 -12
- data/test/headers_test.rb +99 -23
- data/test/metastore_test.rb +27 -17
- data/test/options_test.rb +13 -10
- data/test/spec_setup.rb +13 -7
- metadata +3 -2
data/test/core_test.rb
CHANGED
@@ -24,15 +24,15 @@ describe 'Rack::Cache::Core' do
|
|
24
24
|
it 'executes multiple handlers in LIFO order' do
|
25
25
|
x = 'nothing executed'
|
26
26
|
@core.on :foo do
|
27
|
-
x.should.
|
27
|
+
x.should.equal 'bottom executed'
|
28
28
|
x = 'top executed'
|
29
29
|
end
|
30
30
|
@core.on :foo do
|
31
|
-
x.should.
|
31
|
+
x.should.equal 'nothing executed'
|
32
32
|
x = 'bottom executed'
|
33
33
|
end
|
34
34
|
@core.trigger :foo
|
35
|
-
x.should.
|
35
|
+
x.should.equal 'top executed'
|
36
36
|
end
|
37
37
|
it 'records event execution history' do
|
38
38
|
@core.on(:foo) {}
|
@@ -59,8 +59,8 @@ describe 'Rack::Cache::Core' do
|
|
59
59
|
@core.on(:bar) {}
|
60
60
|
@core.on(:foo) { throw(:transition, [:bar, 1, 2, 3]) }
|
61
61
|
result = @core.transition(from=:foo, to=[:bar])
|
62
|
-
passed.should.
|
63
|
-
result.should.
|
62
|
+
passed.should.equal [1,2,3]
|
63
|
+
result.should.equal 'hi'
|
64
64
|
end
|
65
65
|
it 'fully transitions out of handlers when the next event is invoked' do
|
66
66
|
x = []
|
@@ -70,15 +70,15 @@ describe 'Rack::Cache::Core' do
|
|
70
70
|
x << 'in foo, after transitioning to bar'
|
71
71
|
}
|
72
72
|
@core.on(:bar) { x << 'in bar' }
|
73
|
-
@core.trigger(:foo).should.
|
73
|
+
@core.trigger(:foo).should.equal [:bar]
|
74
74
|
@core.trigger(:bar).should.be.nil
|
75
|
-
x.should.
|
75
|
+
x.should.equal [
|
76
76
|
'in foo, before transitioning to bar',
|
77
77
|
'in bar'
|
78
78
|
]
|
79
79
|
end
|
80
80
|
it 'returns the transition event name' do
|
81
81
|
@core.on(:foo) { throw(:transition, [:bar]) }
|
82
|
-
@core.trigger(:foo).should.
|
82
|
+
@core.trigger(:foo).should.equal [:bar]
|
83
83
|
end
|
84
84
|
end
|
data/test/entitystore_test.rb
CHANGED
@@ -21,7 +21,7 @@ describe_shared 'A Rack::Cache::EntityStore Implementation' do
|
|
21
21
|
key.should.be.sha_like
|
22
22
|
|
23
23
|
data = @store.read(key)
|
24
|
-
data.should.
|
24
|
+
data.should.equal 'My wild love went riding,'
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'correctly determines whether cached body exists for key with #exist?' do
|
@@ -33,20 +33,20 @@ describe_shared 'A Rack::Cache::EntityStore Implementation' do
|
|
33
33
|
it 'can read data written with #write' do
|
34
34
|
key, size = @store.write('And asked him to pay.')
|
35
35
|
data = @store.read(key)
|
36
|
-
data.should.
|
36
|
+
data.should.equal 'And asked him to pay.'
|
37
37
|
end
|
38
38
|
|
39
39
|
it 'gives a 40 character SHA1 hex digest from #write' do
|
40
40
|
key, size = @store.write('she rode to the sea;')
|
41
41
|
key.should.not.be.nil
|
42
|
-
key.length.should.
|
42
|
+
key.length.should.equal 40
|
43
43
|
key.should.be =~ /^[0-9a-z]+$/
|
44
|
-
key.should.
|
44
|
+
key.should.equal '90a4c84d51a277f3dafc34693ca264531b9f51b6'
|
45
45
|
end
|
46
46
|
|
47
47
|
it 'returns the entire body as a String from #read' do
|
48
48
|
key, size = @store.write('She gathered together')
|
49
|
-
@store.read(key).should.
|
49
|
+
@store.read(key).should.equal 'She gathered together'
|
50
50
|
end
|
51
51
|
|
52
52
|
it 'returns nil from #read when key does not exist' do
|
@@ -59,7 +59,7 @@ describe_shared 'A Rack::Cache::EntityStore Implementation' do
|
|
59
59
|
body.should.respond_to :each
|
60
60
|
buf = ''
|
61
61
|
body.each { |part| buf << part }
|
62
|
-
buf.should.
|
62
|
+
buf.should.equal 'Some shells for her hair.'
|
63
63
|
end
|
64
64
|
|
65
65
|
it 'returns nil from #open when key does not exist' do
|
@@ -69,12 +69,17 @@ describe_shared 'A Rack::Cache::EntityStore Implementation' do
|
|
69
69
|
it 'can store largish bodies with binary data' do
|
70
70
|
pony = File.read(File.dirname(__FILE__) + '/pony.jpg')
|
71
71
|
key, size = @store.write(pony)
|
72
|
-
key.should.
|
72
|
+
key.should.equal 'd0f30d8659b4d268c5c64385d9790024c2d78deb'
|
73
73
|
data = @store.read(key)
|
74
|
-
data.length.should.
|
75
|
-
data.hash.should.
|
74
|
+
data.length.should.equal pony.length
|
75
|
+
data.hash.should.equal pony.hash
|
76
76
|
end
|
77
77
|
|
78
|
+
it 'deletes stored entries with #purge' do
|
79
|
+
key, size = @store.write('My wild love went riding,')
|
80
|
+
@store.purge(key).should.be.nil
|
81
|
+
@store.read(key).should.be.nil
|
82
|
+
end
|
78
83
|
end
|
79
84
|
|
80
85
|
describe 'Rack::Cache::EntityStore' do
|
@@ -84,7 +89,7 @@ describe 'Rack::Cache::EntityStore' do
|
|
84
89
|
before { @store = Rack::Cache::EntityStore::Heap.new }
|
85
90
|
it 'takes a Hash to ::new' do
|
86
91
|
@store = Rack::Cache::EntityStore::Heap.new('foo' => ['bar'])
|
87
|
-
@store.read('foo').should.
|
92
|
+
@store.read('foo').should.equal 'bar'
|
88
93
|
end
|
89
94
|
it 'uses its own Hash with no args to ::new' do
|
90
95
|
@store.read('foo').should.be.nil
|
@@ -106,6 +111,13 @@ describe 'Rack::Cache::EntityStore' do
|
|
106
111
|
@store = Rack::Cache::EntityStore::Disk.new(path)
|
107
112
|
File.should.be.a.directory path
|
108
113
|
end
|
114
|
+
it 'produces a body that responds to #to_path' do
|
115
|
+
key, size = @store.write('Some shells for her hair.')
|
116
|
+
body = @store.open(key)
|
117
|
+
body.should.respond_to :to_path
|
118
|
+
path = "#{@temp_dir}/#{key[0..1]}/#{key[2..-1]}"
|
119
|
+
body.to_path.should.equal path
|
120
|
+
end
|
109
121
|
it 'spreads data over a 36² hash radius' do
|
110
122
|
(<<-PROSE).each { |line| @store.write(line).first.should.be.sha_like }
|
111
123
|
My wild love went riding,
|
@@ -158,7 +170,7 @@ describe 'Rack::Cache::EntityStore' do
|
|
158
170
|
end
|
159
171
|
files.length.should.be > 0
|
160
172
|
end
|
161
|
-
subdirs.length.should.
|
173
|
+
subdirs.length.should.equal 28
|
162
174
|
end
|
163
175
|
end
|
164
176
|
|
@@ -21,27 +21,27 @@ describe 'Rack::Utils::EnvironmentHeaders' do
|
|
21
21
|
|
22
22
|
it 'retrieves headers with #[]' do
|
23
23
|
@h.should.respond_to :[]
|
24
|
-
@h['X-Foo'].should.
|
25
|
-
@h['If-Modified-Since'].should.
|
24
|
+
@h['X-Foo'].should.equal 'BAR'
|
25
|
+
@h['If-Modified-Since'].should.equal @now
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'sets headers with #[]=' do
|
29
29
|
@h.should.respond_to :[]=
|
30
30
|
@h['X-Foo'] = 'BAZZLE'
|
31
|
-
@h['X-Foo'].should.
|
31
|
+
@h['X-Foo'].should.equal 'BAZZLE'
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'sets values on the underlying environment hash' do
|
35
35
|
@h['X-Something-Else'] = 'FOO'
|
36
|
-
@env['HTTP_X_SOMETHING_ELSE'].should.
|
36
|
+
@env['HTTP_X_SOMETHING_ELSE'].should.equal 'FOO'
|
37
37
|
end
|
38
38
|
|
39
39
|
it 'handles Content-Type special case' do
|
40
|
-
@h['Content-Type'].should.
|
40
|
+
@h['Content-Type'].should.equal 'text/plain'
|
41
41
|
end
|
42
42
|
|
43
43
|
it 'handles Content-Length special case' do
|
44
|
-
@h['Content-Length'].should.
|
44
|
+
@h['Content-Length'].should.equal '0x1A4'
|
45
45
|
end
|
46
46
|
|
47
47
|
it 'implements #include? with RFC 2616 header name' do
|
@@ -60,12 +60,10 @@ describe 'Rack::Utils::EnvironmentHeaders' do
|
|
60
60
|
it 'iterates over all headers with #each' do
|
61
61
|
hash = {}
|
62
62
|
@h.each { |name,value| hash[name] = value }
|
63
|
-
hash.should.
|
64
|
-
'Content-
|
65
|
-
'
|
66
|
-
'
|
67
|
-
'If-Modified-Since' => @now
|
68
|
-
}
|
63
|
+
hash.should.equal 'Content-Type' => 'text/plain',
|
64
|
+
'Content-Length' => '0x1A4',
|
65
|
+
'X-Foo' => 'BAR',
|
66
|
+
'If-Modified-Since' => @now
|
69
67
|
end
|
70
68
|
|
71
69
|
end
|
data/test/headers_test.rb
CHANGED
@@ -19,13 +19,13 @@ describe 'Rack::Cache::Headers' do
|
|
19
19
|
describe '#cache_control' do
|
20
20
|
it 'handles single name=value pair' do
|
21
21
|
@res.headers['Cache-Control'] = 'max-age=600'
|
22
|
-
@res.cache_control['max-age'].should.
|
22
|
+
@res.cache_control['max-age'].should.equal '600'
|
23
23
|
end
|
24
24
|
it 'handles multiple name=value pairs' do
|
25
25
|
@res.headers['Cache-Control'] = 'max-age=600, max-stale=300, min-fresh=570'
|
26
|
-
@res.cache_control['max-age'].should.
|
27
|
-
@res.cache_control['max-stale'].should.
|
28
|
-
@res.cache_control['min-fresh'].should.
|
26
|
+
@res.cache_control['max-age'].should.equal '600'
|
27
|
+
@res.cache_control['max-stale'].should.equal '300'
|
28
|
+
@res.cache_control['min-fresh'].should.equal '570'
|
29
29
|
end
|
30
30
|
it 'handles a single flag value' do
|
31
31
|
@res.headers['Cache-Control'] = 'no-cache'
|
@@ -34,15 +34,15 @@ describe 'Rack::Cache::Headers' do
|
|
34
34
|
end
|
35
35
|
it 'handles a bunch of all kinds of stuff' do
|
36
36
|
@res.headers['Cache-Control'] = 'max-age=600,must-revalidate,min-fresh=3000,foo=bar,baz'
|
37
|
-
@res.cache_control['max-age'].should.
|
37
|
+
@res.cache_control['max-age'].should.equal '600'
|
38
38
|
@res.cache_control['must-revalidate'].should.be true
|
39
|
-
@res.cache_control['min-fresh'].should.
|
40
|
-
@res.cache_control['foo'].should.
|
39
|
+
@res.cache_control['min-fresh'].should.equal '3000'
|
40
|
+
@res.cache_control['foo'].should.equal 'bar'
|
41
41
|
@res.cache_control['baz'].should.be true
|
42
42
|
end
|
43
43
|
it 'removes the header when given an empty hash' do
|
44
44
|
@res.headers['Cache-Control'] = 'max-age=600, must-revalidate'
|
45
|
-
@res.cache_control['max-age'].should.
|
45
|
+
@res.cache_control['max-age'].should.equal '600'
|
46
46
|
@res.cache_control = {}
|
47
47
|
@res.headers.should.not.include 'Cache-Control'
|
48
48
|
end
|
@@ -82,7 +82,7 @@ describe 'Rack::Cache::ResponseHeaders' do
|
|
82
82
|
it 'uses the Date header if present' do
|
83
83
|
@res = MockResponse.new(200, { 'Date' => @one_hour_ago.httpdate }, '')
|
84
84
|
@res.extend Rack::Cache::ResponseHeaders
|
85
|
-
@res.date.should.
|
85
|
+
@res.date.should.equal @one_hour_ago
|
86
86
|
end
|
87
87
|
it 'uses the current time when no Date header present' do
|
88
88
|
@res = MockResponse.new(200, {}, '')
|
@@ -92,20 +92,20 @@ describe 'Rack::Cache::ResponseHeaders' do
|
|
92
92
|
it 'returns the correct date when the header is modified directly' do
|
93
93
|
@res = MockResponse.new(200, { 'Date' => @one_hour_ago.httpdate }, '')
|
94
94
|
@res.extend Rack::Cache::ResponseHeaders
|
95
|
-
@res.date.should.
|
95
|
+
@res.date.should.equal @one_hour_ago
|
96
96
|
@res.headers['Date'] = @now.httpdate
|
97
|
-
@res.date.should.
|
97
|
+
@res.date.should.equal @now
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
101
|
describe '#expires_at' do
|
102
102
|
it 'returns #date + #max_age when Cache-Control/max-age is present' do
|
103
103
|
@res.headers['Cache-Control'] = 'max-age=500'
|
104
|
-
@res.expires_at.should.
|
104
|
+
@res.expires_at.should.equal @res.date + 500
|
105
105
|
end
|
106
106
|
it 'uses the Expires header when present and no Cache-Control/max-age' do
|
107
107
|
@res.headers['Expires'] = @one_hour_ago.httpdate
|
108
|
-
@res.expires_at.should.
|
108
|
+
@res.expires_at.should.equal @one_hour_ago
|
109
109
|
end
|
110
110
|
it 'returns nil when no Expires or Cache-Control provided' do
|
111
111
|
@res.expires_at.should.be nil
|
@@ -113,14 +113,18 @@ describe 'Rack::Cache::ResponseHeaders' do
|
|
113
113
|
end
|
114
114
|
|
115
115
|
describe '#max_age' do
|
116
|
-
it 'uses
|
116
|
+
it 'uses s-maxage cache control directive when present' do
|
117
|
+
@res.headers['Cache-Control'] = 's-maxage=600, max-age=0'
|
118
|
+
@res.max_age.should.equal 600
|
119
|
+
end
|
120
|
+
it 'falls back to max-age when no s-maxage directive present' do
|
117
121
|
@res.headers['Cache-Control'] = 'max-age=600'
|
118
|
-
@res.max_age.should.
|
122
|
+
@res.max_age.should.equal 600
|
119
123
|
end
|
120
|
-
it '
|
124
|
+
it 'falls back to Expires when no max-age or s-maxage directive present' do
|
121
125
|
@res.headers['Cache-Control'] = 'must-revalidate'
|
122
126
|
@res.headers['Expires'] = @one_hour_later.httpdate
|
123
|
-
@res.max_age.should.
|
127
|
+
@res.max_age.should.equal 60 ** 2
|
124
128
|
end
|
125
129
|
it 'gives a #max_age of nil when no freshness information available' do
|
126
130
|
@res.max_age.should.be.nil
|
@@ -136,18 +140,90 @@ describe 'Rack::Cache::ResponseHeaders' do
|
|
136
140
|
@res.headers['Cache-Control'] = 'max-age=500'
|
137
141
|
@res.freshness_information?.should.be true
|
138
142
|
end
|
143
|
+
it 'is true when a Cache-Control s-maxage directive is present' do
|
144
|
+
@res.headers['Cache-Control'] = 's-maxage=500'
|
145
|
+
@res.freshness_information?.should.be true
|
146
|
+
end
|
139
147
|
it 'is not true otherwise' do
|
140
148
|
@res.freshness_information?.should.be false
|
141
149
|
end
|
142
150
|
end
|
143
151
|
|
152
|
+
describe '#public=' do
|
153
|
+
it 'adds the public Cache-Control directive when set true' do
|
154
|
+
@res.headers['Cache-Control'] = 'max-age=100'
|
155
|
+
@res.public = true
|
156
|
+
@res.headers['Cache-Control'].should.equal 'public, max-age=100'
|
157
|
+
end
|
158
|
+
it 'removes the private Cache-Control directive' do
|
159
|
+
@res.headers['Cache-Control'] = 'private, max-age=100'
|
160
|
+
@res.public = true
|
161
|
+
@res.headers['Cache-Control'].should.equal 'public, max-age=100'
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe '#public?' do
|
166
|
+
it 'is true when the public directive is present' do
|
167
|
+
@res.headers['Cache-Control'] = 'public'
|
168
|
+
@res.should.be.public
|
169
|
+
end
|
170
|
+
it 'is false when only the private directive is present' do
|
171
|
+
@res.headers['Cache-Control'] = 'private'
|
172
|
+
@res.should.not.be.public
|
173
|
+
end
|
174
|
+
it 'is false when no Cache-Control header is present' do
|
175
|
+
@res.should.not.be.public
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe '#private=' do
|
180
|
+
it 'adds the private Cache-Control directive when set true' do
|
181
|
+
@res.headers['Cache-Control'] = 'max-age=100'
|
182
|
+
@res.private = true
|
183
|
+
@res.headers['Cache-Control'].should.equal 'private, max-age=100'
|
184
|
+
end
|
185
|
+
it 'removes the public Cache-Control directive' do
|
186
|
+
@res.headers['Cache-Control'] = 'public, max-age=100'
|
187
|
+
@res.private = true
|
188
|
+
@res.headers['Cache-Control'].should.equal 'private, max-age=100'
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe '#private?' do
|
193
|
+
it 'is true when the private directive is present' do
|
194
|
+
@res.headers['Cache-Control'] = 'private'
|
195
|
+
@res.should.be.private
|
196
|
+
end
|
197
|
+
it 'is false when the private directive is not present' do
|
198
|
+
@res.headers['Cache-Control'] = 'public'
|
199
|
+
@res.should.not.be.private
|
200
|
+
end
|
201
|
+
it 'is false when no Cache-Control header is present' do
|
202
|
+
@res.should.not.be.private
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
144
206
|
describe '#no_cache?' do
|
145
207
|
it 'is true when a Cache-Control no-cache directive is present' do
|
146
208
|
@res.headers['Cache-Control'] = 'no-cache'
|
147
|
-
@res.no_cache
|
209
|
+
assert @res.no_cache?
|
210
|
+
end
|
211
|
+
it 'is false otherwise' do
|
212
|
+
assert !@res.no_cache?
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe '#must_revalidate?' do
|
217
|
+
it 'is true when a Cache-Control must-revalidate directive is present' do
|
218
|
+
@res.headers['Cache-Control'] = 'private, must-revalidate'
|
219
|
+
assert @res.must_revalidate?
|
220
|
+
end
|
221
|
+
it 'is true when a Cache-Control proxy-revalidate directive is present' do
|
222
|
+
@res.headers['Cache-Control'] = 'public, proxy-revalidate'
|
223
|
+
assert @res.must_revalidate?
|
148
224
|
end
|
149
225
|
it 'is false otherwise' do
|
150
|
-
|
226
|
+
assert !@res.must_revalidate?
|
151
227
|
end
|
152
228
|
end
|
153
229
|
|
@@ -190,7 +266,7 @@ describe 'Rack::Cache::ResponseHeaders' do
|
|
190
266
|
end
|
191
267
|
it 'returns the literal value of the Vary header' do
|
192
268
|
@res.headers['Vary'] = 'Foo Bar Baz'
|
193
|
-
@res.vary.should.
|
269
|
+
@res.vary.should.equal 'Foo Bar Baz'
|
194
270
|
end
|
195
271
|
it 'can be checked for existence using the #vary? method' do
|
196
272
|
@res.should.respond_to :vary?
|
@@ -206,16 +282,16 @@ describe 'Rack::Cache::ResponseHeaders' do
|
|
206
282
|
end
|
207
283
|
it 'parses a single header name value' do
|
208
284
|
@res.headers['Vary'] = 'Accept-Language'
|
209
|
-
@res.vary_header_names.should.
|
285
|
+
@res.vary_header_names.should.equal ['Accept-Language']
|
210
286
|
end
|
211
287
|
it 'parses multiple header name values separated by spaces' do
|
212
288
|
@res.headers['Vary'] = 'Accept-Language User-Agent X-Foo'
|
213
|
-
@res.vary_header_names.should.
|
289
|
+
@res.vary_header_names.should.equal \
|
214
290
|
['Accept-Language', 'User-Agent', 'X-Foo']
|
215
291
|
end
|
216
292
|
it 'parses multiple header name values separated by commas' do
|
217
293
|
@res.headers['Vary'] = 'Accept-Language,User-Agent, X-Foo'
|
218
|
-
@res.vary_header_names.should.
|
294
|
+
@res.vary_header_names.should.equal \
|
219
295
|
['Accept-Language', 'User-Agent', 'X-Foo']
|
220
296
|
end
|
221
297
|
end
|
data/test/metastore_test.rb
CHANGED
@@ -2,7 +2,6 @@ require "#{File.dirname(__FILE__)}/spec_setup"
|
|
2
2
|
require 'rack/cache/metastore'
|
3
3
|
|
4
4
|
describe_shared 'A Rack::Cache::MetaStore Implementation' do
|
5
|
-
|
6
5
|
before do
|
7
6
|
@request = mock_request('/', {})
|
8
7
|
@response = mock_response(200, {}, ['hello world'])
|
@@ -22,7 +21,7 @@ describe_shared 'A Rack::Cache::MetaStore Implementation' do
|
|
22
21
|
it 'reads a list of negotation tuples with #read' do
|
23
22
|
@store.write('/test', [[{},{}],[{},{}]])
|
24
23
|
tuples = @store.read('/test')
|
25
|
-
tuples.should.
|
24
|
+
tuples.should.equal [ [{},{}], [{},{}] ]
|
26
25
|
end
|
27
26
|
|
28
27
|
it 'reads an empty list with #read when nothing cached at key' do
|
@@ -45,20 +44,20 @@ describe_shared 'A Rack::Cache::MetaStore Implementation' do
|
|
45
44
|
it 'returns nil from #purge' do
|
46
45
|
@store.write('/test', [[{},{}]])
|
47
46
|
@store.purge('/test').should.be nil
|
48
|
-
@store.read('/test').should.
|
47
|
+
@store.read('/test').should.equal []
|
49
48
|
end
|
50
49
|
|
51
50
|
%w[/test http://example.com:8080/ /test?x=y /test?x=y&p=q].each do |key|
|
52
51
|
it "can read and write key: '#{key}'" do
|
53
52
|
lambda { @store.write(key, [[{},{}]]) }.should.not.raise
|
54
|
-
@store.read(key).should.
|
53
|
+
@store.read(key).should.equal [[{},{}]]
|
55
54
|
end
|
56
55
|
end
|
57
56
|
|
58
57
|
it "can read and write fairly large keys" do
|
59
58
|
key = "b" * 4096
|
60
59
|
lambda { @store.write(key, [[{},{}]]) }.should.not.raise
|
61
|
-
@store.read(key).should.
|
60
|
+
@store.read(key).should.equal [[{},{}]]
|
62
61
|
end
|
63
62
|
|
64
63
|
# Abstract methods ===========================================================
|
@@ -79,7 +78,7 @@ describe_shared 'A Rack::Cache::MetaStore Implementation' do
|
|
79
78
|
it 'sets the X-Content-Digest response header before storing' do
|
80
79
|
store_simple_entry
|
81
80
|
req, res = @store.read('/test').first
|
82
|
-
res['X-Content-Digest'].should.
|
81
|
+
res['X-Content-Digest'].should.equal 'a94a8fe5ccb19ba61c4c0873d391e987982fbbd3'
|
83
82
|
end
|
84
83
|
|
85
84
|
it 'finds a stored entry with #lookup' do
|
@@ -89,18 +88,30 @@ describe_shared 'A Rack::Cache::MetaStore Implementation' do
|
|
89
88
|
response.should.be.kind_of Rack::Cache::Response
|
90
89
|
end
|
91
90
|
|
91
|
+
it 'does not find an entry with #lookup when none exists' do
|
92
|
+
req = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
|
93
|
+
@store.lookup(req, @entity_store).should.be.nil
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'does not find an entry with #lookup when the body does not exist' do
|
97
|
+
store_simple_entry
|
98
|
+
@response['X-Content-Digest'].should.not.be.nil
|
99
|
+
@entity_store.purge(@response['X-Content-Digest'])
|
100
|
+
@store.lookup(@request, @entity_store).should.be.nil
|
101
|
+
end
|
102
|
+
|
92
103
|
it 'restores response headers properly with #lookup' do
|
93
104
|
store_simple_entry
|
94
105
|
response = @store.lookup(@request, @entity_store)
|
95
106
|
response.headers.
|
96
|
-
should.
|
107
|
+
should.equal @response.headers.merge('Age' => '0', 'Content-Length' => '4')
|
97
108
|
end
|
98
109
|
|
99
110
|
it 'restores response body from entity store with #lookup' do
|
100
111
|
store_simple_entry
|
101
112
|
response = @store.lookup(@request, @entity_store)
|
102
113
|
body = '' ; response.body.each {|p| body << p}
|
103
|
-
body.should.
|
114
|
+
body.should.equal 'test'
|
104
115
|
end
|
105
116
|
|
106
117
|
# Vary =======================================================================
|
@@ -127,30 +138,30 @@ describe_shared 'A Rack::Cache::MetaStore Implementation' do
|
|
127
138
|
res3 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 3'])
|
128
139
|
@store.store(req3, res3, @entity_store)
|
129
140
|
|
130
|
-
slurp(@store.lookup(req3, @entity_store).body).should.
|
131
|
-
slurp(@store.lookup(req1, @entity_store).body).should.
|
132
|
-
slurp(@store.lookup(req2, @entity_store).body).should.
|
141
|
+
slurp(@store.lookup(req3, @entity_store).body).should.equal 'test 3'
|
142
|
+
slurp(@store.lookup(req1, @entity_store).body).should.equal 'test 1'
|
143
|
+
slurp(@store.lookup(req2, @entity_store).body).should.equal 'test 2'
|
133
144
|
|
134
|
-
@store.read('/test').length.should.
|
145
|
+
@store.read('/test').length.should.equal 3
|
135
146
|
end
|
136
147
|
|
137
148
|
it 'overwrites non-varying responses with #store' do
|
138
149
|
req1 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
|
139
150
|
res1 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 1'])
|
140
151
|
@store.store(req1, res1, @entity_store)
|
141
|
-
slurp(@store.lookup(req1, @entity_store).body).should.
|
152
|
+
slurp(@store.lookup(req1, @entity_store).body).should.equal 'test 1'
|
142
153
|
|
143
154
|
req2 = mock_request('/test', {'HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam'})
|
144
155
|
res2 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 2'])
|
145
156
|
@store.store(req2, res2, @entity_store)
|
146
|
-
slurp(@store.lookup(req2, @entity_store).body).should.
|
157
|
+
slurp(@store.lookup(req2, @entity_store).body).should.equal 'test 2'
|
147
158
|
|
148
159
|
req3 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'})
|
149
160
|
res3 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 3'])
|
150
161
|
@store.store(req3, res3, @entity_store)
|
151
|
-
slurp(@store.lookup(req1, @entity_store).body).should.
|
162
|
+
slurp(@store.lookup(req1, @entity_store).body).should.equal 'test 3'
|
152
163
|
|
153
|
-
@store.read('/test').length.should.
|
164
|
+
@store.read('/test').length.should.equal 2
|
154
165
|
end
|
155
166
|
|
156
167
|
# Helper Methods =============================================================
|
@@ -171,7 +182,6 @@ describe_shared 'A Rack::Cache::MetaStore Implementation' do
|
|
171
182
|
body.each {|part| buf << part }
|
172
183
|
buf
|
173
184
|
end
|
174
|
-
|
175
185
|
end
|
176
186
|
|
177
187
|
|
data/test/options_test.rb
CHANGED
@@ -7,7 +7,10 @@ end
|
|
7
7
|
|
8
8
|
class MockOptions
|
9
9
|
include Rack::Cache::Options
|
10
|
-
|
10
|
+
def initialize
|
11
|
+
@env = nil
|
12
|
+
initialize_options
|
13
|
+
end
|
11
14
|
end
|
12
15
|
|
13
16
|
describe 'Rack::Cache::Options' do
|
@@ -16,30 +19,30 @@ describe 'Rack::Cache::Options' do
|
|
16
19
|
describe '#set' do
|
17
20
|
it 'sets a Symbol option as rack-cache.symbol' do
|
18
21
|
@options.set :bar, 'baz'
|
19
|
-
@options.options['rack-cache.bar'].should.
|
22
|
+
@options.options['rack-cache.bar'].should.equal 'baz'
|
20
23
|
end
|
21
24
|
it 'sets a String option as string' do
|
22
25
|
@options.set 'foo.bar', 'bling'
|
23
|
-
@options.options['foo.bar'].should.
|
26
|
+
@options.options['foo.bar'].should.equal 'bling'
|
24
27
|
end
|
25
28
|
it 'sets all key/value pairs when given a Hash' do
|
26
29
|
@options.set :foo => 'bar', :bar => 'baz', 'foo.bar' => 'bling'
|
27
|
-
@options.foo.should.
|
28
|
-
@options.options['rack-cache.bar'].should.
|
29
|
-
@options.options['foo.bar'].should.
|
30
|
+
@options.foo.should.equal 'bar'
|
31
|
+
@options.options['rack-cache.bar'].should.equal 'baz'
|
32
|
+
@options.options['foo.bar'].should.equal 'bling'
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
33
36
|
it 'makes options declared with option_accessor available as attributes' do
|
34
37
|
@options.set :foo, 'bar'
|
35
|
-
@options.foo.should.
|
38
|
+
@options.foo.should.equal 'bar'
|
36
39
|
end
|
37
40
|
|
38
41
|
it 'allows setting multiple options via assignment' do
|
39
42
|
@options.options = { :foo => 'bar', :bar => 'baz', 'foo.bar' => 'bling' }
|
40
|
-
@options.foo.should.
|
41
|
-
@options.options['foo.bar'].should.
|
42
|
-
@options.options['rack-cache.bar'].should.
|
43
|
+
@options.foo.should.equal 'bar'
|
44
|
+
@options.options['foo.bar'].should.equal 'bling'
|
45
|
+
@options.options['rack-cache.bar'].should.equal 'baz'
|
43
46
|
end
|
44
47
|
|
45
48
|
it 'allows the meta store to be configured' do
|
data/test/spec_setup.rb
CHANGED
@@ -16,19 +16,23 @@ ENV['MEMCACHED'] ||= 'localhost:11215'
|
|
16
16
|
$memcached = nil
|
17
17
|
|
18
18
|
def have_memcached?(server=ENV['MEMCACHED'])
|
19
|
-
return
|
19
|
+
return $memcached unless $memcached.nil?
|
20
|
+
v, $VERBOSE = $VERBOSE, nil # silence warnings from memcached
|
20
21
|
require 'memcached'
|
22
|
+
$VERBOSE = v
|
21
23
|
$memcached = Memcached.new(server)
|
22
24
|
$memcached.set('ping', '')
|
23
25
|
true
|
24
26
|
rescue LoadError => boom
|
25
|
-
$memcached =
|
27
|
+
$memcached = false
|
26
28
|
false
|
27
29
|
rescue => boom
|
28
|
-
$memcached =
|
30
|
+
$memcached = false
|
29
31
|
false
|
30
32
|
end
|
31
33
|
|
34
|
+
have_memcached?
|
35
|
+
|
32
36
|
def need_memcached(forwhat)
|
33
37
|
if have_memcached?
|
34
38
|
yield
|
@@ -125,7 +129,7 @@ module CacheContextHelpers
|
|
125
129
|
@caches << @cache
|
126
130
|
@request = Rack::MockRequest.new(@cache)
|
127
131
|
yield @cache if block_given?
|
128
|
-
@response = @request.
|
132
|
+
@response = @request.request(method.to_s.upcase, uri, opts)
|
129
133
|
@responses << @response
|
130
134
|
@response
|
131
135
|
end
|
@@ -134,10 +138,13 @@ module CacheContextHelpers
|
|
134
138
|
request(:get, stem, env, &b)
|
135
139
|
end
|
136
140
|
|
141
|
+
def head(stem, env={}, &b)
|
142
|
+
request(:head, stem, env, &b)
|
143
|
+
end
|
144
|
+
|
137
145
|
def post(*args, &b)
|
138
146
|
request(:post, *args, &b)
|
139
147
|
end
|
140
|
-
|
141
148
|
end
|
142
149
|
|
143
150
|
|
@@ -149,7 +156,7 @@ module TestHelpers
|
|
149
156
|
|
150
157
|
def create_temp_directory
|
151
158
|
@@temp_dir_count += 1
|
152
|
-
path = F.join(Dir.tmpdir, "
|
159
|
+
path = F.join(Dir.tmpdir, "rack-cache-#{$$}-#{@@temp_dir_count}")
|
153
160
|
mkdir_p path
|
154
161
|
if block_given?
|
155
162
|
yield path
|
@@ -186,4 +193,3 @@ class Object
|
|
186
193
|
class_eval { define_method name, &blk }
|
187
194
|
end
|
188
195
|
end
|
189
|
-
|