rack-cache 0.3.0 → 0.4

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.

Files changed (44) hide show
  1. data/CHANGES +43 -0
  2. data/README +18 -9
  3. data/Rakefile +1 -14
  4. data/TODO +13 -14
  5. data/doc/configuration.markdown +7 -153
  6. data/doc/faq.markdown +8 -0
  7. data/doc/index.markdown +7 -9
  8. data/example/sinatra/app.rb +25 -0
  9. data/example/sinatra/views/index.erb +44 -0
  10. data/lib/rack/cache.rb +5 -11
  11. data/lib/rack/cache/cachecontrol.rb +193 -0
  12. data/lib/rack/cache/context.rb +190 -52
  13. data/lib/rack/cache/entitystore.rb +10 -4
  14. data/lib/rack/cache/key.rb +52 -0
  15. data/lib/rack/cache/metastore.rb +52 -16
  16. data/lib/rack/cache/options.rb +60 -39
  17. data/lib/rack/cache/request.rb +11 -15
  18. data/lib/rack/cache/response.rb +221 -30
  19. data/lib/rack/cache/storage.rb +1 -2
  20. data/rack-cache.gemspec +9 -15
  21. data/test/cache_test.rb +9 -6
  22. data/test/cachecontrol_test.rb +139 -0
  23. data/test/context_test.rb +251 -169
  24. data/test/entitystore_test.rb +12 -11
  25. data/test/key_test.rb +50 -0
  26. data/test/metastore_test.rb +57 -14
  27. data/test/options_test.rb +11 -0
  28. data/test/request_test.rb +19 -0
  29. data/test/response_test.rb +164 -23
  30. data/test/spec_setup.rb +7 -0
  31. metadata +12 -20
  32. data/doc/events.dot +0 -27
  33. data/lib/rack/cache/config.rb +0 -65
  34. data/lib/rack/cache/config/busters.rb +0 -16
  35. data/lib/rack/cache/config/default.rb +0 -133
  36. data/lib/rack/cache/config/no-cache.rb +0 -13
  37. data/lib/rack/cache/core.rb +0 -299
  38. data/lib/rack/cache/headers.rb +0 -325
  39. data/lib/rack/utils/environment_headers.rb +0 -78
  40. data/test/config_test.rb +0 -66
  41. data/test/core_test.rb +0 -84
  42. data/test/environment_headers_test.rb +0 -69
  43. data/test/headers_test.rb +0 -298
  44. data/test/logging_test.rb +0 -45
