Syd-sinatra 0.9.0.2 → 0.9.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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'
@@ -33,10 +32,6 @@ end
33
32
 
34
33
  include Sinatra::Delegator
35
34
 
36
- def helpers(&block)
37
- Sinatra::Application.send :class_eval, &block
38
- end
39
-
40
35
  def mime(ext, type)
41
36
  ext = ".#{ext}" unless ext.to_s[0] == ?.
42
37
  Rack::Mime::MIME_TYPES[ext.to_s] = type
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,423 @@
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
3
+ describe 'Helpers#status' do
4
+ before do
5
+ mock_app {
6
+ get '/' do
7
+ status 207
8
+ nil
9
+ end
10
+ }
11
+ end
13
12
 
14
- it 'sets the response status code' do
15
- get '/'
16
- assert_equal 207, response.status
17
- end
13
+ it 'sets the response status code' do
14
+ get '/'
15
+ assert_equal 207, response.status
18
16
  end
17
+ end
19
18
 
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
- }
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
+ }
27
26
 
28
- get '/'
29
- assert_equal 'Hello World', body
30
- end
27
+ get '/'
28
+ assert_equal 'Hello World', body
29
+ end
31
30
 
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
- }
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
+ }
38
37
 
39
- get '/'
40
- assert_equal 'Hello World', body
41
- end
38
+ get '/'
39
+ assert_equal 'Hello World', body
42
40
  end
41
+ end
43
42
 
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
- }
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
+ }
66
51
 
67
- get '/'
68
- assert_equal 301, status
69
- assert_equal '', body
70
- assert_equal '/foo', response['Location']
71
- end
52
+ get '/'
53
+ assert_equal 302, status
54
+ assert_equal '', body
55
+ assert_equal '/foo', response['Location']
72
56
  end
73
57
 
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
- }
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
+ }
108
65
 
109
- get '/'
110
- assert_equal 500, status
111
- assert_equal 'FAIL', body
112
- end
66
+ get '/'
67
+ assert_equal 301, status
68
+ assert_equal '', body
69
+ assert_equal '/foo', response['Location']
113
70
  end
71
+ end
114
72
 
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
- }
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
+ }
123
81
 
124
- get '/'
125
- assert_equal 404, status
126
- assert_equal '', body
127
- end
82
+ get '/'
83
+ assert_equal 501, status
84
+ assert_equal '', body
128
85
  end
129
86
 
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
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
227
98
  end
228
99
 
229
- describe '#send_file' do
230
- before {
231
- @file = File.dirname(__FILE__) + '/file.txt'
232
- File.open(@file, 'wb') { |io| io.write('Hello World') }
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
233
106
  }
234
- after {
235
- File.unlink @file
236
- @file = nil
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
237
121
  }
238
122
 
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."
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
185
+ }
186
+
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
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']
380
+ end
381
+ end
382
+
383
+ describe 'Adding new helpers' do
384
+ module HelperOne; def one; '1'; end; end
385
+ module HelperTwo; def two; '2'; end; end
386
+
387
+ it 'should allow passing a list of modules' do
388
+ mock_app {
389
+ helpers HelperOne, HelperTwo
390
+
391
+ get '/one' do
392
+ one
393
+ end
394
+
395
+ get '/two' do
396
+ two
397
+ end
398
+ }
399
+
400
+ get '/one'
401
+ assert_equal '1', body
402
+
403
+ get '/two'
404
+ assert_equal '2', body
405
+ end
406
+
407
+ it 'should take a block and mix it into the app' do
408
+ mock_app {
409
+ helpers do
410
+ def foo
411
+ 'foo'
354
412
  end
355
- }
356
- get '/'
357
- assert_equal 'W/"FOO"', response['ETag']
358
- end
413
+ end
414
+
415
+ get '/' do
416
+ foo
417
+ end
418
+ }
359
419
 
420
+ get '/'
421
+ assert_equal 'foo', body
360
422
  end
361
423
  end