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.
- data/AUTHORS +8 -7
- data/README.rdoc +52 -81
- data/Rakefile +7 -58
- data/compat/streaming_test.rb +13 -1
- data/lib/sinatra/base.rb +106 -38
- data/lib/sinatra/compat.rb +12 -12
- data/lib/sinatra/main.rb +1 -2
- data/lib/sinatra/test.rb +14 -8
- data/lib/sinatra/test/bacon.rb +1 -1
- data/lib/sinatra/test/rspec.rb +3 -1
- data/lib/sinatra/test/unit.rb +1 -1
- data/sinatra.gemspec +2 -2
- data/test/filter_test.rb +24 -0
- data/test/helper.rb +24 -5
- data/test/helpers_test.rb +373 -353
- data/test/middleware_test.rb +2 -2
- data/test/options_test.rb +43 -0
- data/test/reload_test.rb +7 -0
- data/test/result_test.rb +10 -0
- data/test/routing_test.rb +98 -2
- data/test/static_test.rb +10 -0
- metadata +2 -2
data/lib/sinatra/compat.rb
CHANGED
@@ -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(
|
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(
|
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
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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
|
data/lib/sinatra/test/bacon.rb
CHANGED
data/lib/sinatra/test/rspec.rb
CHANGED
data/lib/sinatra/test/unit.rb
CHANGED
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.
|
7
|
-
s.date = '2009-01-
|
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 '
|
2
|
+
require 'rack'
|
3
3
|
rescue LoadError
|
4
4
|
require 'rubygems'
|
5
|
-
require '
|
5
|
+
require 'rack'
|
6
6
|
end
|
7
7
|
|
8
|
-
|
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 '
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
235
|
-
|
236
|
-
|
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
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
get '/'
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
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
|