data/test/core_test.rb DELETED
@@ -1,84 +0,0 @@
1
- require "#{File.dirname(__FILE__)}/spec_setup"
2
- require 'rack/cache/core'
3
-
4
- class MockCore
5
- include Rack::Cache::Core
6
- alias_method :initialize, :initialize_core
7
- public :transition, :trigger, :events
8
- end
9
-
10
- describe 'Rack::Cache::Core' do
11
- before :each do
12
- @core = MockCore.new
13
- end
14
-
15
- it 'has events after instantiation' do
16
- @core.events.should.respond_to :[]
17
- end
18
- it 'defines and triggers event handlers' do
19
- executed = false
20
- @core.on(:foo) { executed = true }
21
- @core.trigger :foo
22
- executed.should.be true
23
- end
24
- it 'executes multiple handlers in LIFO order' do
25
- x = 'nothing executed'
26
- @core.on :foo do
27
- x.should.equal 'bottom executed'
28
- x = 'top executed'
29
- end
30
- @core.on :foo do
31
- x.should.equal 'nothing executed'
32
- x = 'bottom executed'
33
- end
34
- @core.trigger :foo
35
- x.should.equal 'top executed'
36
- end
37
- it 'records event execution history' do
38
- @core.on(:foo) {}
39
- @core.trigger :foo
40
- @core.should.a.performed :foo
41
- end
42
- it 'raises an exception when asked to perform an unknown event' do
43
- assert_raises NameError do
44
- @core.trigger :foo
45
- end
46
- end
47
- it 'raises an exception when asked to transition to an unknown event' do
48
- @core.on(:bling) {}
49
- @core.on(:foo) { throw(:transition, [:bling]) }
50
- lambda { @core.transition(from=:foo, to=[:bar, :baz]) }.
51
- should.raise Rack::Cache::IllegalTransition
52
- end
53
- it 'passes transition arguments to handlers' do
54
- passed = nil
55
- @core.meta_def(:perform_bar) do |*args|
56
- passed = args
57
- 'hi'
58
- end
59
- @core.on(:bar) {}
60
- @core.on(:foo) { throw(:transition, [:bar, 1, 2, 3]) }
61
- result = @core.transition(from=:foo, to=[:bar])
62
- passed.should.equal [1,2,3]
63
- result.should.equal 'hi'
64
- end
65
- it 'fully transitions out of handlers when the next event is invoked' do
66
- x = []
67
- @core.on(:foo) {
68
- x << 'in foo, before transitioning to bar'
69
- throw(:transition, [:bar])
70
- x << 'in foo, after transitioning to bar'
71
- }
72
- @core.on(:bar) { x << 'in bar' }
73
- @core.trigger(:foo).should.equal [:bar]
74
- @core.trigger(:bar).should.be.nil
75
- x.should.equal [
76
- 'in foo, before transitioning to bar',
77
- 'in bar'
78
- ]
79
- end
80
- it 'returns the transition event name' do
81
- @core.on(:foo) { throw(:transition, [:bar]) }
82
- @core.trigger(:foo).should.equal [:bar]
83
- end
84
- end
@@ -1,69 +0,0 @@
1
- require "#{File.dirname(__FILE__)}/spec_setup"
2
- require 'rack/utils/environment_headers'
3
-
4
- describe 'Rack::Utils::EnvironmentHeaders' do
5
-
6
- before :each do
7
- @now = Time.now.httpdate
8
- @env = {
9
- 'CONTENT_TYPE' => 'text/plain',
10
- 'CONTENT_LENGTH' => '0x1A4',
11
- 'HTTP_X_FOO' => 'BAR',
12
- 'HTTP_IF_MODIFIED_SINCE' => @now,
13
- 'rack.run_once' => true
14
- }
15
- @h = Rack::Utils::EnvironmentHeaders.new(@env)
16
- end
17
-
18
- after(:each) {
19
- @env, @h = nil, nil
20
- }
21
-
22
- it 'retrieves headers with #[]' do
23
- @h.should.respond_to :[]
24
- @h['X-Foo'].should.equal 'BAR'
25
- @h['If-Modified-Since'].should.equal @now
26
- end
27
-
28
- it 'sets headers with #[]=' do
29
- @h.should.respond_to :[]=
30
- @h['X-Foo'] = 'BAZZLE'
31
- @h['X-Foo'].should.equal 'BAZZLE'
32
- end
33
-
34
- it 'sets values on the underlying environment hash' do
35
- @h['X-Something-Else'] = 'FOO'
36
- @env['HTTP_X_SOMETHING_ELSE'].should.equal 'FOO'
37
- end
38
-
39
- it 'handles Content-Type special case' do
40
- @h['Content-Type'].should.equal 'text/plain'
41
- end
42
-
43
- it 'handles Content-Length special case' do
44
- @h['Content-Length'].should.equal '0x1A4'
45
- end
46
-
47
- it 'implements #include? with RFC 2616 header name' do
48
- @h.should.include 'If-Modified-Since'
49
- end
50
-
51
- it 'deletes underlying env entries' do
52
- @h.delete('X-Foo')
53
- @env.should.not.include? 'HTTP_X_FOO'
54
- end
55
-
56
- it 'returns the underlying environment hash with #to_env' do
57
- @h.to_env.should.be @env
58
- end
59
-
60
- it 'iterates over all headers with #each' do
61
- hash = {}
62
- @h.each { |name,value| hash[name] = value }
63
- hash.should.equal 'Content-Type' => 'text/plain',
64
- 'Content-Length' => '0x1A4',
65
- 'X-Foo' => 'BAR',
66
- 'If-Modified-Since' => @now
67
- end
68
-
69
- end
data/test/headers_test.rb DELETED
@@ -1,298 +0,0 @@
1
- require "#{File.dirname(__FILE__)}/spec_setup"
2
-
3
- class MockResponse < Rack::MockResponse
4
- include Rack::Cache::Headers
5
- include Rack::Cache::ResponseHeaders
6
- public :now
7
- end
8
-
9
- describe 'Rack::Cache::Headers' do
10
- before :each do
11
- @now = Time.httpdate(Time.now.httpdate)
12
- @res = MockResponse.new(200, {'Date' => @now.httpdate}, '')
13
- @one_hour_ago = Time.httpdate((Time.now - (60**2)).httpdate)
14
- end
15
- after :each do
16
- @now, @res, @one_hour_ago = nil
17
- end
18
-
19
- describe '#cache_control' do
20
- it 'handles single name=value pair' do
21
- @res.headers['Cache-Control'] = 'max-age=600'
22
- @res.cache_control['max-age'].should.equal '600'
23
- end
24
- it 'handles multiple name=value pairs' do
25
- @res.headers['Cache-Control'] = 'max-age=600, max-stale=300, min-fresh=570'
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
- end
30
- it 'handles a single flag value' do
31
- @res.headers['Cache-Control'] = 'no-cache'
32
- @res.cache_control.should.include 'no-cache'
33
- @res.cache_control['no-cache'].should.be true
34
- end
35
- it 'handles a bunch of all kinds of stuff' do
36
- @res.headers['Cache-Control'] = 'max-age=600,must-revalidate,min-fresh=3000,foo=bar,baz'
37
- @res.cache_control['max-age'].should.equal '600'
38
- @res.cache_control['must-revalidate'].should.be true
39
- @res.cache_control['min-fresh'].should.equal '3000'
40
- @res.cache_control['foo'].should.equal 'bar'
41
- @res.cache_control['baz'].should.be true
42
- end
43
- it 'removes the header when given an empty hash' do
44
- @res.headers['Cache-Control'] = 'max-age=600, must-revalidate'
45
- @res.cache_control['max-age'].should.equal '600'
46
- @res.cache_control = {}
47
- @res.headers.should.not.include 'Cache-Control'
48
- end
49
- end
50
- end
51
-
52
- describe 'Rack::Cache::ResponseHeaders' do
53
- before :each do
54
- @now = Time.httpdate(Time.now.httpdate)
55
- @one_hour_ago = Time.httpdate((Time.now - (60**2)).httpdate)
56
- @one_hour_later = Time.httpdate((Time.now + (60**2)).httpdate)
57
- @res = MockResponse.new(200, {'Date' => @now.httpdate}, '')
58
- end
59
- after :each do
60
- @now, @res, @one_hour_ago = nil
61
- end
62
-
63
- describe '#validateable?' do
64
- it 'is true when Last-Modified header present' do
65
- @res = MockResponse.new(200, { 'Last-Modified' => @one_hour_ago.httpdate }, '')
66
- @res.extend Rack::Cache::ResponseHeaders
67
- @res.should.be.validateable
68
- end
69
- it 'is true when Etag header present' do
70
- @res = MockResponse.new(200, { 'Etag' => '"12345"' }, '')
71
- @res.extend Rack::Cache::ResponseHeaders
72
- @res.should.be.validateable
73
- end
74
- it 'is false when no validator is present' do
75
- @res = MockResponse.new(200, {}, '')
76
- @res.extend Rack::Cache::ResponseHeaders
77
- @res.should.not.be.validateable
78
- end
79
- end
80
-
81
- describe '#date' do
82
- it 'uses the Date header if present' do
83
- @res = MockResponse.new(200, { 'Date' => @one_hour_ago.httpdate }, '')
84
- @res.extend Rack::Cache::ResponseHeaders
85
- @res.date.should.equal @one_hour_ago
86
- end
87
- it 'uses the current time when no Date header present' do
88
- @res = MockResponse.new(200, {}, '')
89
- @res.extend Rack::Cache::ResponseHeaders
90
- @res.date.should.be.close Time.now, 1
91
- end
92
- it 'returns the correct date when the header is modified directly' do
93
- @res = MockResponse.new(200, { 'Date' => @one_hour_ago.httpdate }, '')
94
- @res.extend Rack::Cache::ResponseHeaders
95
- @res.date.should.equal @one_hour_ago
96
- @res.headers['Date'] = @now.httpdate
97
- @res.date.should.equal @now
98
- end
99
- end
100
-
101
- describe '#expires_at' do
102
- it 'returns #date + #max_age when Cache-Control/max-age is present' do
103
- @res.headers['Cache-Control'] = 'max-age=500'
104
- @res.expires_at.should.equal @res.date + 500
105
- end
106
- it 'uses the Expires header when present and no Cache-Control/max-age' do
107
- @res.headers['Expires'] = @one_hour_ago.httpdate
108
- @res.expires_at.should.equal @one_hour_ago
109
- end
110
- it 'returns nil when no Expires or Cache-Control provided' do
111
- @res.expires_at.should.be nil
112
- end
113
- end
114
-
115
- describe '#max_age' do
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
121
- @res.headers['Cache-Control'] = 'max-age=600'
122
- @res.max_age.should.equal 600
123
- end
124
- it 'falls back to Expires when no max-age or s-maxage directive present' do
125
- @res.headers['Cache-Control'] = 'must-revalidate'
126
- @res.headers['Expires'] = @one_hour_later.httpdate
127
- @res.max_age.should.equal 60 ** 2
128
- end
129
- it 'gives a #max_age of nil when no freshness information available' do
130
- @res.max_age.should.be.nil
131
- end
132
- end
133
-
134
- describe '#freshness_information?' do
135
- it 'is true when Expires header is present' do
136
- @res.headers['Expires'] = Time.now.httpdate
137
- @res.freshness_information?.should.be true
138
- end
139
- it 'is true when a Cache-Control max-age directive is present' do
140
- @res.headers['Cache-Control'] = 'max-age=500'
141
- @res.freshness_information?.should.be true
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
147
- it 'is not true otherwise' do
148
- @res.freshness_information?.should.be false
149
- end
150
- end
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
-
206
- describe '#no_cache?' do
207
- it 'is true when a Cache-Control no-cache directive is present' do
208
- @res.headers['Cache-Control'] = '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?
224
- end
225
- it 'is false otherwise' do
226
- assert !@res.must_revalidate?
227
- end
228
- end
229
-
230
- describe '#stale?' do
231
- it 'is true when TTL cannot be established' do
232
- @res.should.be.stale
233
- end
234
- it 'is false when the TTL is <= 0' do
235
- @res.headers['Expires'] = (@res.now + 10).httpdate
236
- @res.should.not.be.stale
237
- end
238
- it 'is true when the TTL is >= 0' do
239
- @res.headers['Expires'] = (@res.now - 10).httpdate
240
- @res.should.be.stale
241
- end
242
- end
243
-
244
- describe '#ttl' do
245
- it 'is nil when no Expires or Cache-Control headers present' do
246
- @res.ttl.should.be.nil
247
- end
248
- it 'uses the Expires header when no max-age is present' do
249
- @res.headers['Expires'] = (@res.now + (60**2)).httpdate
250
- @res.ttl.should.be.close(60**2, 1)
251
- end
252
- it 'returns negative values when Expires is in part' do
253
- @res.ttl.should.be.nil
254
- @res.headers['Expires'] = @one_hour_ago.httpdate
255
- @res.ttl.should.be < 0
256
- end
257
- it 'uses the Cache-Control max-age value when present' do
258
- @res.headers['Cache-Control'] = 'max-age=60'
259
- @res.ttl.should.be.close(60, 1)
260
- end
261
- end
262
-
263
- describe '#vary' do
264
- it 'is nil when no Vary header is present' do
265
- @res.vary.should.be.nil
266
- end
267
- it 'returns the literal value of the Vary header' do
268
- @res.headers['Vary'] = 'Foo Bar Baz'
269
- @res.vary.should.equal 'Foo Bar Baz'
270
- end
271
- it 'can be checked for existence using the #vary? method' do
272
- @res.should.respond_to :vary?
273
- @res.should.not.vary
274
- @res.headers['Vary'] = '*'
275
- @res.should.vary
276
- end
277
- end
278
-
279
- describe '#vary_header_names' do
280
- it 'returns an empty Array when no Vary header is present' do
281
- @res.vary_header_names.should.be.empty
282
- end
283
- it 'parses a single header name value' do
284
- @res.headers['Vary'] = 'Accept-Language'
285
- @res.vary_header_names.should.equal ['Accept-Language']
286
- end
287
- it 'parses multiple header name values separated by spaces' do
288
- @res.headers['Vary'] = 'Accept-Language User-Agent X-Foo'
289
- @res.vary_header_names.should.equal \
290
- ['Accept-Language', 'User-Agent', 'X-Foo']
291
- end
292
- it 'parses multiple header name values separated by commas' do
293
- @res.headers['Vary'] = 'Accept-Language,User-Agent, X-Foo'
294
- @res.vary_header_names.should.equal \
295
- ['Accept-Language', 'User-Agent', 'X-Foo']
296
- end
297
- end
298
- end