sinatra-sinatra 0.9.0.2 → 0.9.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.
@@ -51,6 +51,17 @@ module Sinatra
51
51
  module Compat
52
52
  end
53
53
 
54
+ # Make Sinatra::EventContext an alias for Sinatra::Default to unbreak plugins.
55
+ def self.const_missing(const_name)
56
+ if const_name == :EventContext
57
+ const_set :EventContext, Sinatra::Default
58
+ sinatra_warn 'Sinatra::EventContext is deprecated; use Sinatra::Default instead.'
59
+ Sinatra::Default
60
+ else
61
+ super
62
+ end
63
+ end
64
+
54
65
  # The ServerError exception is deprecated. Any exception is considered an
55
66
  # internal server error.
56
67
  class ServerError < RuntimeError
@@ -93,21 +104,10 @@ module Sinatra
93
104
  etag(*args, &block)
94
105
  end
95
106
 
96
- # The :disposition option is deprecated; use: #attachment. This method
97
- # setting the Content-Transfer-Encoding header is deprecated.
98
- #--
99
- # TODO deprecation warning for :disposition argument.
100
- def send_file(path, opts={})
101
- opts[:disposition] = 'attachment' if !opts.key?(:disposition)
102
- attachment opts[:filename] || path if opts[:filename] || opts[:disposition]
103
- response['Content-Transfer-Encoding'] = 'binary' if opts[:disposition]
104
- super(path, opts)
105
- end
106
-
107
107
  # Throwing halt with a Symbol and the to_result convention are
108
108
  # deprecated. Override the invoke method to detect those types of return
109
109
  # values.
110
- def invoke(handler)
110
+ def invoke(&block)
111
111
  res = super
112
112
  case
113
113
  when res.kind_of?(Symbol)
data/lib/sinatra/main.rb CHANGED
@@ -9,7 +9,7 @@ module Sinatra
9
9
  /custom_require\.rb$/ # rubygems require hacks
10
10
  ]
11
11
  path =
12
- caller.map{ |line| line.split(':', 2).first }.find do |file|
12
+ caller.map{ |line| line.split(/:\d/, 2).first }.find do |file|
13
13
  next if ignore.any? { |pattern| file =~ pattern }
14
14
  file
15
15
  end
@@ -17,7 +17,6 @@ module Sinatra
17
17
  }.call
18
18
 
19
19
  set :run, Proc.new { $0 == app_file }
20
- set :reload, Proc.new{ app_file? && development? }
21
20
 
22
21
  if run? && ARGV.any?
23
22
  require 'optparse'
data/lib/sinatra/test.rb CHANGED
@@ -75,20 +75,26 @@ module Sinatra
75
75
  }
76
76
 
77
77
  def rack_opts(opts)
78
- opts.inject({}) do |hash,(key,val)|
78
+ opts.merge(:lint => true).inject({}) do |hash,(key,val)|
79
79
  key = RACK_OPT_NAMES[key] || key
80
80
  hash[key] = val
81
81
  hash
82
82
  end
83
83
  end
84
84
 
