rack-cache 0.3.0 → 0.4

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.

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