darkhelmet-sinatra 0.9.0.5
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 +41 -0
- data/CHANGES +243 -0
- data/LICENSE +22 -0
- data/README.rdoc +535 -0
- data/Rakefile +136 -0
- data/compat/app_test.rb +301 -0
- data/compat/application_test.rb +334 -0
- data/compat/builder_test.rb +101 -0
- data/compat/compat_test.rb +12 -0
- data/compat/custom_error_test.rb +62 -0
- data/compat/erb_test.rb +136 -0
- data/compat/events_test.rb +78 -0
- data/compat/filter_test.rb +30 -0
- data/compat/haml_test.rb +233 -0
- data/compat/helper.rb +30 -0
- data/compat/mapped_error_test.rb +72 -0
- data/compat/pipeline_test.rb +71 -0
- data/compat/public/foo.xml +1 -0
- data/compat/sass_test.rb +57 -0
- data/compat/sessions_test.rb +39 -0
- data/compat/streaming_test.rb +133 -0
- data/compat/sym_params_test.rb +19 -0
- data/compat/template_test.rb +30 -0
- data/compat/use_in_file_templates_test.rb +47 -0
- data/compat/views/foo.builder +1 -0
- data/compat/views/foo.erb +1 -0
- data/compat/views/foo.haml +1 -0
- data/compat/views/foo.sass +2 -0
- data/compat/views/foo_layout.erb +2 -0
- data/compat/views/foo_layout.haml +2 -0
- data/compat/views/layout_test/foo.builder +1 -0
- data/compat/views/layout_test/foo.erb +1 -0
- data/compat/views/layout_test/foo.haml +1 -0
- data/compat/views/layout_test/foo.sass +2 -0
- data/compat/views/layout_test/layout.builder +3 -0
- data/compat/views/layout_test/layout.erb +1 -0
- data/compat/views/layout_test/layout.haml +1 -0
- data/compat/views/layout_test/layout.sass +2 -0
- data/compat/views/no_layout/no_layout.builder +1 -0
- data/compat/views/no_layout/no_layout.haml +1 -0
- data/lib/sinatra/base.rb +1007 -0
- data/lib/sinatra/compat.rb +252 -0
- data/lib/sinatra/images/404.png +0 -0
- data/lib/sinatra/images/500.png +0 -0
- data/lib/sinatra/main.rb +47 -0
- data/lib/sinatra/test/bacon.rb +19 -0
- data/lib/sinatra/test/rspec.rb +13 -0
- data/lib/sinatra/test/spec.rb +11 -0
- data/lib/sinatra/test/unit.rb +13 -0
- data/lib/sinatra/test.rb +121 -0
- data/lib/sinatra.rb +8 -0
- data/sinatra.gemspec +116 -0
- data/test/base_test.rb +112 -0
- data/test/builder_test.rb +64 -0
- data/test/data/reload_app_file.rb +3 -0
- data/test/erb_test.rb +81 -0
- data/test/extensions_test.rb +63 -0
- data/test/filter_test.rb +99 -0
- data/test/haml_test.rb +68 -0
- data/test/helper.rb +85 -0
- data/test/helpers_test.rb +467 -0
- data/test/mapped_error_test.rb +160 -0
- data/test/middleware_test.rb +60 -0
- data/test/options_test.rb +374 -0
- data/test/reload_test.rb +68 -0
- data/test/request_test.rb +18 -0
- data/test/response_test.rb +42 -0
- data/test/result_test.rb +98 -0
- data/test/routing_test.rb +712 -0
- data/test/sass_test.rb +36 -0
- data/test/server_test.rb +41 -0
- data/test/sinatra_test.rb +13 -0
- data/test/static_test.rb +65 -0
- data/test/templates_test.rb +88 -0
- data/test/test_test.rb +109 -0
- data/test/views/hello.builder +1 -0
- data/test/views/hello.erb +1 -0
- data/test/views/hello.haml +1 -0
- data/test/views/hello.sass +2 -0
- data/test/views/hello.test +1 -0
- data/test/views/layout2.builder +3 -0
- data/test/views/layout2.erb +2 -0
- data/test/views/layout2.haml +2 -0
- data/test/views/layout2.test +1 -0
- metadata +184 -0
data/test/reload_test.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
$reload_count = 0
|
4
|
+
$reload_app = nil
|
5
|
+
|
6
|
+
describe "Reloading" do
|
7
|
+
before {
|
8
|
+
@app = mock_app(Sinatra::Default)
|
9
|
+
$reload_app = @app
|
10
|
+
}
|
11
|
+
|
12
|
+
after {
|
13
|
+
$reload_app = nil
|
14
|
+
}
|
15
|
+
|
16
|
+
it 'is enabled by default when in development and the app_file is set' do
|
17
|
+
@app.set :app_file, __FILE__
|
18
|
+
@app.set :environment, :development
|
19
|
+
assert_same true, @app.reload
|
20
|
+
assert_same true, @app.reload?
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'is disabled by default when running in non-development environment' do
|
24
|
+
@app.set :app_file, __FILE__
|
25
|
+
@app.set :environment, :test
|
26
|
+
assert !@app.reload
|
27
|
+
assert_same false, @app.reload?
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'is disabled by default when no app_file is available' do
|
31
|
+
@app.set :app_file, nil
|
32
|
+
@app.set :environment, :development
|
33
|
+
assert !@app.reload
|
34
|
+
assert_same false, @app.reload?
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'is disabled when app_file is a rackup (.ru) file' do
|
38
|
+
@app.set :app_file, __FILE__.sub(/\.rb$/, '.ru')
|
39
|
+
@app.set :environment, :development
|
40
|
+
assert !@app.reload
|
41
|
+
assert_same false, @app.reload?
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'can be turned off explicitly' do
|
45
|
+
@app.set :app_file, __FILE__
|
46
|
+
@app.set :environment, :development
|
47
|
+
assert_same true, @app.reload
|
48
|
+
@app.set :reload, false
|
49
|
+
assert_same false, @app.reload
|
50
|
+
assert_same false, @app.reload?
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'reloads the app_file each time a request is made' do
|
54
|
+
@app.set :app_file, File.dirname(__FILE__) + '/data/reload_app_file.rb'
|
55
|
+
@app.set :reload, true
|
56
|
+
@app.get('/') { 'Hello World' }
|
57
|
+
|
58
|
+
get '/'
|
59
|
+
assert_equal 200, status
|
60
|
+
assert_equal 'Hello from reload file', body
|
61
|
+
assert_equal 1, $reload_count
|
62
|
+
|
63
|
+
get '/'
|
64
|
+
assert_equal 200, status
|
65
|
+
assert_equal 'Hello from reload file', body
|
66
|
+
assert_equal 2, $reload_count
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
describe 'Sinatra::Request' do
|
4
|
+
it 'responds to #user_agent' do
|
5
|
+
request = Sinatra::Request.new({'HTTP_USER_AGENT' => 'Test'})
|
6
|
+
assert request.respond_to?(:user_agent)
|
7
|
+
assert_equal 'Test', request.user_agent
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'parses POST params when Content-Type is form-dataish' do
|
11
|
+
request = Sinatra::Request.new(
|
12
|
+
'REQUEST_METHOD' => 'PUT',
|
13
|
+
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
|
14
|
+
'rack.input' => StringIO.new('foo=bar')
|
15
|
+
)
|
16
|
+
assert_equal 'bar', request.params['foo']
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/helper'
|
4
|
+
|
5
|
+
describe 'Sinatra::Response' do
|
6
|
+
before do
|
7
|
+
@response = Sinatra::Response.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it "initializes with 200, text/html, and empty body" do
|
11
|
+
assert_equal 200, @response.status
|
12
|
+
assert_equal 'text/html', @response['Content-Type']
|
13
|
+
assert_equal [], @response.body
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'uses case insensitive headers' do
|
17
|
+
@response['content-type'] = 'application/foo'
|
18
|
+
assert_equal 'application/foo', @response['Content-Type']
|
19
|
+
assert_equal 'application/foo', @response['CONTENT-TYPE']
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'writes to body' do
|
23
|
+
@response.body = 'Hello'
|
24
|
+
@response.write ' World'
|
25
|
+
assert_equal 'Hello World', @response.body
|
26
|
+
end
|
27
|
+
|
28
|
+
[204, 304].each do |status_code|
|
29
|
+
it "removes the Content-Type header and body when response status is #{status_code}" do
|
30
|
+
@response.status = status_code
|
31
|
+
@response.body = ['Hello World']
|
32
|
+
assert_equal [status_code, {}, []], @response.finish
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'Calculates the Content-Length using the bytesize of the body' do
|
37
|
+
@response.body = ['Hello', 'World!', '✈']
|
38
|
+
status, headers, body = @response.finish
|
39
|
+
assert_equal '14', headers['Content-Length']
|
40
|
+
assert_equal @response.body, body
|
41
|
+
end
|
42
|
+
end
|
data/test/result_test.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
describe 'Result Handling' do
|
4
|
+
it "sets response.body when result is a String" do
|
5
|
+
mock_app {
|
6
|
+
get '/' do
|
7
|
+
'Hello World'
|
8
|
+
end
|
9
|
+
}
|
10
|
+
|
11
|
+
get '/'
|
12
|
+
assert ok?
|
13
|
+
assert_equal 'Hello World', body
|
14
|
+
end
|
15
|
+
|
16
|
+
it "sets response.body when result is an Array of Strings" do
|
17
|
+
mock_app {
|
18
|
+
get '/' do
|
19
|
+
['Hello', 'World']
|
20
|
+
end
|
21
|
+
}
|
22
|
+
|
23
|
+
get '/'
|
24
|
+
assert ok?
|
25
|
+
assert_equal 'HelloWorld', body
|
26
|
+
end
|
27
|
+
|
28
|
+
it "sets response.body when result responds to #each" do
|
29
|
+
mock_app {
|
30
|
+
get '/' do
|
31
|
+
res = lambda { 'Hello World' }
|
32
|
+
def res.each ; yield call ; end
|
33
|
+
res
|
34
|
+
end
|
35
|
+
}
|
36
|
+
|
37
|
+
get '/'
|
38
|
+
assert ok?
|
39
|
+
assert_equal 'Hello World', body
|
40
|
+
end
|
41
|
+
|
42
|
+
it "sets response.body to [] when result is nil" do
|
43
|
+
mock_app {
|
44
|
+
get '/' do
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
}
|
48
|
+
|
49
|
+
get '/'
|
50
|
+
assert ok?
|
51
|
+
assert_equal '', body
|
52
|
+
end
|
53
|
+
|
54
|
+
it "sets status, headers, and body when result is a Rack response tuple" do
|
55
|
+
mock_app {
|
56
|
+
get '/' do
|
57
|
+
[205, {'Content-Type' => 'foo/bar'}, 'Hello World']
|
58
|
+
end
|
59
|
+
}
|
60
|
+
|
61
|
+
get '/'
|
62
|
+
assert_equal 205, status
|
63
|
+
assert_equal 'foo/bar', response['Content-Type']
|
64
|
+
assert_equal 'Hello World', body
|
65
|
+
end
|
66
|
+
|
67
|
+
it "sets status and body when result is a two-tuple" do
|
68
|
+
mock_app {
|
69
|
+
get '/' do
|
70
|
+
[409, 'formula of']
|
71
|
+
end
|
72
|
+
}
|
73
|
+
|
74
|
+
get '/'
|
75
|
+
assert_equal 409, status
|
76
|
+
assert_equal 'formula of', body
|
77
|
+
end
|
78
|
+
|
79
|
+
it "raises a TypeError when result is a non two or three tuple Array" do
|
80
|
+
mock_app {
|
81
|
+
get '/' do
|
82
|
+
[409, 'formula of', 'something else', 'even more']
|
83
|
+
end
|
84
|
+
}
|
85
|
+
|
86
|
+
assert_raise(TypeError) { get '/' }
|
87
|
+
end
|
88
|
+
|
89
|
+
it "sets status when result is a Fixnum status code" do
|
90
|
+
mock_app {
|
91
|
+
get('/') { 205 }
|
92
|
+
}
|
93
|
+
|
94
|
+
get '/'
|
95
|
+
assert_equal 205, status
|
96
|
+
assert_equal '', body
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,712 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
# Helper method for easy route pattern matching testing
|
4
|
+
def route_def(pattern)
|
5
|
+
mock_app { get(pattern) { } }
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "Routing" do
|
9
|
+
%w[get put post delete].each do |verb|
|
10
|
+
it "defines #{verb.upcase} request handlers with #{verb}" do
|
11
|
+
mock_app {
|
12
|
+
send verb, '/hello' do
|
13
|
+
'Hello World'
|
14
|
+
end
|
15
|
+
}
|
16
|
+
|
17
|
+
request = Rack::MockRequest.new(@app)
|
18
|
+
response = request.request(verb.upcase, '/hello', {})
|
19
|
+
assert response.ok?
|
20
|
+
assert_equal 'Hello World', response.body
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it "defines HEAD request handlers with HEAD" do
|
25
|
+
mock_app {
|
26
|
+
head '/hello' do
|
27
|
+
response['X-Hello'] = 'World!'
|
28
|
+
'remove me'
|
29
|
+
end
|
30
|
+
}
|
31
|
+
|
32
|
+
request = Rack::MockRequest.new(@app)
|
33
|
+
response = request.request('HEAD', '/hello', {})
|
34
|
+
assert response.ok?
|
35
|
+
assert_equal 'World!', response['X-Hello']
|
36
|
+
assert_equal '', response.body
|
37
|
+
end
|
38
|
+
|
39
|
+
it "404s when no route satisfies the request" do
|
40
|
+
mock_app {
|
41
|
+
get('/foo') { }
|
42
|
+
}
|
43
|
+
get '/bar'
|
44
|
+
assert_equal 404, status
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'takes multiple definitions of a route' do
|
48
|
+
mock_app {
|
49
|
+
user_agent(/Foo/)
|
50
|
+
get '/foo' do
|
51
|
+
'foo'
|
52
|
+
end
|
53
|
+
|
54
|
+
get '/foo' do
|
55
|
+
'not foo'
|
56
|
+
end
|
57
|
+
}
|
58
|
+
|
59
|
+
get '/foo', {}, 'HTTP_USER_AGENT' => 'Foo'
|
60
|
+
assert ok?
|
61
|
+
assert_equal 'foo', body
|
62
|
+
|
63
|
+
get '/foo'
|
64
|
+
assert ok?
|
65
|
+
assert_equal 'not foo', body
|
66
|
+
end
|
67
|
+
|
68
|
+
it "exposes params with indifferent hash" do
|
69
|
+
mock_app {
|
70
|
+
get '/:foo' do
|
71
|
+
assert_equal 'bar', params['foo']
|
72
|
+
assert_equal 'bar', params[:foo]
|
73
|
+
'well, alright'
|
74
|
+
end
|
75
|
+
}
|
76
|
+
get '/bar'
|
77
|
+
assert_equal 'well, alright', body
|
78
|
+
end
|
79
|
+
|
80
|
+
it "merges named params and query string params in params" do
|
81
|
+
mock_app {
|
82
|
+
get '/:foo' do
|
83
|
+
assert_equal 'bar', params['foo']
|
84
|
+
assert_equal 'biz', params['baz']
|
85
|
+
end
|
86
|
+
}
|
87
|
+
get '/bar?baz=biz'
|
88
|
+
assert ok?
|
89
|
+
end
|
90
|
+
|
91
|
+
it "supports named params like /hello/:person" do
|
92
|
+
mock_app {
|
93
|
+
get '/hello/:person' do
|
94
|
+
"Hello #{params['person']}"
|
95
|
+
end
|
96
|
+
}
|
97
|
+
get '/hello/Frank'
|
98
|
+
assert_equal 'Hello Frank', body
|
99
|
+
end
|
100
|
+
|
101
|
+
it "supports optional named params like /?:foo?/?:bar?" do
|
102
|
+
mock_app {
|
103
|
+
get '/?:foo?/?:bar?' do
|
104
|
+
"foo=#{params[:foo]};bar=#{params[:bar]}"
|
105
|
+
end
|
106
|
+
}
|
107
|
+
|
108
|
+
get '/hello/world'
|
109
|
+
assert ok?
|
110
|
+
assert_equal "foo=hello;bar=world", body
|
111
|
+
|
112
|
+
get '/hello'
|
113
|
+
assert ok?
|
114
|
+
assert_equal "foo=hello;bar=", body
|
115
|
+
|
116
|
+
get '/'
|
117
|
+
assert ok?
|
118
|
+
assert_equal "foo=;bar=", body
|
119
|
+
end
|
120
|
+
|
121
|
+
it "supports single splat params like /*" do
|
122
|
+
mock_app {
|
123
|
+
get '/*' do
|
124
|
+
assert params['splat'].kind_of?(Array)
|
125
|
+
params['splat'].join "\n"
|
126
|
+
end
|
127
|
+
}
|
128
|
+
|
129
|
+
get '/foo'
|
130
|
+
assert_equal "foo", body
|
131
|
+
|
132
|
+
get '/foo/bar/baz'
|
133
|
+
assert_equal "foo/bar/baz", body
|
134
|
+
end
|
135
|
+
|
136
|
+
it "supports mixing multiple splat params like /*/foo/*/*" do
|
137
|
+
mock_app {
|
138
|
+
get '/*/foo/*/*' do
|
139
|
+
assert params['splat'].kind_of?(Array)
|
140
|
+
params['splat'].join "\n"
|
141
|
+
end
|
142
|
+
}
|
143
|
+
|
144
|
+
get '/bar/foo/bling/baz/boom'
|
145
|
+
assert_equal "bar\nbling\nbaz/boom", body
|
146
|
+
|
147
|
+
get '/bar/foo/baz'
|
148
|
+
assert not_found?
|
149
|
+
end
|
150
|
+
|
151
|
+
it "supports mixing named and splat params like /:foo/*" do
|
152
|
+
mock_app {
|
153
|
+
get '/:foo/*' do
|
154
|
+
assert_equal 'foo', params['foo']
|
155
|
+
assert_equal ['bar/baz'], params['splat']
|
156
|
+
end
|
157
|
+
}
|
158
|
+
|
159
|
+
get '/foo/bar/baz'
|
160
|
+
assert ok?
|
161
|
+
end
|
162
|
+
|
163
|
+
it "matches a dot ('.') as part of a named param" do
|
164
|
+
mock_app {
|
165
|
+
get '/:foo/:bar' do
|
166
|
+
params[:foo]
|
167
|
+
end
|
168
|
+
}
|
169
|
+
|
170
|
+
get '/user@example.com/name'
|
171
|
+
assert_equal 200, response.status
|
172
|
+
assert_equal 'user@example.com', body
|
173
|
+
end
|
174
|
+
|
175
|
+
it "matches a literal dot ('.') outside of named params" do
|
176
|
+
mock_app {
|
177
|
+
get '/:file.:ext' do
|
178
|
+
assert_equal 'pony', params[:file]
|
179
|
+
assert_equal 'jpg', params[:ext]
|
180
|
+
'right on'
|
181
|
+
end
|
182
|
+
}
|
183
|
+
|
184
|
+
get '/pony.jpg'
|
185
|
+
assert_equal 200, response.status
|
186
|
+
assert_equal 'right on', body
|
187
|
+
end
|
188
|
+
|
189
|
+
it "literally matches . in paths" do
|
190
|
+
route_def '/test.bar'
|
191
|
+
|
192
|
+
get '/test.bar'
|
193
|
+
assert ok?
|
194
|
+
get 'test0bar'
|
195
|
+
assert not_found?
|
196
|
+
end
|
197
|
+
|
198
|
+
it "literally matches $ in paths" do
|
199
|
+
route_def '/test$/'
|
200
|
+
|
201
|
+
get '/test$/'
|
202
|
+
assert ok?
|
203
|
+
end
|
204
|
+
|
205
|
+
it "literally matches + in paths" do
|
206
|
+
route_def '/te+st/'
|
207
|
+
|
208
|
+
get '/te%2Bst/'
|
209
|
+
assert ok?
|
210
|
+
get '/teeeeeeest/'
|
211
|
+
assert not_found?
|
212
|
+
end
|
213
|
+
|
214
|
+
it "literally matches () in paths" do
|
215
|
+
route_def '/test(bar)/'
|
216
|
+
|
217
|
+
get '/test(bar)/'
|
218
|
+
assert ok?
|
219
|
+
end
|
220
|
+
|
221
|
+
it "supports basic nested params" do
|
222
|
+
mock_app {
|
223
|
+
get '/hi' do
|
224
|
+
params["person"]["name"]
|
225
|
+
end
|
226
|
+
}
|
227
|
+
|
228
|
+
get "/hi?person[name]=John+Doe"
|
229
|
+
assert ok?
|
230
|
+
assert_equal "John Doe", body
|
231
|
+
end
|
232
|
+
|
233
|
+
it "exposes nested params with indifferent hash" do
|
234
|
+
mock_app {
|
235
|
+
get '/testme' do
|
236
|
+
assert_equal 'baz', params['bar']['foo']
|
237
|
+
assert_equal 'baz', params['bar'][:foo]
|
238
|
+
'well, alright'
|
239
|
+
end
|
240
|
+
}
|
241
|
+
get '/testme?bar[foo]=baz'
|
242
|
+
assert_equal 'well, alright', body
|
243
|
+
end
|
244
|
+
|
245
|
+
it "supports deeply nested params" do
|
246
|
+
input = {
|
247
|
+
'browser[chrome][engine][name]' => 'V8',
|
248
|
+
'browser[chrome][engine][version]' => '1.0',
|
249
|
+
'browser[firefox][engine][name]' => 'spidermonkey',
|
250
|
+
'browser[firefox][engine][version]' => '1.7.0',
|
251
|
+
'emacs[map][goto-line]' => 'M-g g',
|
252
|
+
'emacs[version]' => '22.3.1',
|
253
|
+
'paste[name]' => 'hello world',
|
254
|
+
'paste[syntax]' => 'ruby'
|
255
|
+
}
|
256
|
+
expected = {
|
257
|
+
"emacs" => {
|
258
|
+
"map" => { "goto-line" => "M-g g" },
|
259
|
+
"version" => "22.3.1"
|
260
|
+
},
|
261
|
+
"browser" => {
|
262
|
+
"firefox" => {"engine" => {"name"=>"spidermonkey", "version"=>"1.7.0"}},
|
263
|
+
"chrome" => {"engine" => {"name"=>"V8", "version"=>"1.0"}}
|
264
|
+
},
|
265
|
+
"paste" => {"name"=>"hello world", "syntax"=>"ruby"}
|
266
|
+
}
|
267
|
+
mock_app {
|
268
|
+
get '/foo' do
|
269
|
+
assert_equal expected, params
|
270
|
+
'looks good'
|
271
|
+
end
|
272
|
+
}
|
273
|
+
get "/foo?#{build_query(input)}"
|
274
|
+
assert ok?
|
275
|
+
assert_equal 'looks good', body
|
276
|
+
end
|
277
|
+
|
278
|
+
it "preserves non-nested params" do
|
279
|
+
mock_app {
|
280
|
+
get '/foo' do
|
281
|
+
assert_equal "2", params["article_id"]
|
282
|
+
assert_equal "awesome", params['comment']['body']
|
283
|
+
assert_nil params['comment[body]']
|
284
|
+
'looks good'
|
285
|
+
end
|
286
|
+
}
|
287
|
+
|
288
|
+
get '/foo?article_id=2&comment[body]=awesome'
|
289
|
+
assert ok?
|
290
|
+
assert_equal 'looks good', body
|
291
|
+
end
|
292
|
+
|
293
|
+
it "matches paths that include spaces encoded with %20" do
|
294
|
+
mock_app {
|
295
|
+
get '/path with spaces' do
|
296
|
+
'looks good'
|
297
|
+
end
|
298
|
+
}
|
299
|
+
|
300
|
+
get '/path%20with%20spaces'
|
301
|
+
assert ok?
|
302
|
+
assert_equal 'looks good', body
|
303
|
+
end
|
304
|
+
|
305
|
+
it "matches paths that include spaces encoded with +" do
|
306
|
+
mock_app {
|
307
|
+
get '/path with spaces' do
|
308
|
+
'looks good'
|
309
|
+
end
|
310
|
+
}
|
311
|
+
|
312
|
+
get '/path+with+spaces'
|
313
|
+
assert ok?
|
314
|
+
assert_equal 'looks good', body
|
315
|
+
end
|
316
|
+
|
317
|
+
it "URL decodes named parameters and splats" do
|
318
|
+
mock_app {
|
319
|
+
get '/:foo/*' do
|
320
|
+
assert_equal 'hello world', params['foo']
|
321
|
+
assert_equal ['how are you'], params['splat']
|
322
|
+
nil
|
323
|
+
end
|
324
|
+
}
|
325
|
+
|
326
|
+
get '/hello%20world/how%20are%20you'
|
327
|
+
assert ok?
|
328
|
+
end
|
329
|
+
|
330
|
+
it 'supports regular expressions' do
|
331
|
+
mock_app {
|
332
|
+
get(/^\/foo...\/bar$/) do
|
333
|
+
'Hello World'
|
334
|
+
end
|
335
|
+
}
|
336
|
+
|
337
|
+
get '/foooom/bar'
|
338
|
+
assert ok?
|
339
|
+
assert_equal 'Hello World', body
|
340
|
+
end
|
341
|
+
|
342
|
+
it 'makes regular expression captures available in params[:captures]' do
|
343
|
+
mock_app {
|
344
|
+
get(/^\/fo(.*)\/ba(.*)/) do
|
345
|
+
assert_equal ['orooomma', 'f'], params[:captures]
|
346
|
+
'right on'
|
347
|
+
end
|
348
|
+
}
|
349
|
+
|
350
|
+
get '/foorooomma/baf'
|
351
|
+
assert ok?
|
352
|
+
assert_equal 'right on', body
|
353
|
+
end
|
354
|
+
|
355
|
+
it 'raises a TypeError when pattern is not a String or Regexp' do
|
356
|
+
@app = mock_app
|
357
|
+
assert_raise(TypeError) { @app.get(42){} }
|
358
|
+
end
|
359
|
+
|
360
|
+
it "returns response immediately on halt" do
|
361
|
+
mock_app {
|
362
|
+
get '/' do
|
363
|
+
halt 'Hello World'
|
364
|
+
'Boo-hoo World'
|
365
|
+
end
|
366
|
+
}
|
367
|
+
|
368
|
+
get '/'
|
369
|
+
assert ok?
|
370
|
+
assert_equal 'Hello World', body
|
371
|
+
end
|
372
|
+
|
373
|
+
it "halts with a response tuple" do
|
374
|
+
mock_app {
|
375
|
+
get '/' do
|
376
|
+
halt 295, {'Content-Type' => 'text/plain'}, 'Hello World'
|
377
|
+
end
|
378
|
+
}
|
379
|
+
|
380
|
+
get '/'
|
381
|
+
assert_equal 295, status
|
382
|
+
assert_equal 'text/plain', response['Content-Type']
|
383
|
+
assert_equal 'Hello World', body
|
384
|
+
end
|
385
|
+
|
386
|
+
it "halts with an array of strings" do
|
387
|
+
mock_app {
|
388
|
+
get '/' do
|
389
|
+
halt %w[Hello World How Are You]
|
390
|
+
end
|
391
|
+
}
|
392
|
+
|
393
|
+
get '/'
|
394
|
+
assert_equal 'HelloWorldHowAreYou', body
|
395
|
+
end
|
396
|
+
|
397
|
+
it "transitions to the next matching route on pass" do
|
398
|
+
mock_app {
|
399
|
+
get '/:foo' do
|
400
|
+
pass
|
401
|
+
'Hello Foo'
|
402
|
+
end
|
403
|
+
|
404
|
+
get '/*' do
|
405
|
+
assert !params.include?('foo')
|
406
|
+
'Hello World'
|
407
|
+
end
|
408
|
+
}
|
409
|
+
|
410
|
+
get '/bar'
|
411
|
+
assert ok?
|
412
|
+
assert_equal 'Hello World', body
|
413
|
+
end
|
414
|
+
|
415
|
+
it "transitions to 404 when passed and no subsequent route matches" do
|
416
|
+
mock_app {
|
417
|
+
get '/:foo' do
|
418
|
+
pass
|
419
|
+
'Hello Foo'
|
420
|
+
end
|
421
|
+
}
|
422
|
+
|
423
|
+
get '/bar'
|
424
|
+
assert not_found?
|
425
|
+
end
|
426
|
+
|
427
|
+
it "passes when matching condition returns false" do
|
428
|
+
mock_app {
|
429
|
+
condition { params[:foo] == 'bar' }
|
430
|
+
get '/:foo' do
|
431
|
+
'Hello World'
|
432
|
+
end
|
433
|
+
}
|
434
|
+
|
435
|
+
get '/bar'
|
436
|
+
assert ok?
|
437
|
+
assert_equal 'Hello World', body
|
438
|
+
|
439
|
+
get '/foo'
|
440
|
+
assert not_found?
|
441
|
+
end
|
442
|
+
|
443
|
+
it "does not pass when matching condition returns nil" do
|
444
|
+
mock_app {
|
445
|
+
condition { nil }
|
446
|
+
get '/:foo' do
|
447
|
+
'Hello World'
|
448
|
+
end
|
449
|
+
}
|
450
|
+
|
451
|
+
get '/bar'
|
452
|
+
assert ok?
|
453
|
+
assert_equal 'Hello World', body
|
454
|
+
end
|
455
|
+
|
456
|
+
it "passes to next route when condition calls pass explicitly" do
|
457
|
+
mock_app {
|
458
|
+
condition { pass unless params[:foo] == 'bar' }
|
459
|
+
get '/:foo' do
|
460
|
+
'Hello World'
|
461
|
+
end
|
462
|
+
}
|
463
|
+
|
464
|
+
get '/bar'
|
465
|
+
assert ok?
|
466
|
+
assert_equal 'Hello World', body
|
467
|
+
|
468
|
+
get '/foo'
|
469
|
+
assert not_found?
|
470
|
+
end
|
471
|
+
|
472
|
+
it "passes to the next route when host_name does not match" do
|
473
|
+
mock_app {
|
474
|
+
host_name 'example.com'
|
475
|
+
get '/foo' do
|
476
|
+
'Hello World'
|
477
|
+
end
|
478
|
+
}
|
479
|
+
get '/foo'
|
480
|
+
assert not_found?
|
481
|
+
|
482
|
+
get '/foo', :env => { 'HTTP_HOST' => 'example.com' }
|
483
|
+
assert_equal 200, status
|
484
|
+
assert_equal 'Hello World', body
|
485
|
+
end
|
486
|
+
|
487
|
+
it "passes to the next route when user_agent does not match" do
|
488
|
+
mock_app {
|
489
|
+
user_agent(/Foo/)
|
490
|
+
get '/foo' do
|
491
|
+
'Hello World'
|
492
|
+
end
|
493
|
+
}
|
494
|
+
get '/foo'
|
495
|
+
assert not_found?
|
496
|
+
|
497
|
+
get '/foo', :env => { 'HTTP_USER_AGENT' => 'Foo Bar' }
|
498
|
+
assert_equal 200, status
|
499
|
+
assert_equal 'Hello World', body
|
500
|
+
end
|
501
|
+
|
502
|
+
it "makes captures in user agent pattern available in params[:agent]" do
|
503
|
+
mock_app {
|
504
|
+
user_agent(/Foo (.*)/)
|
505
|
+
get '/foo' do
|
506
|
+
'Hello ' + params[:agent].first
|
507
|
+
end
|
508
|
+
}
|
509
|
+
get '/foo', :env => { 'HTTP_USER_AGENT' => 'Foo Bar' }
|
510
|
+
assert_equal 200, status
|
511
|
+
assert_equal 'Hello Bar', body
|
512
|
+
end
|
513
|
+
|
514
|
+
it "filters by accept header" do
|
515
|
+
mock_app {
|
516
|
+
get '/', :provides => :xml do
|
517
|
+
request.env['HTTP_ACCEPT']
|
518
|
+
end
|
519
|
+
}
|
520
|
+
|
521
|
+
get '/', :env => { :accept => 'application/xml' }
|
522
|
+
assert ok?
|
523
|
+
assert_equal 'application/xml', body
|
524
|
+
assert_equal 'application/xml', response.headers['Content-Type']
|
525
|
+
|
526
|
+
get '/', :env => { :accept => 'text/html' }
|
527
|
+
assert !ok?
|
528
|
+
end
|
529
|
+
|
530
|
+
it "allows multiple mime types for accept header" do
|
531
|
+
types = ['image/jpeg', 'image/pjpeg']
|
532
|
+
|
533
|
+
mock_app {
|
534
|
+
get '/', :provides => types do
|
535
|
+
request.env['HTTP_ACCEPT']
|
536
|
+
end
|
537
|
+
}
|
538
|
+
|
539
|
+
types.each do |type|
|
540
|
+
get '/', :env => { :accept => type }
|
541
|
+
assert ok?
|
542
|
+
assert_equal type, body
|
543
|
+
assert_equal type, response.headers['Content-Type']
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
it 'degrades gracefully when optional accept header is not provided' do
|
548
|
+
mock_app {
|
549
|
+
get '/', :provides => :xml do
|
550
|
+
request.env['HTTP_ACCEPT']
|
551
|
+
end
|
552
|
+
get '/' do
|
553
|
+
'default'
|
554
|
+
end
|
555
|
+
}
|
556
|
+
get '/'
|
557
|
+
assert ok?
|
558
|
+
assert_equal 'default', body
|
559
|
+
end
|
560
|
+
|
561
|
+
it 'passes a single url param as block parameters when one param is specified' do
|
562
|
+
mock_app {
|
563
|
+
get '/:foo' do |foo|
|
564
|
+
assert_equal 'bar', foo
|
565
|
+
end
|
566
|
+
}
|
567
|
+
|
568
|
+
get '/bar'
|
569
|
+
assert ok?
|
570
|
+
end
|
571
|
+
|
572
|
+
it 'passes multiple params as block parameters when many are specified' do
|
573
|
+
mock_app {
|
574
|
+
get '/:foo/:bar/:baz' do |foo, bar, baz|
|
575
|
+
assert_equal 'abc', foo
|
576
|
+
assert_equal 'def', bar
|
577
|
+
assert_equal 'ghi', baz
|
578
|
+
end
|
579
|
+
}
|
580
|
+
|
581
|
+
get '/abc/def/ghi'
|
582
|
+
assert ok?
|
583
|
+
end
|
584
|
+
|
585
|
+
it 'passes regular expression captures as block parameters' do
|
586
|
+
mock_app {
|
587
|
+
get(/^\/fo(.*)\/ba(.*)/) do |foo, bar|
|
588
|
+
assert_equal 'orooomma', foo
|
589
|
+
assert_equal 'f', bar
|
590
|
+
'looks good'
|
591
|
+
end
|
592
|
+
}
|
593
|
+
|
594
|
+
get '/foorooomma/baf'
|
595
|
+
assert ok?
|
596
|
+
assert_equal 'looks good', body
|
597
|
+
end
|
598
|
+
|
599
|
+
it "supports mixing multiple splat params like /*/foo/*/* as block parameters" do
|
600
|
+
mock_app {
|
601
|
+
get '/*/foo/*/*' do |foo, bar, baz|
|
602
|
+
assert_equal 'bar', foo
|
603
|
+
assert_equal 'bling', bar
|
604
|
+
assert_equal 'baz/boom', baz
|
605
|
+
'looks good'
|
606
|
+
end
|
607
|
+
}
|
608
|
+
|
609
|
+
get '/bar/foo/bling/baz/boom'
|
610
|
+
assert ok?
|
611
|
+
assert_equal 'looks good', body
|
612
|
+
end
|
613
|
+
|
614
|
+
it 'raises an ArgumentError with block arity > 1 and too many values' do
|
615
|
+
mock_app {
|
616
|
+
get '/:foo/:bar/:baz' do |foo, bar|
|
617
|
+
'quux'
|
618
|
+
end
|
619
|
+
}
|
620
|
+
|
621
|
+
assert_raise(ArgumentError) { get '/a/b/c' }
|
622
|
+
end
|
623
|
+
|
624
|
+
it 'raises an ArgumentError with block param arity > 1 and too few values' do
|
625
|
+
mock_app {
|
626
|
+
get '/:foo/:bar' do |foo, bar, baz|
|
627
|
+
'quux'
|
628
|
+
end
|
629
|
+
}
|
630
|
+
|
631
|
+
assert_raise(ArgumentError) { get '/a/b' }
|
632
|
+
end
|
633
|
+
|
634
|
+
it 'succeeds if no block parameters are specified' do
|
635
|
+
mock_app {
|
636
|
+
get '/:foo/:bar' do
|
637
|
+
'quux'
|
638
|
+
end
|
639
|
+
}
|
640
|
+
|
641
|
+
get '/a/b'
|
642
|
+
assert ok?
|
643
|
+
assert_equal 'quux', body
|
644
|
+
end
|
645
|
+
|
646
|
+
it 'passes all params with block param arity -1 (splat args)' do
|
647
|
+
mock_app {
|
648
|
+
get '/:foo/:bar' do |*args|
|
649
|
+
args.join
|
650
|
+
end
|
651
|
+
}
|
652
|
+
|
653
|
+
get '/a/b'
|
654
|
+
assert ok?
|
655
|
+
assert_equal 'ab', body
|
656
|
+
end
|
657
|
+
|
658
|
+
# NOTE Block params behaves differently under 1.8 and 1.9. Under 1.8, block
|
659
|
+
# param arity is lax: declaring a mismatched number of block params results
|
660
|
+
# in a warning. Under 1.9, block param arity is strict: mismatched block
|
661
|
+
# arity raises an ArgumentError.
|
662
|
+
|
663
|
+
if RUBY_VERSION >= '1.9'
|
664
|
+
|
665
|
+
it 'raises an ArgumentError with block param arity 1 and no values' do
|
666
|
+
mock_app {
|
667
|
+
get '/foo' do |foo|
|
668
|
+
'quux'
|
669
|
+
end
|
670
|
+
}
|
671
|
+
|
672
|
+
assert_raise(ArgumentError) { get '/foo' }
|
673
|
+
end
|
674
|
+
|
675
|
+
it 'raises an ArgumentError with block param arity 1 and too many values' do
|
676
|
+
mock_app {
|
677
|
+
get '/:foo/:bar/:baz' do |foo|
|
678
|
+
'quux'
|
679
|
+
end
|
680
|
+
}
|
681
|
+
|
682
|
+
assert_raise(ArgumentError) { get '/a/b/c' }
|
683
|
+
end
|
684
|
+
|
685
|
+
else
|
686
|
+
|
687
|
+
it 'does not raise an ArgumentError with block param arity 1 and no values' do
|
688
|
+
mock_app {
|
689
|
+
get '/foo' do |foo|
|
690
|
+
'quux'
|
691
|
+
end
|
692
|
+
}
|
693
|
+
|
694
|
+
silence_warnings { get '/foo' }
|
695
|
+
assert ok?
|
696
|
+
assert_equal 'quux', body
|
697
|
+
end
|
698
|
+
|
699
|
+
it 'does not raise an ArgumentError with block param arity 1 and too many values' do
|
700
|
+
mock_app {
|
701
|
+
get '/:foo/:bar/:baz' do |foo|
|
702
|
+
'quux'
|
703
|
+
end
|
704
|
+
}
|
705
|
+
|
706
|
+
silence_warnings { get '/a/b/c' }
|
707
|
+
assert ok?
|
708
|
+
assert_equal 'quux', body
|
709
|
+
end
|
710
|
+
|
711
|
+
end
|
712
|
+
end
|