85
- def env_for(opts={})
86
- opts = rack_opts(opts)
87
- Rack::MockRequest.env_for(opts)
88
- end
89
-
90
- def param_string(hash)
91
- hash.map { |pair| pair.map{|v|escape(v)}.join('=') }.join('&')
85
+ def param_string(value, prefix = nil)
86
+ case value
87
+ when Array
88
+ value.map { |v|
89
+ param_string(v, "#{prefix}[]")
90
+ } * "&"
91
+ when Hash
92
+ value.map { |k, v|
93
+ param_string(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
94
+ } * "&"
95
+ else
96
+ "#{prefix}=#{escape(value)}"
97
+ end
92
98
  end
93
99
 
94
100
  if defined? Sinatra::Compat
@@ -2,7 +2,7 @@ require 'bacon'
2
2
  require 'sinatra/test'
3
3
 
4
4
  Sinatra::Default.set(
5
- :env => :test,
5
+ :environment => :test,
6
6
  :run => false,
7
7
  :raise_errors => true,
8
8
  :logging => false
@@ -1,8 +1,10 @@
1
1
  require 'sinatra/test'
2
+ require 'sinatra/test/unit'
3
+ require 'spec'
2
4
  require 'spec/interop/test'
3
5
 
4
6
  Sinatra::Default.set(
5
- :env => :test,
7
+ :environment => :test,
6
8
  :run => false,
7
9
  :raise_errors => true,
8
10
  :logging => false
@@ -4,7 +4,7 @@ require 'test/unit'
4
4
  Test::Unit::TestCase.send :include, Sinatra::Test
5
5
 
6
6
  Sinatra::Default.set(
7
- :env => :test,
7
+ :environment => :test,
8
8
  :run => false,
9
9
  :raise_errors => true,
10
10
  :logging => false
data/sinatra.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 = 'sinatra'
6
- s.version = '0.9.0.2'
7
- s.date = '2009-01-18'
6
+ s.version = '0.9.0.4'
7
+ s.date = '2009-01-25'
8
8
 
9
9
  s.description = "Classy web-development dressed in a DSL"
10
10
  s.summary = "Classy web-development dressed in a DSL"
data/test/filter_test.rb CHANGED
@@ -72,4 +72,28 @@ describe "Filters" do
72
72
  assert ok?
73
73
  assert_equal 'cool', body
74
74
  end
75
+
76
+ it "does modify the response with halt" do
77
+ mock_app {
78
+ before { halt 302, 'Hi' }
79
+ get '/foo' do
80
+ "should not happen"
81
+ end
82
+ }
83
+
84
+ get '/foo'
85
+ assert_equal 302, response.status
86
+ assert_equal 'Hi', body
87
+ end
88
+
89
+ it "gives you access to params" do
90
+ mock_app {
91
+ before { @foo = params['foo'] }
92
+ get('/foo') { @foo }
93
+ }
94
+
95
+ get '/foo?foo=cool'
96
+ assert ok?
97
+ assert_equal 'cool', body
98
+ end
75
99
  end
data/test/helper.rb CHANGED
@@ -1,14 +1,15 @@
1
1
  begin
2
- require 'test/spec'
2
+ require 'rack'
3
3
  rescue LoadError
4
4
  require 'rubygems'
5
- require 'test/spec'
5
+ require 'rack'
6
6
  end
7
7
 
8
- $:.unshift File.dirname(File.dirname(__FILE__)) + '/lib'
8
+ libdir = File.dirname(File.dirname(__FILE__)) + '/lib'
9
+ $LOAD_PATH.unshift libdir unless $LOAD_PATH.include?(libdir)
10
+
9
11
  require 'sinatra/base'
10
- require 'sinatra/test'
11
- require 'sinatra/test/spec'
12
+ require 'sinatra/test/unit'
12
13
 
13
14
  module Sinatra::Test
14
15
  # Sets up a Sinatra::Base subclass defined with the block
@@ -23,3 +24,21 @@ class Sinatra::Base
23
24
  # Allow assertions in request context
24
25
  include Test::Unit::Assertions
25
26
  end
27
+
28
+ ##
29
+ # test/spec/mini
30
+ # http://pastie.caboo.se/158871
31
+ # chris@ozmm.org
32
+ #
33
+ def describe(*args, &block)
34
+ return super unless (name = args.first) && block
35
+ klass = Class.new(Test::Unit::TestCase) do
36
+ def self.it(name, &block)
37
+ define_method("test_#{name.gsub(/\W/,'_')}", &block)
38
+ end
39
+ def self.xspecify(*args) end
40
+ def self.before(&block) define_method(:setup, &block) end
41
+ def self.after(&block) define_method(:teardown, &block) end
42
+ end
43
+ klass.class_eval &block
44
+ end
data/test/helpers_test.rb CHANGED
@@ -1,361 +1,381 @@
1
1
  require File.dirname(__FILE__) + '/helper'
2
2
 
3
- describe 'Sinatra::Helpers' do
4
- describe '#status' do
5
- setup do
6
- mock_app {
7
- get '/' do
8
- status 207
9
- nil
10
- end
11
- }
12
- end
13
-
14
- it 'sets the response status code' do
15
- get '/'
16
- assert_equal 207, response.status
17
- end
18
- end
19
-
20
- describe '#body' do
21
- it 'takes a block for defered body generation' do
22
- mock_app {
23
- get '/' do
24
- body { 'Hello World' }
25
- end
26
- }
27
-
28
- get '/'
29
- assert_equal 'Hello World', body
30
- end
31
-
32
- it 'takes a String, Array, or other object responding to #each' do
33
- mock_app {
34
- get '/' do
35
- body 'Hello World'
36
- end
37
- }
38
-
39
- get '/'
40
- assert_equal 'Hello World', body
41
- end
42
- end
43
-
44
- describe '#redirect' do
45
- it 'uses a 302 when only a path is given' do
46
- mock_app {
47
- get '/' do
48
- redirect '/foo'
49
- fail 'redirect should halt'
50
- end
51
- }
52
-
53
- get '/'
54
- assert_equal 302, status
55
- assert_equal '', body
56
- assert_equal '/foo', response['Location']
57
- end
58
-
59
- it 'uses the code given when specified' do
60
- mock_app {
61
- get '/' do
62
- redirect '/foo', 301
63
- fail 'redirect should halt'
64
- end
65
- }
66
-
67
- get '/'
68
- assert_equal 301, status
69
- assert_equal '', body
70
- assert_equal '/foo', response['Location']
71
- end
72
- end
73
-
74
- describe '#error' do
75
- it 'sets a status code and halts' do
76
- mock_app {
77
- get '/' do
78
- error 501
79
- fail 'error should halt'
80
- end
81
- }
82
-
83
- get '/'
84
- assert_equal 501, status
85
- assert_equal '', body
86
- end
87
-
88
- it 'takes an optional body' do
89
- mock_app {
90
- get '/' do
91
- error 501, 'FAIL'
92
- fail 'error should halt'
93
- end
94
- }
95
-
96
- get '/'
97
- assert_equal 501, status
98
- assert_equal 'FAIL', body
99
- end
100
-
101
- it 'uses a 500 status code when first argument is a body' do
102
- mock_app {
103
- get '/' do
104
- error 'FAIL'
105
- fail 'error should halt'
106
- end
107
- }
108
-
109
- get '/'
110
- assert_equal 500, status
111
- assert_equal 'FAIL', body
112
- end
113
- end
114
-
115
- describe '#not_found' do
116
- it 'halts with a 404 status' do
117
- mock_app {
118
- get '/' do
119
- not_found
120
- fail 'not_found should halt'
121
- end
122
- }
123
-
124
- get '/'
125
- assert_equal 404, status
126
- assert_equal '', body
127
- end
128
- end
129
-
130
- describe '#session' do
131
- it 'uses the existing rack.session' do
132
- mock_app {
133
- get '/' do
134
- session[:foo]
135
- end
136
- }
137
-
138
- get '/', :env => { 'rack.session' => { :foo => 'bar' } }
139
- assert_equal 'bar', body
140
- end
141
-
142
- it 'creates a new session when none provided' do
143
- mock_app {
144
- get '/' do
145
- assert session.empty?
146
- session[:foo] = 'bar'
147
- 'Hi'
148
- end
149
- }
150
-
151
- get '/'
152
- assert_equal 'Hi', body
153
- end
154
- end
155
-
156
- describe '#media_type' do
157
- include Sinatra::Helpers
158
- it "looks up media types in Rack's MIME registry" do
159
- Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
160
- assert_equal 'application/foo', media_type('foo')
161
- assert_equal 'application/foo', media_type('.foo')
162
- assert_equal 'application/foo', media_type(:foo)
163
- end
164
- it 'returns nil when given nil' do
165
- assert media_type(nil).nil?
166
- end
167
- it 'returns nil when media type not registered' do
168
- assert media_type(:bizzle).nil?
169
- end
170
- it 'returns the argument when given a media type string' do
171
- assert_equal 'text/plain', media_type('text/plain')
172
- end
173
- end
174
-
175
- describe '#content_type' do
176
- it 'sets the Content-Type header' do
177
- mock_app {
178
- get '/' do
179
- content_type 'text/plain'
180
- 'Hello World'
181
- end
182
- }
183
-
184
- get '/'
185
- assert_equal 'text/plain', response['Content-Type']
186
- assert_equal 'Hello World', body
187
- end
188
-
189
- it 'takes media type parameters (like charset=)' do
190
- mock_app {
191
- get '/' do
192
- content_type 'text/html', :charset => 'utf-8'
193
- "<h1>Hello, World</h1>"
194
- end
195
- }
196
-
197
- get '/'
198
- assert ok?
199
- assert_equal 'text/html;charset=utf-8', response['Content-Type']
200
- assert_equal "<h1>Hello, World</h1>", body
201
- end
202
-
203
- it "looks up symbols in Rack's mime types dictionary" do
204
- Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
205
- mock_app {
206
- get '/foo.xml' do
207
- content_type :foo
208
- "I AM FOO"
209
- end
210
- }
211
-
212
- get '/foo.xml'
213
- assert ok?
214
- assert_equal 'application/foo', response['Content-Type']
215
- assert_equal 'I AM FOO', body
216
- end
217
-
218
- it 'fails when no mime type is registered for the argument provided' do
219
- mock_app {
220
- get '/foo.xml' do
221
- content_type :bizzle
222
- "I AM FOO"
223
- end
224
- }
225
- assert_raise(RuntimeError) { get '/foo.xml' }
226
- end
227
- end
228
-
229
- describe '#send_file' do
230
- before {
231
- @file = File.dirname(__FILE__) + '/file.txt'
232
- File.open(@file, 'wb') { |io| io.write('Hello World') }
3
+ describe 'Helpers#status' do
4
+ before do
5
+ mock_app {
6
+ get '/' do
7
+ status 207
8
+ nil
9
+ end
233
10
  }
234
- after {
235
- File.unlink @file
236
- @file = nil
11
+ end
12
+
13
+ it 'sets the response status code' do
14
+ get '/'
15
+ assert_equal 207, response.status
16
+ end
17
+ end
18
+
19
+ describe 'Helpers#body' do
20
+ it 'takes a block for defered body generation' do
21
+ mock_app {
22
+ get '/' do
23
+ body { 'Hello World' }
24
+ end
25
+ }
26
+
27
+ get '/'
28
+ assert_equal 'Hello World', body
29
+ end
30
+
31
+ it 'takes a String, Array, or other object responding to #each' do
32
+ mock_app {
33
+ get '/' do
34
+ body 'Hello World'
35
+ end
36
+ }
37
+
38
+ get '/'
39
+ assert_equal 'Hello World', body
40
+ end
41
+ end
42
+
43
+ describe 'Helpers#redirect' do
44
+ it 'uses a 302 when only a path is given' do
45
+ mock_app {
46
+ get '/' do
47
+ redirect '/foo'
48
+ fail 'redirect should halt'
49
+ end
50
+ }
51
+
52
+ get '/'
53
+ assert_equal 302, status
54
+ assert_equal '', body
55
+ assert_equal '/foo', response['Location']
56
+ end
57
+
58
+ it 'uses the code given when specified' do
59
+ mock_app {
60
+ get '/' do
61
+ redirect '/foo', 301
62
+ fail 'redirect should halt'
63
+ end
64
+ }
65
+
66
+ get '/'
67
+ assert_equal 301, status
68
+ assert_equal '', body
69
+ assert_equal '/foo', response['Location']
70
+ end
71
+ end
72
+
73
+ describe 'Helpers#error' do
74
+ it 'sets a status code and halts' do
75
+ mock_app {
76
+ get '/' do
77
+ error 501
78
+ fail 'error should halt'
79
+ end
80
+ }
81
+
82
+ get '/'
83
+ assert_equal 501, status
84
+ assert_equal '', body
85
+ end
86
+
87
+ it 'takes an optional body' do
88
+ mock_app {
89
+ get '/' do
90
+ error 501, 'FAIL'
91
+ fail 'error should halt'
92
+ end
93
+ }
94
+
95
+ get '/'
96
+ assert_equal 501, status
97
+ assert_equal 'FAIL', body
98
+ end
99
+
100
+ it 'uses a 500 status code when first argument is a body' do
101
+ mock_app {
102
+ get '/' do
103
+ error 'FAIL'
104
+ fail 'error should halt'
105
+ end
106
+ }
107
+
108
+ get '/'
109
+ assert_equal 500, status
110
+ assert_equal 'FAIL', body
111
+ end
112
+ end
113
+
114
+ describe 'Helpers#not_found' do
115
+ it 'halts with a 404 status' do
116
+ mock_app {
117
+ get '/' do
118
+ not_found
119
+ fail 'not_found should halt'
120
+ end
121
+ }
122
+
123
+ get '/'
124
+ assert_equal 404, status
125
+ assert_equal '', body
126
+ end
127
+ end
128
+
129
+ describe 'Helpers#session' do
130
+ it 'uses the existing rack.session' do
131
+ mock_app {
132
+ get '/' do
133
+ session[:foo]
134
+ end
135
+ }
136
+
137
+ get '/', :env => { 'rack.session' => { :foo => 'bar' } }
138
+ assert_equal 'bar', body
139
+ end
140
+
141
+ it 'creates a new session when none provided' do
142
+ mock_app {
143
+ get '/' do
144
+ assert session.empty?
145
+ session[:foo] = 'bar'
146
+ 'Hi'
147
+ end
148
+ }
149
+
150
+ get '/'
151
+ assert_equal 'Hi', body
152
+ end
153
+ end
154
+
155
+ describe 'Helpers#media_type' do
156
+ include Sinatra::Helpers
157
+
158
+ it "looks up media types in Rack's MIME registry" do
159
+ Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
160
+ assert_equal 'application/foo', media_type('foo')
161
+ assert_equal 'application/foo', media_type('.foo')
162
+ assert_equal 'application/foo', media_type(:foo)
163
+ end
164
+
165
+ it 'returns nil when given nil' do
166
+ assert media_type(nil).nil?
167
+ end
168
+
169
+ it 'returns nil when media type not registered' do
170
+ assert media_type(:bizzle).nil?
171
+ end
172
+
173
+ it 'returns the argument when given a media type string' do
174
+ assert_equal 'text/plain', media_type('text/plain')
175
+ end
176
+ end
177
+
178
+ describe 'Helpers#content_type' do
179
+ it 'sets the Content-Type header' do
180
+ mock_app {
181
+ get '/' do
182
+ content_type 'text/plain'
183
+ 'Hello World'
184
+ end
237
185
  }
238
186
 
239
- def send_file_app
240
- path = @file
241
- mock_app {
242
- get '/file.txt' do
243
- send_file path
244
- end
245
- }
246
- end
247
-
248
- it "sends the contents of the file" do
249
- send_file_app
250
- get '/file.txt'
251
- assert ok?
252
- assert_equal 'Hello World', body
253
- end
254
-
255
- it 'sets the Content-Type response header if a mime-type can be located' do
256
- send_file_app
257
- get '/file.txt'
258
- assert_equal 'text/plain', response['Content-Type']
259
- end
260
-
261
- it 'sets the Content-Length response header' do
262
- send_file_app
263
- get '/file.txt'
264
- assert_equal 'Hello World'.length.to_s, response['Content-Length']
265
- end
266
-
267
- it 'sets the Last-Modified response header' do
268
- send_file_app
269
- get '/file.txt'
270
- assert_equal File.mtime(@file).httpdate, response['Last-Modified']
271
- end
272
-
273
- it "returns a 404 when not found" do
274
- mock_app {
275
- get '/' do
276
- send_file 'this-file-does-not-exist.txt'
277
- end
278
- }
279
- get '/'
280
- assert not_found?
281
- end
282
- end
283
-
284
- describe '#last_modified' do
285
- before do
286
- now = Time.now
287
- mock_app {
288
- get '/' do
289
- body { 'Hello World' }
290
- last_modified now
291
- 'Boo!'
292
- end
293
- }
294
- @now = now
295
- end
296
-
297
- it 'sets the Last-Modified header to a valid RFC 2616 date value' do
298
- get '/'
299
- assert_equal @now.httpdate, response['Last-Modified']
300
- end
301
-
302
- it 'returns a body when conditional get misses' do
303
- get '/'
304
- assert_equal 200, status
305
- assert_equal 'Boo!', body
306
- end
307
-
308
- it 'halts when a conditional GET matches' do
309
- get '/', :env => { 'HTTP_IF_MODIFIED_SINCE' => @now.httpdate }
310
- assert_equal 304, status
311
- assert_equal '', body
312
- end
313
- end
314
-
315
- describe '#etag' do
316
- before do
317
- mock_app {
318
- get '/' do
319
- body { 'Hello World' }
320
- etag 'FOO'
321
- 'Boo!'
322
- end
323
- }
324
- end
325
-
326
- it 'sets the ETag header' do
327
- get '/'
328
- assert_equal '"FOO"', response['ETag']
329
- end
330
-
331
- it 'returns a body when conditional get misses' do
332
- get '/'
333
- assert_equal 200, status
334
- assert_equal 'Boo!', body
335
- end
336
-
337
- it 'halts when a conditional GET matches' do
338
- get '/', :env => { 'HTTP_IF_NONE_MATCH' => '"FOO"' }
339
- assert_equal 304, status
340
- assert_equal '', body
341
- end
342
-
343
- it 'should handle multiple ETag values in If-None-Match header' do
344
- get '/', :env => { 'HTTP_IF_NONE_MATCH' => '"BAR", *' }
345
- assert_equal 304, status
346
- assert_equal '', body
347
- end
348
-
349
- it 'uses a weak etag with the :weak option' do
350
- mock_app {
351
- get '/' do
352
- etag 'FOO', :weak
353
- "that's weak, dude."
354
- end
355
- }
356
- get '/'
357
- assert_equal 'W/"FOO"', response['ETag']
358
- end
187
+ get '/'
188
+ assert_equal 'text/plain', response['Content-Type']
189
+ assert_equal 'Hello World', body
190
+ end
191
+
192
+ it 'takes media type parameters (like charset=)' do
193
+ mock_app {
194
+ get '/' do
195
+ content_type 'text/html', :charset => 'utf-8'
196
+ "<h1>Hello, World</h1>"
197
+ end
198
+ }
199
+
200
+ get '/'
201
+ assert ok?
202
+ assert_equal 'text/html;charset=utf-8', response['Content-Type']
203
+ assert_equal "<h1>Hello, World</h1>", body
204
+ end
205
+
206
+ it "looks up symbols in Rack's mime types dictionary" do
207
+ Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
208
+ mock_app {
209
+ get '/foo.xml' do
210
+ content_type :foo
211
+ "I AM FOO"
212
+ end
213
+ }
214
+
215
+ get '/foo.xml'
216
+ assert ok?
217
+ assert_equal 'application/foo', response['Content-Type']
218
+ assert_equal 'I AM FOO', body
219
+ end
220
+
221
+ it 'fails when no mime type is registered for the argument provided' do
222
+ mock_app {
223
+ get '/foo.xml' do
224
+ content_type :bizzle
225
+ "I AM FOO"
226
+ end
227
+ }
228
+ assert_raise(RuntimeError) { get '/foo.xml' }
229
+ end
230
+ end
231
+
232
+ describe 'Helpers#send_file' do
233
+ before do
234
+ @file = File.dirname(__FILE__) + '/file.txt'
235
+ File.open(@file, 'wb') { |io| io.write('Hello World') }
236
+ end
237
+
238
+ after do
239
+ File.unlink @file
240
+ @file = nil
241
+ end
242
+
243
+ def send_file_app(opts={})
244
+ path = @file
245
+ mock_app {
246
+ get '/file.txt' do
247
+ send_file path, opts
248
+ end
249
+ }
250
+ end
251
+
252
+ it "sends the contents of the file" do
253
+ send_file_app
254
+ get '/file.txt'
255
+ assert ok?
256
+ assert_equal 'Hello World', body
257
+ end
258
+
259
+ it 'sets the Content-Type response header if a mime-type can be located' do
260
+ send_file_app
261
+ get '/file.txt'
262
+ assert_equal 'text/plain', response['Content-Type']
263
+ end
264
+
265
+ it 'sets the Content-Length response header' do
266
+ send_file_app
267
+ get '/file.txt'
268
+ assert_equal 'Hello World'.length.to_s, response['Content-Length']
269
+ end
270
+
271
+ it 'sets the Last-Modified response header' do
272
+ send_file_app
273
+ get '/file.txt'
274
+ assert_equal File.mtime(@file).httpdate, response['Last-Modified']
275
+ end
276
+
277
+ it "returns a 404 when not found" do
278
+ mock_app {
279
+ get '/' do
280
+ send_file 'this-file-does-not-exist.txt'
281
+ end
282
+ }
283
+ get '/'
284
+ assert not_found?
285
+ end
286
+
287
+ it "does not set the Content-Disposition header by default" do
288
+ send_file_app
289
+ get '/file.txt'
290
+ assert_nil response['Content-Disposition']
291
+ end
292
+
293
+ it "sets the Content-Disposition header when :disposition set to 'attachment'" do
294
+ send_file_app :disposition => 'attachment'
295
+ get '/file.txt'
296
+ assert_equal 'attachment; filename="file.txt"', response['Content-Disposition']
297
+ end
298
+
299
+ it "sets the Content-Disposition header when :filename provided" do
300
+ send_file_app :filename => 'foo.txt'
301
+ get '/file.txt'
302
+ assert_equal 'attachment; filename="foo.txt"', response['Content-Disposition']
303
+ end
304
+ end
305
+
306
+ describe 'Helpers#last_modified' do
307
+ before do
308
+ now = Time.now
309
+ mock_app {
310
+ get '/' do
311
+ body { 'Hello World' }
312
+ last_modified now
313
+ 'Boo!'
314
+ end
315
+ }
316
+ @now = now
317
+ end
318
+
319
+ it 'sets the Last-Modified header to a valid RFC 2616 date value' do
320
+ get '/'
321
+ assert_equal @now.httpdate, response['Last-Modified']
322
+ end
323
+
324
+ it 'returns a body when conditional get misses' do
325
+ get '/'
326
+ assert_equal 200, status
327
+ assert_equal 'Boo!', body
328
+ end
359
329
 
330
+ it 'halts when a conditional GET matches' do
331
+ get '/', :env => { 'HTTP_IF_MODIFIED_SINCE' => @now.httpdate }
332
+ assert_equal 304, status
333
+ assert_equal '', body
334
+ end
335
+ end
336
+
337
+ describe 'Helpers#etag' do
338
+ before do
339
+ mock_app {
340
+ get '/' do
341
+ body { 'Hello World' }
342
+ etag 'FOO'
343
+ 'Boo!'
344
+ end
345
+ }
346
+ end
347
+
348
+ it 'sets the ETag header' do
349
+ get '/'
350
+ assert_equal '"FOO"', response['ETag']
351
+ end
352
+
353
+ it 'returns a body when conditional get misses' do
354
+ get '/'
355
+ assert_equal 200, status
356
+ assert_equal 'Boo!', body
357
+ end
358
+
359
+ it 'halts when a conditional GET matches' do
360
+ get '/', :env => { 'HTTP_IF_NONE_MATCH' => '"FOO"' }
361
+ assert_equal 304, status
362
+ assert_equal '', body
363
+ end
364
+
365
+ it 'should handle multiple ETag values in If-None-Match header' do
366
+ get '/', :env => { 'HTTP_IF_NONE_MATCH' => '"BAR", *' }
367
+ assert_equal 304, status
368
+ assert_equal '', body
369
+ end
370
+
371
+ it 'uses a weak etag with the :weak option' do
372
+ mock_app {
373
+ get '/' do
374
+ etag 'FOO', :weak
375
+ "that's weak, dude."
376
+ end
377
+ }
378
+ get '/'
379
+ assert_equal 'W/"FOO"', response['ETag']
360
380
  end
361
381
  end