sinatra 1.4.8 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sinatra might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +77 -47
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +37 -49
- data/MAINTENANCE.md +42 -0
- data/README.de.md +5 -5
- data/README.es.md +5 -5
- data/README.fr.md +9 -9
- data/README.hu.md +3 -3
- data/README.ja.md +19 -8
- data/README.ko.md +8 -8
- data/README.md +90 -61
- data/README.pt-br.md +3 -3
- data/README.pt-pt.md +2 -2
- data/README.ru.md +42 -26
- data/README.zh.md +8 -8
- data/Rakefile +0 -6
- data/SECURITY.md +35 -0
- data/lib/sinatra/base.rb +113 -161
- data/lib/sinatra/main.rb +1 -0
- data/lib/sinatra/show_exceptions.rb +8 -8
- data/lib/sinatra/version.rb +1 -1
- data/sinatra.gemspec +7 -4
- metadata +34 -168
- data/lib/sinatra/ext.rb +0 -17
- data/test/asciidoctor_test.rb +0 -72
- data/test/base_test.rb +0 -167
- data/test/builder_test.rb +0 -91
- data/test/coffee_test.rb +0 -96
- data/test/compile_test.rb +0 -183
- data/test/contest.rb +0 -91
- data/test/creole_test.rb +0 -65
- data/test/delegator_test.rb +0 -160
- data/test/encoding_test.rb +0 -20
- data/test/erb_test.rb +0 -116
- data/test/extensions_test.rb +0 -98
- data/test/filter_test.rb +0 -487
- data/test/haml_test.rb +0 -109
- data/test/helper.rb +0 -132
- data/test/helpers_test.rb +0 -1917
- data/test/integration/app.rb +0 -79
- data/test/integration_helper.rb +0 -236
- data/test/integration_test.rb +0 -104
- data/test/less_test.rb +0 -69
- data/test/liquid_test.rb +0 -77
- data/test/mapped_error_test.rb +0 -285
- data/test/markaby_test.rb +0 -80
- data/test/markdown_test.rb +0 -85
- data/test/mediawiki_test.rb +0 -68
- data/test/middleware_test.rb +0 -68
- data/test/nokogiri_test.rb +0 -67
- data/test/public/favicon.ico +0 -0
- data/test/public/hello+world.txt +0 -1
- data/test/rabl_test.rb +0 -89
- data/test/rack_test.rb +0 -45
- data/test/radius_test.rb +0 -59
- data/test/rdoc_test.rb +0 -66
- data/test/readme_test.rb +0 -130
- data/test/request_test.rb +0 -100
- data/test/response_test.rb +0 -63
- data/test/result_test.rb +0 -76
- data/test/route_added_hook_test.rb +0 -59
- data/test/routing_test.rb +0 -1456
- data/test/sass_test.rb +0 -115
- data/test/scss_test.rb +0 -88
- data/test/server_test.rb +0 -56
- data/test/settings_test.rb +0 -582
- data/test/sinatra_test.rb +0 -12
- data/test/slim_test.rb +0 -102
- data/test/static_test.rb +0 -266
- data/test/streaming_test.rb +0 -149
- data/test/stylus_test.rb +0 -90
- data/test/templates_test.rb +0 -382
- data/test/textile_test.rb +0 -65
- data/test/views/a/in_a.str +0 -1
- data/test/views/ascii.erb +0 -2
- data/test/views/b/in_b.str +0 -1
- data/test/views/calc.html.erb +0 -1
- data/test/views/error.builder +0 -3
- data/test/views/error.erb +0 -3
- data/test/views/error.haml +0 -3
- data/test/views/error.sass +0 -2
- data/test/views/explicitly_nested.str +0 -1
- data/test/views/foo/hello.test +0 -1
- data/test/views/hello.asciidoc +0 -1
- data/test/views/hello.builder +0 -1
- data/test/views/hello.coffee +0 -1
- data/test/views/hello.creole +0 -1
- data/test/views/hello.erb +0 -1
- data/test/views/hello.haml +0 -1
- data/test/views/hello.less +0 -5
- data/test/views/hello.liquid +0 -1
- data/test/views/hello.mab +0 -1
- data/test/views/hello.md +0 -1
- data/test/views/hello.mediawiki +0 -1
- data/test/views/hello.nokogiri +0 -1
- data/test/views/hello.rabl +0 -2
- data/test/views/hello.radius +0 -1
- data/test/views/hello.rdoc +0 -1
- data/test/views/hello.sass +0 -2
- data/test/views/hello.scss +0 -3
- data/test/views/hello.slim +0 -1
- data/test/views/hello.str +0 -1
- data/test/views/hello.styl +0 -2
- data/test/views/hello.test +0 -1
- data/test/views/hello.textile +0 -1
- data/test/views/hello.wlang +0 -1
- data/test/views/hello.yajl +0 -1
- data/test/views/layout2.builder +0 -3
- data/test/views/layout2.erb +0 -2
- data/test/views/layout2.haml +0 -2
- data/test/views/layout2.liquid +0 -2
- data/test/views/layout2.mab +0 -2
- data/test/views/layout2.nokogiri +0 -3
- data/test/views/layout2.rabl +0 -3
- data/test/views/layout2.radius +0 -2
- data/test/views/layout2.slim +0 -3
- data/test/views/layout2.str +0 -2
- data/test/views/layout2.test +0 -1
- data/test/views/layout2.wlang +0 -2
- data/test/views/nested.str +0 -1
- data/test/views/utf8.erb +0 -2
- data/test/wlang_test.rb +0 -87
- data/test/yajl_test.rb +0 -86
data/test/readme_test.rb
DELETED
@@ -1,130 +0,0 @@
|
|
1
|
-
# Tests to check if all the README examples work.
|
2
|
-
require File.expand_path('../helper', __FILE__)
|
3
|
-
|
4
|
-
class ReadmeTest < Minitest::Test
|
5
|
-
example do
|
6
|
-
mock_app { get('/') { 'Hello world!' } }
|
7
|
-
get '/'
|
8
|
-
assert_body 'Hello world!'
|
9
|
-
end
|
10
|
-
|
11
|
-
section "Routes" do
|
12
|
-
example do
|
13
|
-
mock_app do
|
14
|
-
get('/') { ".. show something .." }
|
15
|
-
|
16
|
-
post('/') { ".. create something .." }
|
17
|
-
|
18
|
-
put('/') { ".. replace something .." }
|
19
|
-
|
20
|
-
patch('/') { ".. modify something .." }
|
21
|
-
|
22
|
-
delete('/') { ".. annihilate something .." }
|
23
|
-
|
24
|
-
options('/') { ".. appease something .." }
|
25
|
-
|
26
|
-
link('/') { ".. affiliate something .." }
|
27
|
-
|
28
|
-
unlink('/') { ".. separate something .." }
|
29
|
-
end
|
30
|
-
|
31
|
-
get '/'
|
32
|
-
assert_body '.. show something ..'
|
33
|
-
|
34
|
-
post '/'
|
35
|
-
assert_body '.. create something ..'
|
36
|
-
|
37
|
-
put '/'
|
38
|
-
assert_body '.. replace something ..'
|
39
|
-
|
40
|
-
patch '/'
|
41
|
-
assert_body '.. modify something ..'
|
42
|
-
|
43
|
-
delete '/'
|
44
|
-
assert_body '.. annihilate something ..'
|
45
|
-
|
46
|
-
options '/'
|
47
|
-
assert_body '.. appease something ..'
|
48
|
-
|
49
|
-
link '/'
|
50
|
-
assert_body '.. affiliate something ..'
|
51
|
-
|
52
|
-
unlink '/'
|
53
|
-
assert_body '.. separate something ..'
|
54
|
-
end
|
55
|
-
|
56
|
-
example do
|
57
|
-
mock_app do
|
58
|
-
get('/hello/:name') do
|
59
|
-
# matches "GET /hello/foo" and "GET /hello/bar"
|
60
|
-
# params[:name] is 'foo' or 'bar'
|
61
|
-
"Hello #{params[:name]}!"
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
get '/hello/foo'
|
66
|
-
assert_body 'Hello foo!'
|
67
|
-
|
68
|
-
get '/hello/bar'
|
69
|
-
assert_body 'Hello bar!'
|
70
|
-
end
|
71
|
-
|
72
|
-
example do
|
73
|
-
mock_app { get('/hello/:name') { |n| "Hello #{n}!" } }
|
74
|
-
|
75
|
-
get '/hello/foo'
|
76
|
-
assert_body 'Hello foo!'
|
77
|
-
|
78
|
-
get '/hello/bar'
|
79
|
-
assert_body 'Hello bar!'
|
80
|
-
end
|
81
|
-
|
82
|
-
example do
|
83
|
-
mock_app do
|
84
|
-
get('/say/*/to/*') do
|
85
|
-
# matches /say/hello/to/world
|
86
|
-
params[:splat].inspect # => ["hello", "world"]
|
87
|
-
end
|
88
|
-
|
89
|
-
get('/download/*.*') do
|
90
|
-
# matches /download/path/to/file.xml
|
91
|
-
params[:splat].inspect # => ["path/to/file", "xml"]
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
get "/say/hello/to/world"
|
96
|
-
assert_body '["hello", "world"]'
|
97
|
-
|
98
|
-
get "/download/path/to/file.xml"
|
99
|
-
assert_body '["path/to/file", "xml"]'
|
100
|
-
end
|
101
|
-
|
102
|
-
example do
|
103
|
-
mock_app do
|
104
|
-
get(%r{/hello/([\w]+)}) {
|
105
|
-
"Hello, #{params[:captures].first}!"
|
106
|
-
}
|
107
|
-
end
|
108
|
-
|
109
|
-
get '/hello/foo'
|
110
|
-
assert_body 'Hello, foo!'
|
111
|
-
|
112
|
-
get '/hello/bar'
|
113
|
-
assert_body 'Hello, bar!'
|
114
|
-
end
|
115
|
-
|
116
|
-
example do
|
117
|
-
mock_app do
|
118
|
-
get( %r{/hello/([\w]+)}) { |c|
|
119
|
-
"Hello, #{c}!"
|
120
|
-
}
|
121
|
-
end
|
122
|
-
|
123
|
-
get '/hello/foo'
|
124
|
-
assert_body 'Hello, foo!'
|
125
|
-
|
126
|
-
get '/hello/bar'
|
127
|
-
assert_body 'Hello, bar!'
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
data/test/request_test.rb
DELETED
@@ -1,100 +0,0 @@
|
|
1
|
-
require File.expand_path('../helper', __FILE__)
|
2
|
-
require 'stringio'
|
3
|
-
|
4
|
-
class RequestTest < Minitest::Test
|
5
|
-
it 'responds to #user_agent' do
|
6
|
-
request = Sinatra::Request.new({'HTTP_USER_AGENT' => 'Test'})
|
7
|
-
assert request.respond_to?(:user_agent)
|
8
|
-
assert_equal 'Test', request.user_agent
|
9
|
-
end
|
10
|
-
|
11
|
-
it 'parses POST params when Content-Type is form-dataish' do
|
12
|
-
request = Sinatra::Request.new(
|
13
|
-
'REQUEST_METHOD' => 'PUT',
|
14
|
-
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
|
15
|
-
'rack.input' => StringIO.new('foo=bar')
|
16
|
-
)
|
17
|
-
assert_equal 'bar', request.params['foo']
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'is secure when the url scheme is https' do
|
21
|
-
request = Sinatra::Request.new('rack.url_scheme' => 'https')
|
22
|
-
assert request.secure?
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'is not secure when the url scheme is http' do
|
26
|
-
request = Sinatra::Request.new('rack.url_scheme' => 'http')
|
27
|
-
assert !request.secure?
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'respects X-Forwarded-Proto header for proxied SSL' do
|
31
|
-
request = Sinatra::Request.new('HTTP_X_FORWARDED_PROTO' => 'https')
|
32
|
-
assert request.secure?
|
33
|
-
end
|
34
|
-
|
35
|
-
it 'is possible to marshal params' do
|
36
|
-
request = Sinatra::Request.new(
|
37
|
-
'REQUEST_METHOD' => 'PUT',
|
38
|
-
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
|
39
|
-
'rack.input' => StringIO.new('foo=bar')
|
40
|
-
)
|
41
|
-
Sinatra::Base.new!.send(:indifferent_hash).replace(request.params)
|
42
|
-
dumped = Marshal.dump(request.params)
|
43
|
-
assert_equal 'bar', Marshal.load(dumped)['foo']
|
44
|
-
end
|
45
|
-
|
46
|
-
it "exposes the preferred type's parameters" do
|
47
|
-
request = Sinatra::Request.new(
|
48
|
-
'HTTP_ACCEPT' => 'image/jpeg; compress=0.25'
|
49
|
-
)
|
50
|
-
assert_equal({ 'compress' => '0.25' }, request.preferred_type.params)
|
51
|
-
end
|
52
|
-
|
53
|
-
it "makes accept types behave like strings" do
|
54
|
-
request = Sinatra::Request.new('HTTP_ACCEPT' => 'image/jpeg; compress=0.25')
|
55
|
-
assert request.accept?('image/jpeg')
|
56
|
-
assert_equal 'image/jpeg', request.preferred_type.to_s
|
57
|
-
assert_equal 'image/jpeg; compress=0.25', request.preferred_type.to_s(true)
|
58
|
-
assert_equal 'image/jpeg', request.preferred_type.to_str
|
59
|
-
assert_equal 'image', request.preferred_type.split('/').first
|
60
|
-
|
61
|
-
String.instance_methods.each do |method|
|
62
|
-
next unless "".respond_to? method
|
63
|
-
assert request.preferred_type.respond_to?(method), "responds to #{method}"
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
it "accepts types when wildcards are requested" do
|
68
|
-
request = Sinatra::Request.new('HTTP_ACCEPT' => 'image/*')
|
69
|
-
assert request.accept?('image/jpeg')
|
70
|
-
end
|
71
|
-
|
72
|
-
it "properly decodes MIME type parameters" do
|
73
|
-
request = Sinatra::Request.new(
|
74
|
-
'HTTP_ACCEPT' => 'image/jpeg;unquoted=0.25;quoted="0.25";chartest="\";,\x"'
|
75
|
-
)
|
76
|
-
expected = { 'unquoted' => '0.25', 'quoted' => '0.25', 'chartest' => '";,x' }
|
77
|
-
assert_equal(expected, request.preferred_type.params)
|
78
|
-
end
|
79
|
-
|
80
|
-
it 'accepts */* when HTTP_ACCEPT is not present in the request' do
|
81
|
-
request = Sinatra::Request.new Hash.new
|
82
|
-
assert_equal 1, request.accept.size
|
83
|
-
assert request.accept?('text/html')
|
84
|
-
assert_equal '*/*', request.preferred_type.to_s
|
85
|
-
assert_equal '*/*', request.preferred_type.to_s(true)
|
86
|
-
end
|
87
|
-
|
88
|
-
it 'accepts */* when HTTP_ACCEPT is blank in the request' do
|
89
|
-
request = Sinatra::Request.new 'HTTP_ACCEPT' => ''
|
90
|
-
assert_equal 1, request.accept.size
|
91
|
-
assert request.accept?('text/html')
|
92
|
-
assert_equal '*/*', request.preferred_type.to_s
|
93
|
-
assert_equal '*/*', request.preferred_type.to_s(true)
|
94
|
-
end
|
95
|
-
|
96
|
-
it 'will not accept types not specified in HTTP_ACCEPT when HTTP_ACCEPT is provided' do
|
97
|
-
request = Sinatra::Request.new 'HTTP_ACCEPT' => 'application/json'
|
98
|
-
assert !request.accept?('text/html')
|
99
|
-
end
|
100
|
-
end
|
data/test/response_test.rb
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require File.expand_path('../helper', __FILE__)
|
4
|
-
|
5
|
-
class ResponseTest < Minitest::Test
|
6
|
-
setup { @response = Sinatra::Response.new }
|
7
|
-
|
8
|
-
def assert_same_body(a, b)
|
9
|
-
assert_equal a.to_enum(:each).to_a, b.to_enum(:each).to_a
|
10
|
-
end
|
11
|
-
|
12
|
-
it "initializes with 200, text/html, and empty body" do
|
13
|
-
assert_equal 200, @response.status
|
14
|
-
assert_equal 'text/html', @response['Content-Type']
|
15
|
-
assert_equal [], @response.body
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'uses case insensitive headers' do
|
19
|
-
@response['content-type'] = 'application/foo'
|
20
|
-
assert_equal 'application/foo', @response['Content-Type']
|
21
|
-
assert_equal 'application/foo', @response['CONTENT-TYPE']
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'writes to body' do
|
25
|
-
@response.body = 'Hello'
|
26
|
-
@response.write ' World'
|
27
|
-
assert_equal 'Hello World', @response.body.join
|
28
|
-
end
|
29
|
-
|
30
|
-
[204, 304].each do |status_code|
|
31
|
-
it "removes the Content-Type header and body when response status is #{status_code}" do
|
32
|
-
@response.status = status_code
|
33
|
-
@response.body = ['Hello World']
|
34
|
-
assert_equal [status_code, {}, []], @response.finish
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'Calculates the Content-Length using the bytesize of the body' do
|
39
|
-
@response.body = ['Hello', 'World!', '✈']
|
40
|
-
_, headers, body = @response.finish
|
41
|
-
assert_equal '14', headers['Content-Length']
|
42
|
-
assert_same_body @response.body, body
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'does not call #to_ary or #inject on the body' do
|
46
|
-
object = Object.new
|
47
|
-
def object.inject(*) fail 'called' end
|
48
|
-
def object.to_ary(*) fail 'called' end
|
49
|
-
def object.each(*) end
|
50
|
-
@response.body = object
|
51
|
-
assert @response.finish
|
52
|
-
end
|
53
|
-
|
54
|
-
it 'does not nest a Sinatra::Response' do
|
55
|
-
@response.body = Sinatra::Response.new ["foo"]
|
56
|
-
assert_same_body @response.body, ["foo"]
|
57
|
-
end
|
58
|
-
|
59
|
-
it 'does not nest a Rack::Response' do
|
60
|
-
@response.body = Rack::Response.new ["foo"]
|
61
|
-
assert_same_body @response.body, ["foo"]
|
62
|
-
end
|
63
|
-
end
|
data/test/result_test.rb
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
require File.expand_path('../helper', __FILE__)
|
2
|
-
|
3
|
-
class ResultTest < Minitest::Test
|
4
|
-
it "sets response.body when result is a String" do
|
5
|
-
mock_app { get('/') { 'Hello World' } }
|
6
|
-
|
7
|
-
get '/'
|
8
|
-
assert ok?
|
9
|
-
assert_equal 'Hello World', body
|
10
|
-
end
|
11
|
-
|
12
|
-
it "sets response.body when result is an Array of Strings" do
|
13
|
-
mock_app { get('/') { ['Hello', 'World'] } }
|
14
|
-
|
15
|
-
get '/'
|
16
|
-
assert ok?
|
17
|
-
assert_equal 'HelloWorld', body
|
18
|
-
end
|
19
|
-
|
20
|
-
it "sets response.body when result responds to #each" do
|
21
|
-
mock_app do
|
22
|
-
get('/') do
|
23
|
-
res = lambda { 'Hello World' }
|
24
|
-
def res.each ; yield call ; end
|
25
|
-
return res
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
get '/'
|
30
|
-
assert ok?
|
31
|
-
assert_equal 'Hello World', body
|
32
|
-
end
|
33
|
-
|
34
|
-
it "sets response.body to [] when result is nil" do
|
35
|
-
mock_app { get( '/') { nil } }
|
36
|
-
|
37
|
-
get '/'
|
38
|
-
assert ok?
|
39
|
-
assert_equal '', body
|
40
|
-
end
|
41
|
-
|
42
|
-
it "sets status, headers, and body when result is a Rack response tuple" do
|
43
|
-
mock_app {
|
44
|
-
get('/') { [203, {'Content-Type' => 'foo/bar'}, 'Hello World'] }
|
45
|
-
}
|
46
|
-
|
47
|
-
get '/'
|
48
|
-
assert_equal 203, status
|
49
|
-
assert_equal 'foo/bar', response['Content-Type']
|
50
|
-
assert_equal 'Hello World', body
|
51
|
-
end
|
52
|
-
|
53
|
-
it "sets status and body when result is a two-tuple" do
|
54
|
-
mock_app { get('/') { [409, 'formula of'] } }
|
55
|
-
|
56
|
-
get '/'
|
57
|
-
assert_equal 409, status
|
58
|
-
assert_equal 'formula of', body
|
59
|
-
end
|
60
|
-
|
61
|
-
it "raises a ArgumentError when result is a non two or three tuple Array" do
|
62
|
-
mock_app {
|
63
|
-
get('/') { [409, 'formula of', 'something else', 'even more'] }
|
64
|
-
}
|
65
|
-
|
66
|
-
assert_raises(ArgumentError) { get '/' }
|
67
|
-
end
|
68
|
-
|
69
|
-
it "sets status when result is a Fixnum status code" do
|
70
|
-
mock_app { get('/') { 205 } }
|
71
|
-
|
72
|
-
get '/'
|
73
|
-
assert_equal 205, status
|
74
|
-
assert_equal '', body
|
75
|
-
end
|
76
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
require File.expand_path('../helper', __FILE__)
|
2
|
-
|
3
|
-
module RouteAddedTest
|
4
|
-
@routes, @procs = [], []
|
5
|
-
def self.routes ; @routes ; end
|
6
|
-
def self.procs ; @procs ; end
|
7
|
-
def self.route_added(verb, path, proc)
|
8
|
-
@routes << [verb, path]
|
9
|
-
@procs << proc
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
class RouteAddedHookTest < Minitest::Test
|
14
|
-
setup do
|
15
|
-
RouteAddedTest.routes.clear
|
16
|
-
RouteAddedTest.procs.clear
|
17
|
-
end
|
18
|
-
|
19
|
-
it "should be notified of an added route" do
|
20
|
-
mock_app(Class.new(Sinatra::Base)) do
|
21
|
-
register RouteAddedTest
|
22
|
-
get('/') {}
|
23
|
-
end
|
24
|
-
|
25
|
-
assert_equal [["GET", "/"], ["HEAD", "/"]],
|
26
|
-
RouteAddedTest.routes
|
27
|
-
end
|
28
|
-
|
29
|
-
it "should include hooks from superclass" do
|
30
|
-
a = Class.new(Class.new(Sinatra::Base))
|
31
|
-
b = Class.new(a)
|
32
|
-
|
33
|
-
a.register RouteAddedTest
|
34
|
-
b.class_eval { post("/sub_app_route") {} }
|
35
|
-
|
36
|
-
assert_equal [["POST", "/sub_app_route"]],
|
37
|
-
RouteAddedTest.routes
|
38
|
-
end
|
39
|
-
|
40
|
-
it "should only run once per extension" do
|
41
|
-
mock_app(Class.new(Sinatra::Base)) do
|
42
|
-
register RouteAddedTest
|
43
|
-
register RouteAddedTest
|
44
|
-
get('/') {}
|
45
|
-
end
|
46
|
-
|
47
|
-
assert_equal [["GET", "/"], ["HEAD", "/"]],
|
48
|
-
RouteAddedTest.routes
|
49
|
-
end
|
50
|
-
|
51
|
-
it "should pass route blocks as an argument" do
|
52
|
-
mock_app(Class.new(Sinatra::Base)) do
|
53
|
-
register RouteAddedTest
|
54
|
-
get('/') {}
|
55
|
-
end
|
56
|
-
|
57
|
-
assert_kind_of Proc, RouteAddedTest.procs.first
|
58
|
-
end
|
59
|
-
end
|
data/test/routing_test.rb
DELETED
@@ -1,1456 +0,0 @@
|
|
1
|
-
# I like coding: UTF-8
|
2
|
-
require File.expand_path('../helper', __FILE__)
|
3
|
-
|
4
|
-
# Helper method for easy route pattern matching testing
|
5
|
-
def route_def(pattern)
|
6
|
-
mock_app { get(pattern) { } }
|
7
|
-
end
|
8
|
-
|
9
|
-
class RegexpLookAlike
|
10
|
-
class MatchData
|
11
|
-
def captures
|
12
|
-
["this", "is", "a", "test"]
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def match(string)
|
17
|
-
::RegexpLookAlike::MatchData.new if string == "/this/is/a/test/"
|
18
|
-
end
|
19
|
-
|
20
|
-
def keys
|
21
|
-
["one", "two", "three", "four"]
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class RoutingTest < Minitest::Test
|
26
|
-
%w[get put post delete options patch link unlink].each do |verb|
|
27
|
-
it "defines #{verb.upcase} request handlers with #{verb}" do
|
28
|
-
mock_app {
|
29
|
-
send verb, '/hello' do
|
30
|
-
'Hello World'
|
31
|
-
end
|
32
|
-
}
|
33
|
-
|
34
|
-
request = Rack::MockRequest.new(@app)
|
35
|
-
response = request.request(verb.upcase, '/hello', {})
|
36
|
-
assert response.ok?
|
37
|
-
assert_equal 'Hello World', response.body
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
it "defines HEAD request handlers with HEAD" do
|
42
|
-
mock_app {
|
43
|
-
head '/hello' do
|
44
|
-
response['X-Hello'] = 'World!'
|
45
|
-
'remove me'
|
46
|
-
end
|
47
|
-
}
|
48
|
-
|
49
|
-
request = Rack::MockRequest.new(@app)
|
50
|
-
response = request.request('HEAD', '/hello', {})
|
51
|
-
assert response.ok?
|
52
|
-
assert_equal 'World!', response['X-Hello']
|
53
|
-
assert_equal '', response.body
|
54
|
-
end
|
55
|
-
|
56
|
-
it "404s when no route satisfies the request" do
|
57
|
-
mock_app {
|
58
|
-
get('/foo') { }
|
59
|
-
}
|
60
|
-
get '/bar'
|
61
|
-
assert_equal 404, status
|
62
|
-
end
|
63
|
-
|
64
|
-
it "404s and sets X-Cascade header when no route satisfies the request" do
|
65
|
-
mock_app {
|
66
|
-
get('/foo') { }
|
67
|
-
}
|
68
|
-
get '/bar'
|
69
|
-
assert_equal 404, status
|
70
|
-
assert_equal 'pass', response.headers['X-Cascade']
|
71
|
-
end
|
72
|
-
|
73
|
-
it "404s and does not set X-Cascade header when no route satisfies the request and x_cascade has been disabled" do
|
74
|
-
mock_app {
|
75
|
-
disable :x_cascade
|
76
|
-
get('/foo') { }
|
77
|
-
}
|
78
|
-
get '/bar'
|
79
|
-
assert_equal 404, status
|
80
|
-
assert_equal nil, response.headers['X-Cascade']
|
81
|
-
end
|
82
|
-
|
83
|
-
|
84
|
-
it "allows using unicode" do
|
85
|
-
mock_app do
|
86
|
-
get('/föö') { }
|
87
|
-
end
|
88
|
-
get '/f%C3%B6%C3%B6'
|
89
|
-
assert_equal 200, status
|
90
|
-
end
|
91
|
-
|
92
|
-
it "it handles encoded slashes correctly" do
|
93
|
-
mock_app {
|
94
|
-
set :protection, :except => :path_traversal
|
95
|
-
get("/:a") { |a| a }
|
96
|
-
}
|
97
|
-
get '/foo%2Fbar'
|
98
|
-
assert_equal 200, status
|
99
|
-
assert_body "foo/bar"
|
100
|
-
end
|
101
|
-
|
102
|
-
it "it handles encoded colons correctly" do
|
103
|
-
mock_app {
|
104
|
-
get("/:") { 'a' }
|
105
|
-
get("/a/:") { 'b' }
|
106
|
-
get("/a/:/b") { 'c' }
|
107
|
-
get("/a/b:") { 'd' }
|
108
|
-
get("/a/b: ") { 'e' }
|
109
|
-
}
|
110
|
-
get '/:'
|
111
|
-
assert_equal 200, status
|
112
|
-
assert_body "a"
|
113
|
-
get '/%3a'
|
114
|
-
assert_equal 200, status
|
115
|
-
assert_body "a"
|
116
|
-
|
117
|
-
get '/a/:'
|
118
|
-
assert_equal 200, status
|
119
|
-
assert_body "b"
|
120
|
-
get '/a/%3a'
|
121
|
-
assert_equal 200, status
|
122
|
-
assert_body "b"
|
123
|
-
|
124
|
-
get '/a/:/b'
|
125
|
-
assert_equal 200, status
|
126
|
-
assert_body "c"
|
127
|
-
get '/a/%3A/b'
|
128
|
-
assert_equal 200, status
|
129
|
-
assert_body "c"
|
130
|
-
|
131
|
-
get '/a/b:'
|
132
|
-
assert_equal 200, status
|
133
|
-
assert_body "d"
|
134
|
-
get '/a/b%3a'
|
135
|
-
assert_equal 200, status
|
136
|
-
assert_body "d"
|
137
|
-
|
138
|
-
get '/a/b%3a%20'
|
139
|
-
assert_equal 200, status
|
140
|
-
assert_body "e"
|
141
|
-
get '/a/b%3a+'
|
142
|
-
assert_equal 200, status
|
143
|
-
assert_body "e"
|
144
|
-
end
|
145
|
-
|
146
|
-
it "overrides the content-type in error handlers" do
|
147
|
-
mock_app {
|
148
|
-
before { content_type 'text/plain' }
|
149
|
-
error Sinatra::NotFound do
|
150
|
-
content_type "text/html"
|
151
|
-
"<h1>Not Found</h1>"
|
152
|
-
end
|
153
|
-
}
|
154
|
-
|
155
|
-
get '/foo'
|
156
|
-
assert_equal 404, status
|
157
|
-
assert_equal 'text/html;charset=utf-8', response["Content-Type"]
|
158
|
-
assert_equal "<h1>Not Found</h1>", response.body
|
159
|
-
end
|
160
|
-
|
161
|
-
it "recalculates body length correctly for 404 response" do
|
162
|
-
mock_app {
|
163
|
-
get '/' do
|
164
|
-
@response["Content-Length"] = "30"
|
165
|
-
raise Sinatra::NotFound
|
166
|
-
end
|
167
|
-
}
|
168
|
-
|
169
|
-
get "/"
|
170
|
-
assert_equal "18", response["Content-Length"]
|
171
|
-
assert_equal 404, status
|
172
|
-
end
|
173
|
-
|
174
|
-
it 'matches empty PATH_INFO to "/" if no route is defined for ""' do
|
175
|
-
mock_app do
|
176
|
-
get '/' do
|
177
|
-
'worked'
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
get '/', {}, "PATH_INFO" => ""
|
182
|
-
assert ok?
|
183
|
-
assert_equal 'worked', body
|
184
|
-
end
|
185
|
-
|
186
|
-
it 'matches empty PATH_INFO to "" if a route is defined for ""' do
|
187
|
-
mock_app do
|
188
|
-
disable :protection
|
189
|
-
|
190
|
-
get '/' do
|
191
|
-
'did not work'
|
192
|
-
end
|
193
|
-
|
194
|
-
get '' do
|
195
|
-
'worked'
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
get '/', {}, "PATH_INFO" => ""
|
200
|
-
assert ok?
|
201
|
-
assert_equal 'worked', body
|
202
|
-
end
|
203
|
-
|
204
|
-
it 'takes multiple definitions of a route' do
|
205
|
-
mock_app {
|
206
|
-
user_agent(/Foo/)
|
207
|
-
get '/foo' do
|
208
|
-
'foo'
|
209
|
-
end
|
210
|
-
|
211
|
-
get '/foo' do
|
212
|
-
'not foo'
|
213
|
-
end
|
214
|
-
}
|
215
|
-
|
216
|
-
get '/foo', {}, 'HTTP_USER_AGENT' => 'Foo'
|
217
|
-
assert ok?
|
218
|
-
assert_equal 'foo', body
|
219
|
-
|
220
|
-
get '/foo'
|
221
|
-
assert ok?
|
222
|
-
assert_equal 'not foo', body
|
223
|
-
end
|
224
|
-
|
225
|
-
it "exposes params with indifferent hash" do
|
226
|
-
mock_app {
|
227
|
-
get '/:foo' do
|
228
|
-
assert_equal 'bar', params['foo']
|
229
|
-
assert_equal 'bar', params[:foo]
|
230
|
-
'well, alright'
|
231
|
-
end
|
232
|
-
}
|
233
|
-
get '/bar'
|
234
|
-
assert_equal 'well, alright', body
|
235
|
-
end
|
236
|
-
|
237
|
-
it "merges named params and query string params in params" do
|
238
|
-
mock_app {
|
239
|
-
get '/:foo' do
|
240
|
-
assert_equal 'bar', params['foo']
|
241
|
-
assert_equal 'biz', params['baz']
|
242
|
-
end
|
243
|
-
}
|
244
|
-
get '/bar?baz=biz'
|
245
|
-
assert ok?
|
246
|
-
end
|
247
|
-
|
248
|
-
it "supports named params like /hello/:person" do
|
249
|
-
mock_app {
|
250
|
-
get '/hello/:person' do
|
251
|
-
"Hello #{params['person']}"
|
252
|
-
end
|
253
|
-
}
|
254
|
-
get '/hello/Frank'
|
255
|
-
assert_equal 'Hello Frank', body
|
256
|
-
end
|
257
|
-
|
258
|
-
it "supports optional named params like /?:foo?/?:bar?" do
|
259
|
-
mock_app {
|
260
|
-
get '/?:foo?/?:bar?' do
|
261
|
-
"foo=#{params[:foo]};bar=#{params[:bar]}"
|
262
|
-
end
|
263
|
-
}
|
264
|
-
|
265
|
-
get '/hello/world'
|
266
|
-
assert ok?
|
267
|
-
assert_equal "foo=hello;bar=world", body
|
268
|
-
|
269
|
-
get '/hello'
|
270
|
-
assert ok?
|
271
|
-
assert_equal "foo=hello;bar=", body
|
272
|
-
|
273
|
-
get '/'
|
274
|
-
assert ok?
|
275
|
-
assert_equal "foo=;bar=", body
|
276
|
-
end
|
277
|
-
|
278
|
-
it "supports named captures like %r{/hello/(?<person>[^/?#]+)} on Ruby >= 1.9" do
|
279
|
-
next if RUBY_VERSION < '1.9'
|
280
|
-
mock_app {
|
281
|
-
get Regexp.new('/hello/(?<person>[^/?#]+)') do
|
282
|
-
"Hello #{params['person']}"
|
283
|
-
end
|
284
|
-
}
|
285
|
-
get '/hello/Frank'
|
286
|
-
assert_equal 'Hello Frank', body
|
287
|
-
end
|
288
|
-
|
289
|
-
it "supports optional named captures like %r{/page(?<format>.[^/?#]+)?} on Ruby >= 1.9" do
|
290
|
-
next if RUBY_VERSION < '1.9'
|
291
|
-
mock_app {
|
292
|
-
get Regexp.new('/page(?<format>.[^/?#]+)?') do
|
293
|
-
"format=#{params[:format]}"
|
294
|
-
end
|
295
|
-
}
|
296
|
-
|
297
|
-
get '/page.html'
|
298
|
-
assert ok?
|
299
|
-
assert_equal "format=.html", body
|
300
|
-
|
301
|
-
get '/page.xml'
|
302
|
-
assert ok?
|
303
|
-
assert_equal "format=.xml", body
|
304
|
-
|
305
|
-
get '/page'
|
306
|
-
assert ok?
|
307
|
-
assert_equal "format=", body
|
308
|
-
end
|
309
|
-
|
310
|
-
it 'does not concatenate params with the same name' do
|
311
|
-
mock_app { get('/:foo') { params[:foo] } }
|
312
|
-
get '/a?foo=b'
|
313
|
-
assert_body 'a'
|
314
|
-
end
|
315
|
-
|
316
|
-
it "supports single splat params like /*" do
|
317
|
-
mock_app {
|
318
|
-
get '/*' do
|
319
|
-
assert params['splat'].kind_of?(Array)
|
320
|
-
params['splat'].join "\n"
|
321
|
-
end
|
322
|
-
}
|
323
|
-
|
324
|
-
get '/foo'
|
325
|
-
assert_equal "foo", body
|
326
|
-
|
327
|
-
get '/foo/bar/baz'
|
328
|
-
assert_equal "foo/bar/baz", body
|
329
|
-
end
|
330
|
-
|
331
|
-
it "supports mixing multiple splat params like /*/foo/*/*" do
|
332
|
-
mock_app {
|
333
|
-
get '/*/foo/*/*' do
|
334
|
-
assert params['splat'].kind_of?(Array)
|
335
|
-
params['splat'].join "\n"
|
336
|
-
end
|
337
|
-
}
|
338
|
-
|
339
|
-
get '/bar/foo/bling/baz/boom'
|
340
|
-
assert_equal "bar\nbling\nbaz/boom", body
|
341
|
-
|
342
|
-
get '/bar/foo/baz'
|
343
|
-
assert not_found?
|
344
|
-
end
|
345
|
-
|
346
|
-
it "supports mixing named and splat params like /:foo/*" do
|
347
|
-
mock_app {
|
348
|
-
get '/:foo/*' do
|
349
|
-
assert_equal 'foo', params['foo']
|
350
|
-
assert_equal ['bar/baz'], params['splat']
|
351
|
-
end
|
352
|
-
}
|
353
|
-
|
354
|
-
get '/foo/bar/baz'
|
355
|
-
assert ok?
|
356
|
-
end
|
357
|
-
|
358
|
-
it "matches a dot ('.') as part of a named param" do
|
359
|
-
mock_app {
|
360
|
-
get '/:foo/:bar' do
|
361
|
-
params[:foo]
|
362
|
-
end
|
363
|
-
}
|
364
|
-
|
365
|
-
get '/user@example.com/name'
|
366
|
-
assert_equal 200, response.status
|
367
|
-
assert_equal 'user@example.com', body
|
368
|
-
end
|
369
|
-
|
370
|
-
it "matches a literal dot ('.') outside of named params" do
|
371
|
-
mock_app {
|
372
|
-
get '/:file.:ext' do
|
373
|
-
assert_equal 'pony', params[:file]
|
374
|
-
assert_equal 'jpg', params[:ext]
|
375
|
-
'right on'
|
376
|
-
end
|
377
|
-
}
|
378
|
-
|
379
|
-
get '/pony.jpg'
|
380
|
-
assert_equal 200, response.status
|
381
|
-
assert_equal 'right on', body
|
382
|
-
end
|
383
|
-
|
384
|
-
it "literally matches dot in paths" do
|
385
|
-
route_def '/test.bar'
|
386
|
-
|
387
|
-
get '/test.bar'
|
388
|
-
assert ok?
|
389
|
-
get 'test0bar'
|
390
|
-
assert not_found?
|
391
|
-
end
|
392
|
-
|
393
|
-
it "literally matches dollar sign in paths" do
|
394
|
-
route_def '/test$/'
|
395
|
-
|
396
|
-
get '/test$/'
|
397
|
-
assert ok?
|
398
|
-
end
|
399
|
-
|
400
|
-
it "literally matches plus sign in paths" do
|
401
|
-
route_def '/te+st/'
|
402
|
-
|
403
|
-
get '/te%2Bst/'
|
404
|
-
assert ok?
|
405
|
-
get '/teeeeeeest/'
|
406
|
-
assert not_found?
|
407
|
-
end
|
408
|
-
|
409
|
-
it "does not convert plus sign into space as the value of a named param" do
|
410
|
-
mock_app do
|
411
|
-
get '/:test' do
|
412
|
-
params["test"]
|
413
|
-
end
|
414
|
-
end
|
415
|
-
get '/bob+ross'
|
416
|
-
assert ok?
|
417
|
-
assert_equal 'bob+ross', body
|
418
|
-
end
|
419
|
-
|
420
|
-
it "literally matches parens in paths" do
|
421
|
-
route_def '/test(bar)/'
|
422
|
-
|
423
|
-
get '/test(bar)/'
|
424
|
-
assert ok?
|
425
|
-
end
|
426
|
-
|
427
|
-
it "supports basic nested params" do
|
428
|
-
mock_app {
|
429
|
-
get '/hi' do
|
430
|
-
params["person"]["name"]
|
431
|
-
end
|
432
|
-
}
|
433
|
-
|
434
|
-
get "/hi?person[name]=John+Doe"
|
435
|
-
assert ok?
|
436
|
-
assert_equal "John Doe", body
|
437
|
-
end
|
438
|
-
|
439
|
-
it "exposes nested params with indifferent hash" do
|
440
|
-
mock_app {
|
441
|
-
get '/testme' do
|
442
|
-
assert_equal 'baz', params['bar']['foo']
|
443
|
-
assert_equal 'baz', params['bar'][:foo]
|
444
|
-
'well, alright'
|
445
|
-
end
|
446
|
-
}
|
447
|
-
get '/testme?bar[foo]=baz'
|
448
|
-
assert_equal 'well, alright', body
|
449
|
-
end
|
450
|
-
|
451
|
-
it "exposes params nested within arrays with indifferent hash" do
|
452
|
-
mock_app {
|
453
|
-
get '/testme' do
|
454
|
-
assert_equal 'baz', params['bar'][0]['foo']
|
455
|
-
assert_equal 'baz', params['bar'][0][:foo]
|
456
|
-
'well, alright'
|
457
|
-
end
|
458
|
-
}
|
459
|
-
get '/testme?bar[][foo]=baz'
|
460
|
-
assert_equal 'well, alright', body
|
461
|
-
end
|
462
|
-
|
463
|
-
it "supports arrays within params" do
|
464
|
-
mock_app {
|
465
|
-
get '/foo' do
|
466
|
-
assert_equal ['A', 'B'], params['bar']
|
467
|
-
'looks good'
|
468
|
-
end
|
469
|
-
}
|
470
|
-
get '/foo?bar[]=A&bar[]=B'
|
471
|
-
assert ok?
|
472
|
-
assert_equal 'looks good', body
|
473
|
-
end
|
474
|
-
|
475
|
-
it "supports deeply nested params" do
|
476
|
-
expected_params = {
|
477
|
-
"emacs" => {
|
478
|
-
"map" => { "goto-line" => "M-g g" },
|
479
|
-
"version" => "22.3.1"
|
480
|
-
},
|
481
|
-
"browser" => {
|
482
|
-
"firefox" => {"engine" => {"name"=>"spidermonkey", "version"=>"1.7.0"}},
|
483
|
-
"chrome" => {"engine" => {"name"=>"V8", "version"=>"1.0"}}
|
484
|
-
},
|
485
|
-
"paste" => {"name"=>"hello world", "syntax"=>"ruby"}
|
486
|
-
}
|
487
|
-
mock_app {
|
488
|
-
get '/foo' do
|
489
|
-
assert_equal expected_params, params
|
490
|
-
'looks good'
|
491
|
-
end
|
492
|
-
}
|
493
|
-
get '/foo', expected_params
|
494
|
-
assert ok?
|
495
|
-
assert_equal 'looks good', body
|
496
|
-
end
|
497
|
-
|
498
|
-
it "preserves non-nested params" do
|
499
|
-
mock_app {
|
500
|
-
get '/foo' do
|
501
|
-
assert_equal "2", params["article_id"]
|
502
|
-
assert_equal "awesome", params['comment']['body']
|
503
|
-
assert_nil params['comment[body]']
|
504
|
-
'looks good'
|
505
|
-
end
|
506
|
-
}
|
507
|
-
|
508
|
-
get '/foo?article_id=2&comment[body]=awesome'
|
509
|
-
assert ok?
|
510
|
-
assert_equal 'looks good', body
|
511
|
-
end
|
512
|
-
|
513
|
-
it "matches paths that include spaces encoded with %20" do
|
514
|
-
mock_app {
|
515
|
-
get '/path with spaces' do
|
516
|
-
'looks good'
|
517
|
-
end
|
518
|
-
}
|
519
|
-
|
520
|
-
get '/path%20with%20spaces'
|
521
|
-
assert ok?
|
522
|
-
assert_equal 'looks good', body
|
523
|
-
end
|
524
|
-
|
525
|
-
it "matches paths that include spaces encoded with +" do
|
526
|
-
mock_app {
|
527
|
-
get '/path with spaces' do
|
528
|
-
'looks good'
|
529
|
-
end
|
530
|
-
}
|
531
|
-
|
532
|
-
get '/path+with+spaces'
|
533
|
-
assert ok?
|
534
|
-
assert_equal 'looks good', body
|
535
|
-
end
|
536
|
-
|
537
|
-
it "matches paths that include ampersands" do
|
538
|
-
mock_app {
|
539
|
-
get '/:name' do
|
540
|
-
'looks good'
|
541
|
-
end
|
542
|
-
}
|
543
|
-
|
544
|
-
get '/foo&bar'
|
545
|
-
assert ok?
|
546
|
-
assert_equal 'looks good', body
|
547
|
-
end
|
548
|
-
|
549
|
-
it "URL decodes named parameters and splats" do
|
550
|
-
mock_app {
|
551
|
-
get '/:foo/*' do
|
552
|
-
assert_equal 'hello world', params['foo']
|
553
|
-
assert_equal ['how are you'], params['splat']
|
554
|
-
nil
|
555
|
-
end
|
556
|
-
}
|
557
|
-
|
558
|
-
get '/hello%20world/how%20are%20you'
|
559
|
-
assert ok?
|
560
|
-
end
|
561
|
-
|
562
|
-
it 'supports regular expressions' do
|
563
|
-
mock_app {
|
564
|
-
get(/^\/foo...\/bar$/) do
|
565
|
-
'Hello World'
|
566
|
-
end
|
567
|
-
}
|
568
|
-
|
569
|
-
get '/foooom/bar'
|
570
|
-
assert ok?
|
571
|
-
assert_equal 'Hello World', body
|
572
|
-
end
|
573
|
-
|
574
|
-
it 'makes regular expression captures available in params[:captures]' do
|
575
|
-
mock_app {
|
576
|
-
get(/^\/fo(.*)\/ba(.*)/) do
|
577
|
-
assert_equal ['orooomma', 'f'], params[:captures]
|
578
|
-
'right on'
|
579
|
-
end
|
580
|
-
}
|
581
|
-
|
582
|
-
get '/foorooomma/baf'
|
583
|
-
assert ok?
|
584
|
-
assert_equal 'right on', body
|
585
|
-
end
|
586
|
-
|
587
|
-
it 'supports regular expression look-alike routes' do
|
588
|
-
mock_app {
|
589
|
-
get(RegexpLookAlike.new) do
|
590
|
-
assert_equal 'this', params[:one]
|
591
|
-
assert_equal 'is', params[:two]
|
592
|
-
assert_equal 'a', params[:three]
|
593
|
-
assert_equal 'test', params[:four]
|
594
|
-
'right on'
|
595
|
-
end
|
596
|
-
}
|
597
|
-
|
598
|
-
get '/this/is/a/test/'
|
599
|
-
assert ok?
|
600
|
-
assert_equal 'right on', body
|
601
|
-
end
|
602
|
-
|
603
|
-
it 'raises a TypeError when pattern is not a String or Regexp' do
|
604
|
-
assert_raises(TypeError) {
|
605
|
-
mock_app { get(42){} }
|
606
|
-
}
|
607
|
-
end
|
608
|
-
|
609
|
-
it "returns response immediately on halt" do
|
610
|
-
mock_app {
|
611
|
-
get '/' do
|
612
|
-
halt 'Hello World'
|
613
|
-
'Boo-hoo World'
|
614
|
-
end
|
615
|
-
}
|
616
|
-
|
617
|
-
get '/'
|
618
|
-
assert ok?
|
619
|
-
assert_equal 'Hello World', body
|
620
|
-
end
|
621
|
-
|
622
|
-
it "halts with a response tuple" do
|
623
|
-
mock_app {
|
624
|
-
get '/' do
|
625
|
-
halt 295, {'Content-Type' => 'text/plain'}, 'Hello World'
|
626
|
-
end
|
627
|
-
}
|
628
|
-
|
629
|
-
get '/'
|
630
|
-
assert_equal 295, status
|
631
|
-
assert_equal 'text/plain', response['Content-Type']
|
632
|
-
assert_equal 'Hello World', body
|
633
|
-
end
|
634
|
-
|
635
|
-
it "halts with an array of strings" do
|
636
|
-
mock_app {
|
637
|
-
get '/' do
|
638
|
-
halt %w[Hello World How Are You]
|
639
|
-
end
|
640
|
-
}
|
641
|
-
|
642
|
-
get '/'
|
643
|
-
assert_equal 'HelloWorldHowAreYou', body
|
644
|
-
end
|
645
|
-
|
646
|
-
it 'sets response.status with halt' do
|
647
|
-
status_was = nil
|
648
|
-
mock_app do
|
649
|
-
after { status_was = status }
|
650
|
-
get('/') { halt 500, 'error' }
|
651
|
-
end
|
652
|
-
get '/'
|
653
|
-
assert_status 500
|
654
|
-
assert_equal 500, status_was
|
655
|
-
end
|
656
|
-
|
657
|
-
it "transitions to the next matching route on pass" do
|
658
|
-
mock_app {
|
659
|
-
get '/:foo' do
|
660
|
-
pass
|
661
|
-
'Hello Foo'
|
662
|
-
end
|
663
|
-
|
664
|
-
get '/*' do
|
665
|
-
assert !params.include?('foo')
|
666
|
-
'Hello World'
|
667
|
-
end
|
668
|
-
}
|
669
|
-
|
670
|
-
get '/bar'
|
671
|
-
assert ok?
|
672
|
-
assert_equal 'Hello World', body
|
673
|
-
end
|
674
|
-
|
675
|
-
it "transitions to 404 when passed and no subsequent route matches" do
|
676
|
-
mock_app {
|
677
|
-
get '/:foo' do
|
678
|
-
pass
|
679
|
-
'Hello Foo'
|
680
|
-
end
|
681
|
-
}
|
682
|
-
|
683
|
-
get '/bar'
|
684
|
-
assert not_found?
|
685
|
-
end
|
686
|
-
|
687
|
-
it "transitions to 404 and sets X-Cascade header when passed and no subsequent route matches" do
|
688
|
-
mock_app {
|
689
|
-
get '/:foo' do
|
690
|
-
pass
|
691
|
-
'Hello Foo'
|
692
|
-
end
|
693
|
-
|
694
|
-
get '/bar' do
|
695
|
-
'Hello Bar'
|
696
|
-
end
|
697
|
-
}
|
698
|
-
|
699
|
-
get '/foo'
|
700
|
-
assert not_found?
|
701
|
-
assert_equal 'pass', response.headers['X-Cascade']
|
702
|
-
end
|
703
|
-
|
704
|
-
it "uses optional block passed to pass as route block if no other route is found" do
|
705
|
-
mock_app {
|
706
|
-
get "/" do
|
707
|
-
pass do
|
708
|
-
"this"
|
709
|
-
end
|
710
|
-
"not this"
|
711
|
-
end
|
712
|
-
}
|
713
|
-
|
714
|
-
get "/"
|
715
|
-
assert ok?
|
716
|
-
assert "this", body
|
717
|
-
end
|
718
|
-
|
719
|
-
it "uses optional block passed to pass as route block if no other route is found and superclass has non-matching routes" do
|
720
|
-
base = Class.new(Sinatra::Base)
|
721
|
-
base.get('/foo') { 'foo in baseclass' }
|
722
|
-
|
723
|
-
mock_app(base) {
|
724
|
-
get "/" do
|
725
|
-
pass do
|
726
|
-
"this"
|
727
|
-
end
|
728
|
-
"not this"
|
729
|
-
end
|
730
|
-
}
|
731
|
-
|
732
|
-
get "/"
|
733
|
-
assert_equal 200, status
|
734
|
-
assert "this", body
|
735
|
-
end
|
736
|
-
|
737
|
-
it "passes when matching condition returns false" do
|
738
|
-
mock_app {
|
739
|
-
condition { params[:foo] == 'bar' }
|
740
|
-
get '/:foo' do
|
741
|
-
'Hello World'
|
742
|
-
end
|
743
|
-
}
|
744
|
-
|
745
|
-
get '/bar'
|
746
|
-
assert ok?
|
747
|
-
assert_equal 'Hello World', body
|
748
|
-
|
749
|
-
get '/foo'
|
750
|
-
assert not_found?
|
751
|
-
end
|
752
|
-
|
753
|
-
it "does not pass when matching condition returns nil" do
|
754
|
-
mock_app {
|
755
|
-
condition { nil }
|
756
|
-
get '/:foo' do
|
757
|
-
'Hello World'
|
758
|
-
end
|
759
|
-
}
|
760
|
-
|
761
|
-
get '/bar'
|
762
|
-
assert ok?
|
763
|
-
assert_equal 'Hello World', body
|
764
|
-
end
|
765
|
-
|
766
|
-
it "passes to next route when condition calls pass explicitly" do
|
767
|
-
mock_app {
|
768
|
-
condition { pass unless params[:foo] == 'bar' }
|
769
|
-
get '/:foo' do
|
770
|
-
'Hello World'
|
771
|
-
end
|
772
|
-
}
|
773
|
-
|
774
|
-
get '/bar'
|
775
|
-
assert ok?
|
776
|
-
assert_equal 'Hello World', body
|
777
|
-
|
778
|
-
get '/foo'
|
779
|
-
assert not_found?
|
780
|
-
end
|
781
|
-
|
782
|
-
it "passes to the next route when host_name does not match" do
|
783
|
-
mock_app {
|
784
|
-
host_name 'example.com'
|
785
|
-
get '/foo' do
|
786
|
-
'Hello World'
|
787
|
-
end
|
788
|
-
}
|
789
|
-
get '/foo'
|
790
|
-
assert not_found?
|
791
|
-
|
792
|
-
get '/foo', {}, { 'HTTP_HOST' => 'example.com' }
|
793
|
-
assert_equal 200, status
|
794
|
-
assert_equal 'Hello World', body
|
795
|
-
end
|
796
|
-
|
797
|
-
it "passes to the next route when user_agent does not match" do
|
798
|
-
mock_app {
|
799
|
-
user_agent(/Foo/)
|
800
|
-
get '/foo' do
|
801
|
-
'Hello World'
|
802
|
-
end
|
803
|
-
}
|
804
|
-
get '/foo'
|
805
|
-
assert not_found?
|
806
|
-
|
807
|
-
get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' }
|
808
|
-
assert_equal 200, status
|
809
|
-
assert_equal 'Hello World', body
|
810
|
-
end
|
811
|
-
|
812
|
-
it "treats missing user agent like an empty string" do
|
813
|
-
mock_app do
|
814
|
-
user_agent(/.*/)
|
815
|
-
get '/' do
|
816
|
-
"Hello World"
|
817
|
-
end
|
818
|
-
end
|
819
|
-
get '/'
|
820
|
-
assert_equal 200, status
|
821
|
-
assert_equal 'Hello World', body
|
822
|
-
end
|
823
|
-
|
824
|
-
it "makes captures in user agent pattern available in params[:agent]" do
|
825
|
-
mock_app {
|
826
|
-
user_agent(/Foo (.*)/)
|
827
|
-
get '/foo' do
|
828
|
-
'Hello ' + params[:agent].first
|
829
|
-
end
|
830
|
-
}
|
831
|
-
get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' }
|
832
|
-
assert_equal 200, status
|
833
|
-
assert_equal 'Hello Bar', body
|
834
|
-
end
|
835
|
-
|
836
|
-
it 'matches mime_types with dots, hyphens and plus signs' do
|
837
|
-
mime_types = %w(
|
838
|
-
application/atom+xml
|
839
|
-
application/ecmascript
|
840
|
-
application/EDI-X12
|
841
|
-
application/EDIFACT
|
842
|
-
application/json
|
843
|
-
application/javascript
|
844
|
-
application/octet-stream
|
845
|
-
application/ogg
|
846
|
-
application/pdf
|
847
|
-
application/postscript
|
848
|
-
application/rdf+xml
|
849
|
-
application/rss+xml
|
850
|
-
application/soap+xml
|
851
|
-
application/font-woff
|
852
|
-
application/xhtml+xml
|
853
|
-
application/xml
|
854
|
-
application/xml-dtd
|
855
|
-
application/xop+xml
|
856
|
-
application/zip
|
857
|
-
application/gzip
|
858
|
-
audio/basic
|
859
|
-
audio/L24
|
860
|
-
audio/mp4
|
861
|
-
audio/mpeg
|
862
|
-
audio/ogg
|
863
|
-
audio/vorbis
|
864
|
-
audio/vnd.rn-realaudio
|
865
|
-
audio/vnd.wave
|
866
|
-
audio/webm
|
867
|
-
image/gif
|
868
|
-
image/jpeg
|
869
|
-
image/pjpeg
|
870
|
-
image/png
|
871
|
-
image/svg+xml
|
872
|
-
image/tiff
|
873
|
-
image/vnd.microsoft.icon
|
874
|
-
message/http
|
875
|
-
message/imdn+xml
|
876
|
-
message/partial
|
877
|
-
message/rfc822
|
878
|
-
model/example
|
879
|
-
model/iges
|
880
|
-
model/mesh
|
881
|
-
model/vrml
|
882
|
-
model/x3d+binary
|
883
|
-
model/x3d+vrml
|
884
|
-
model/x3d+xml
|
885
|
-
multipart/mixed
|
886
|
-
multipart/alternative
|
887
|
-
multipart/related
|
888
|
-
multipart/form-data
|
889
|
-
multipart/signed
|
890
|
-
multipart/encrypted
|
891
|
-
text/cmd
|
892
|
-
text/css
|
893
|
-
text/csv
|
894
|
-
text/html
|
895
|
-
text/javascript
|
896
|
-
application/javascript
|
897
|
-
text/plain
|
898
|
-
text/vcard
|
899
|
-
text/xml
|
900
|
-
video/mpeg
|
901
|
-
video/mp4
|
902
|
-
video/ogg
|
903
|
-
video/quicktime
|
904
|
-
video/webm
|
905
|
-
video/x-matroska
|
906
|
-
video/x-ms-wmv
|
907
|
-
video/x-flv
|
908
|
-
application/vnd.oasis.opendocument.text
|
909
|
-
application/vnd.oasis.opendocument.spreadsheet
|
910
|
-
application/vnd.oasis.opendocument.presentation
|
911
|
-
application/vnd.oasis.opendocument.graphics
|
912
|
-
application/vnd.ms-excel
|
913
|
-
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
914
|
-
application/vnd.ms-powerpoint
|
915
|
-
application/vnd.openxmlformats-officedocument.presentationml.presentation
|
916
|
-
application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
917
|
-
application/vnd.mozilla.xul+xml
|
918
|
-
application/vnd.google-earth.kml+xml
|
919
|
-
application/x-deb
|
920
|
-
application/x-dvi
|
921
|
-
application/x-font-ttf
|
922
|
-
application/x-javascript
|
923
|
-
application/x-latex
|
924
|
-
application/x-mpegURL
|
925
|
-
application/x-rar-compressed
|
926
|
-
application/x-shockwave-flash
|
927
|
-
application/x-stuffit
|
928
|
-
application/x-tar
|
929
|
-
application/x-www-form-urlencoded
|
930
|
-
application/x-xpinstall
|
931
|
-
audio/x-aac
|
932
|
-
audio/x-caf
|
933
|
-
image/x-xcf
|
934
|
-
text/x-gwt-rpc
|
935
|
-
text/x-jquery-tmpl
|
936
|
-
application/x-pkcs12
|
937
|
-
application/x-pkcs12
|
938
|
-
application/x-pkcs7-certificates
|
939
|
-
application/x-pkcs7-certificates
|
940
|
-
application/x-pkcs7-certreqresp
|
941
|
-
application/x-pkcs7-mime
|
942
|
-
application/x-pkcs7-mime
|
943
|
-
application/x-pkcs7-signature
|
944
|
-
)
|
945
|
-
|
946
|
-
mime_types.each { |mime_type| assert mime_type.match(Sinatra::Request::HEADER_VALUE_WITH_PARAMS) }
|
947
|
-
end
|
948
|
-
|
949
|
-
it "filters by accept header" do
|
950
|
-
mock_app {
|
951
|
-
get '/', :provides => :xml do
|
952
|
-
env['HTTP_ACCEPT']
|
953
|
-
end
|
954
|
-
get '/foo', :provides => :html do
|
955
|
-
env['HTTP_ACCEPT']
|
956
|
-
end
|
957
|
-
get '/stream', :provides => 'text/event-stream' do
|
958
|
-
env['HTTP_ACCEPT']
|
959
|
-
end
|
960
|
-
}
|
961
|
-
|
962
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
|
963
|
-
assert ok?
|
964
|
-
assert_equal 'application/xml', body
|
965
|
-
assert_equal 'application/xml;charset=utf-8', response.headers['Content-Type']
|
966
|
-
|
967
|
-
get '/', {}, {}
|
968
|
-
assert ok?
|
969
|
-
assert_equal '', body
|
970
|
-
assert_equal 'application/xml;charset=utf-8', response.headers['Content-Type']
|
971
|
-
|
972
|
-
get '/', {}, { 'HTTP_ACCEPT' => '*/*' }
|
973
|
-
assert ok?
|
974
|
-
assert_equal '*/*', body
|
975
|
-
assert_equal 'application/xml;charset=utf-8', response.headers['Content-Type']
|
976
|
-
|
977
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'text/html;q=0.9' }
|
978
|
-
assert !ok?
|
979
|
-
|
980
|
-
get '/foo', {}, { 'HTTP_ACCEPT' => 'text/html;q=0.9' }
|
981
|
-
assert ok?
|
982
|
-
assert_equal 'text/html;q=0.9', body
|
983
|
-
|
984
|
-
get '/foo', {}, { 'HTTP_ACCEPT' => '' }
|
985
|
-
assert ok?
|
986
|
-
assert_equal '', body
|
987
|
-
|
988
|
-
get '/foo', {}, { 'HTTP_ACCEPT' => '*/*' }
|
989
|
-
assert ok?
|
990
|
-
assert_equal '*/*', body
|
991
|
-
|
992
|
-
get '/foo', {}, { 'HTTP_ACCEPT' => 'application/xml' }
|
993
|
-
assert !ok?
|
994
|
-
|
995
|
-
get '/stream', {}, { 'HTTP_ACCEPT' => 'text/event-stream' }
|
996
|
-
assert ok?
|
997
|
-
assert_equal 'text/event-stream', body
|
998
|
-
|
999
|
-
get '/stream', {}, { 'HTTP_ACCEPT' => '' }
|
1000
|
-
assert ok?
|
1001
|
-
assert_equal '', body
|
1002
|
-
|
1003
|
-
get '/stream', {}, { 'HTTP_ACCEPT' => '*/*' }
|
1004
|
-
assert ok?
|
1005
|
-
assert_equal '*/*', body
|
1006
|
-
|
1007
|
-
get '/stream', {}, { 'HTTP_ACCEPT' => 'application/xml' }
|
1008
|
-
assert !ok?
|
1009
|
-
end
|
1010
|
-
|
1011
|
-
it "filters by current Content-Type" do
|
1012
|
-
mock_app do
|
1013
|
-
before('/txt') { content_type :txt }
|
1014
|
-
get('*', :provides => :txt) { 'txt' }
|
1015
|
-
|
1016
|
-
before('/html') { content_type :html }
|
1017
|
-
get('*', :provides => :html) { 'html' }
|
1018
|
-
end
|
1019
|
-
|
1020
|
-
get '/', {}, { 'HTTP_ACCEPT' => '*/*' }
|
1021
|
-
assert ok?
|
1022
|
-
assert_equal 'text/plain;charset=utf-8', response.headers['Content-Type']
|
1023
|
-
assert_body 'txt'
|
1024
|
-
|
1025
|
-
get '/txt', {}, { 'HTTP_ACCEPT' => 'text/plain' }
|
1026
|
-
assert ok?
|
1027
|
-
assert_equal 'text/plain;charset=utf-8', response.headers['Content-Type']
|
1028
|
-
assert_body 'txt'
|
1029
|
-
|
1030
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'text/html' }
|
1031
|
-
assert ok?
|
1032
|
-
assert_equal 'text/html;charset=utf-8', response.headers['Content-Type']
|
1033
|
-
assert_body 'html'
|
1034
|
-
end
|
1035
|
-
|
1036
|
-
it "allows multiple mime types for accept header" do
|
1037
|
-
types = ['image/jpeg', 'image/pjpeg']
|
1038
|
-
|
1039
|
-
mock_app {
|
1040
|
-
get '/', :provides => types do
|
1041
|
-
env['HTTP_ACCEPT']
|
1042
|
-
end
|
1043
|
-
}
|
1044
|
-
|
1045
|
-
types.each do |type|
|
1046
|
-
get '/', {}, { 'HTTP_ACCEPT' => type }
|
1047
|
-
assert ok?
|
1048
|
-
assert_equal type, body
|
1049
|
-
assert_equal type, response.headers['Content-Type']
|
1050
|
-
end
|
1051
|
-
end
|
1052
|
-
|
1053
|
-
it 'respects user agent preferences for the content type' do
|
1054
|
-
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
1055
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5,text/html;q=0.8' }
|
1056
|
-
assert_body 'text/html;charset=utf-8'
|
1057
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.8,text/html;q=0.5' }
|
1058
|
-
assert_body 'image/png'
|
1059
|
-
end
|
1060
|
-
|
1061
|
-
it 'accepts generic types' do
|
1062
|
-
mock_app do
|
1063
|
-
get('/', :provides => :xml) { content_type }
|
1064
|
-
get('/') { 'no match' }
|
1065
|
-
end
|
1066
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'foo/*' }
|
1067
|
-
assert_body 'no match'
|
1068
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'application/*' }
|
1069
|
-
assert_body 'application/xml;charset=utf-8'
|
1070
|
-
get '/', {}, { 'HTTP_ACCEPT' => '*/*' }
|
1071
|
-
assert_body 'application/xml;charset=utf-8'
|
1072
|
-
end
|
1073
|
-
|
1074
|
-
it 'prefers concrete over partly generic types' do
|
1075
|
-
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
1076
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'image/*, text/html' }
|
1077
|
-
assert_body 'text/html;charset=utf-8'
|
1078
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'image/png, text/*' }
|
1079
|
-
assert_body 'image/png'
|
1080
|
-
end
|
1081
|
-
|
1082
|
-
it 'prefers concrete over fully generic types' do
|
1083
|
-
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
1084
|
-
get '/', {}, { 'HTTP_ACCEPT' => '*/*, text/html' }
|
1085
|
-
assert_body 'text/html;charset=utf-8'
|
1086
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'image/png, */*' }
|
1087
|
-
assert_body 'image/png'
|
1088
|
-
end
|
1089
|
-
|
1090
|
-
it 'prefers partly generic over fully generic types' do
|
1091
|
-
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
1092
|
-
get '/', {}, { 'HTTP_ACCEPT' => '*/*, text/*' }
|
1093
|
-
assert_body 'text/html;charset=utf-8'
|
1094
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'image/*, */*' }
|
1095
|
-
assert_body 'image/png'
|
1096
|
-
end
|
1097
|
-
|
1098
|
-
it 'respects quality with generic types' do
|
1099
|
-
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
1100
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'image/*;q=1, text/html;q=0' }
|
1101
|
-
assert_body 'image/png'
|
1102
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5, text/*;q=0.7' }
|
1103
|
-
assert_body 'text/html;charset=utf-8'
|
1104
|
-
end
|
1105
|
-
|
1106
|
-
it 'supplies a default quality of 1.0' do
|
1107
|
-
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
1108
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5, text/*' }
|
1109
|
-
assert_body 'text/html;charset=utf-8'
|
1110
|
-
end
|
1111
|
-
|
1112
|
-
it 'orders types with equal quality by parameter count' do
|
1113
|
-
mock_app do
|
1114
|
-
get('/', :provides => [:png, :jpg]) { content_type }
|
1115
|
-
end
|
1116
|
-
|
1117
|
-
lo_png = 'image/png;q=0.5'
|
1118
|
-
hi_png = 'image/png;q=0.5;profile=FOGRA40;gamma=0.8'
|
1119
|
-
jpeg = 'image/jpeg;q=0.5;compress=0.25'
|
1120
|
-
|
1121
|
-
get '/', {}, { 'HTTP_ACCEPT' => "#{lo_png}, #{jpeg}" }
|
1122
|
-
assert_body 'image/jpeg'
|
1123
|
-
get '/', {}, { 'HTTP_ACCEPT' => "#{hi_png}, #{jpeg}" }
|
1124
|
-
assert_body 'image/png'
|
1125
|
-
end
|
1126
|
-
|
1127
|
-
it 'ignores the quality parameter when ordering by parameter count' do
|
1128
|
-
mock_app do
|
1129
|
-
get('/', :provides => [:png, :jpg]) { content_type }
|
1130
|
-
end
|
1131
|
-
|
1132
|
-
lo_png = 'image/png'
|
1133
|
-
hi_png = 'image/png;profile=FOGRA40;gamma=0.8'
|
1134
|
-
jpeg = 'image/jpeg;q=1.0;compress=0.25'
|
1135
|
-
|
1136
|
-
get '/', {}, { 'HTTP_ACCEPT' => "#{jpeg}, #{lo_png}" }
|
1137
|
-
assert_body 'image/jpeg'
|
1138
|
-
get '/', {}, { 'HTTP_ACCEPT' => "#{jpeg}, #{hi_png}" }
|
1139
|
-
assert_body 'image/png'
|
1140
|
-
end
|
1141
|
-
|
1142
|
-
it 'properly handles quoted strings in parameters' do
|
1143
|
-
mock_app do
|
1144
|
-
get('/', :provides => [:png, :jpg]) { content_type }
|
1145
|
-
end
|
1146
|
-
|
1147
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5;profile=",image/jpeg,"' }
|
1148
|
-
assert_body 'image/png'
|
1149
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5,image/jpeg;q=0;x=";q=1.0"' }
|
1150
|
-
assert_body 'image/png'
|
1151
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5,image/jpeg;q=0;x="\";q=1.0"' }
|
1152
|
-
assert_body 'image/png'
|
1153
|
-
end
|
1154
|
-
|
1155
|
-
it 'accepts both text/javascript and application/javascript for js' do
|
1156
|
-
mock_app { get('/', :provides => :js) { content_type }}
|
1157
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'application/javascript' }
|
1158
|
-
assert_body 'application/javascript;charset=utf-8'
|
1159
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'text/javascript' }
|
1160
|
-
assert_body 'text/javascript;charset=utf-8'
|
1161
|
-
end
|
1162
|
-
|
1163
|
-
it 'accepts both text/xml and application/xml for xml' do
|
1164
|
-
mock_app { get('/', :provides => :xml) { content_type }}
|
1165
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
|
1166
|
-
assert_body 'application/xml;charset=utf-8'
|
1167
|
-
get '/', {}, { 'HTTP_ACCEPT' => 'text/xml' }
|
1168
|
-
assert_body 'text/xml;charset=utf-8'
|
1169
|
-
end
|
1170
|
-
|
1171
|
-
it 'passes a single url param as block parameters when one param is specified' do
|
1172
|
-
mock_app {
|
1173
|
-
get '/:foo' do |foo|
|
1174
|
-
assert_equal 'bar', foo
|
1175
|
-
end
|
1176
|
-
}
|
1177
|
-
|
1178
|
-
get '/bar'
|
1179
|
-
assert ok?
|
1180
|
-
end
|
1181
|
-
|
1182
|
-
it 'passes multiple params as block parameters when many are specified' do
|
1183
|
-
mock_app {
|
1184
|
-
get '/:foo/:bar/:baz' do |foo, bar, baz|
|
1185
|
-
assert_equal 'abc', foo
|
1186
|
-
assert_equal 'def', bar
|
1187
|
-
assert_equal 'ghi', baz
|
1188
|
-
end
|
1189
|
-
}
|
1190
|
-
|
1191
|
-
get '/abc/def/ghi'
|
1192
|
-
assert ok?
|
1193
|
-
end
|
1194
|
-
|
1195
|
-
it 'passes regular expression captures as block parameters' do
|
1196
|
-
mock_app {
|
1197
|
-
get(/^\/fo(.*)\/ba(.*)/) do |foo, bar|
|
1198
|
-
assert_equal 'orooomma', foo
|
1199
|
-
assert_equal 'f', bar
|
1200
|
-
'looks good'
|
1201
|
-
end
|
1202
|
-
}
|
1203
|
-
|
1204
|
-
get '/foorooomma/baf'
|
1205
|
-
assert ok?
|
1206
|
-
assert_equal 'looks good', body
|
1207
|
-
end
|
1208
|
-
|
1209
|
-
it "supports mixing multiple splat params like /*/foo/*/* as block parameters" do
|
1210
|
-
mock_app {
|
1211
|
-
get '/*/foo/*/*' do |foo, bar, baz|
|
1212
|
-
assert_equal 'bar', foo
|
1213
|
-
assert_equal 'bling', bar
|
1214
|
-
assert_equal 'baz/boom', baz
|
1215
|
-
'looks good'
|
1216
|
-
end
|
1217
|
-
}
|
1218
|
-
|
1219
|
-
get '/bar/foo/bling/baz/boom'
|
1220
|
-
assert ok?
|
1221
|
-
assert_equal 'looks good', body
|
1222
|
-
end
|
1223
|
-
|
1224
|
-
it 'raises an ArgumentError with block arity > 1 and too many values' do
|
1225
|
-
mock_app do
|
1226
|
-
get '/:foo/:bar/:baz' do |foo, bar|
|
1227
|
-
'quux'
|
1228
|
-
end
|
1229
|
-
end
|
1230
|
-
|
1231
|
-
assert_raises(ArgumentError) { get '/a/b/c' }
|
1232
|
-
end
|
1233
|
-
|
1234
|
-
it 'raises an ArgumentError with block param arity > 1 and too few values' do
|
1235
|
-
mock_app {
|
1236
|
-
get '/:foo/:bar' do |foo, bar, baz|
|
1237
|
-
'quux'
|
1238
|
-
end
|
1239
|
-
}
|
1240
|
-
|
1241
|
-
assert_raises(ArgumentError) { get '/a/b' }
|
1242
|
-
end
|
1243
|
-
|
1244
|
-
it 'succeeds if no block parameters are specified' do
|
1245
|
-
mock_app {
|
1246
|
-
get '/:foo/:bar' do
|
1247
|
-
'quux'
|
1248
|
-
end
|
1249
|
-
}
|
1250
|
-
|
1251
|
-
get '/a/b'
|
1252
|
-
assert ok?
|
1253
|
-
assert_equal 'quux', body
|
1254
|
-
end
|
1255
|
-
|
1256
|
-
it 'passes all params with block param arity -1 (splat args)' do
|
1257
|
-
mock_app {
|
1258
|
-
get '/:foo/:bar' do |*args|
|
1259
|
-
args.join
|
1260
|
-
end
|
1261
|
-
}
|
1262
|
-
|
1263
|
-
get '/a/b'
|
1264
|
-
assert ok?
|
1265
|
-
assert_equal 'ab', body
|
1266
|
-
end
|
1267
|
-
|
1268
|
-
it 'allows custom route-conditions to be set via route options' do
|
1269
|
-
protector = Module.new {
|
1270
|
-
def protect(*args)
|
1271
|
-
condition {
|
1272
|
-
unless authorize(params["user"], params["password"])
|
1273
|
-
halt 403, "go away"
|
1274
|
-
end
|
1275
|
-
}
|
1276
|
-
end
|
1277
|
-
}
|
1278
|
-
|
1279
|
-
mock_app {
|
1280
|
-
register protector
|
1281
|
-
|
1282
|
-
helpers do
|
1283
|
-
def authorize(username, password)
|
1284
|
-
username == "foo" && password == "bar"
|
1285
|
-
end
|
1286
|
-
end
|
1287
|
-
|
1288
|
-
get "/", :protect => true do
|
1289
|
-
"hey"
|
1290
|
-
end
|
1291
|
-
}
|
1292
|
-
|
1293
|
-
get "/"
|
1294
|
-
assert forbidden?
|
1295
|
-
assert_equal "go away", body
|
1296
|
-
|
1297
|
-
get "/", :user => "foo", :password => "bar"
|
1298
|
-
assert ok?
|
1299
|
-
assert_equal "hey", body
|
1300
|
-
end
|
1301
|
-
|
1302
|
-
# NOTE Block params behaves differently under 1.8 and 1.9. Under 1.8, block
|
1303
|
-
# param arity is lax: declaring a mismatched number of block params results
|
1304
|
-
# in a warning. Under 1.9, block param arity is strict: mismatched block
|
1305
|
-
# arity raises an ArgumentError.
|
1306
|
-
|
1307
|
-
if RUBY_VERSION >= '1.9'
|
1308
|
-
|
1309
|
-
it 'raises an ArgumentError with block param arity 1 and no values' do
|
1310
|
-
mock_app {
|
1311
|
-
get '/foo' do |foo|
|
1312
|
-
'quux'
|
1313
|
-
end
|
1314
|
-
}
|
1315
|
-
|
1316
|
-
assert_raises(ArgumentError) { get '/foo' }
|
1317
|
-
end
|
1318
|
-
|
1319
|
-
it 'raises an ArgumentError with block param arity 1 and too many values' do
|
1320
|
-
mock_app {
|
1321
|
-
get '/:foo/:bar/:baz' do |foo|
|
1322
|
-
'quux'
|
1323
|
-
end
|
1324
|
-
}
|
1325
|
-
|
1326
|
-
assert_raises(ArgumentError) { get '/a/b/c' }
|
1327
|
-
end
|
1328
|
-
|
1329
|
-
else
|
1330
|
-
|
1331
|
-
it 'does not raise an ArgumentError with block param arity 1 and no values' do
|
1332
|
-
mock_app {
|
1333
|
-
get '/foo' do |foo|
|
1334
|
-
'quux'
|
1335
|
-
end
|
1336
|
-
}
|
1337
|
-
|
1338
|
-
silence_warnings { get '/foo' }
|
1339
|
-
assert ok?
|
1340
|
-
assert_equal 'quux', body
|
1341
|
-
end
|
1342
|
-
|
1343
|
-
it 'does not raise an ArgumentError with block param arity 1 and too many values' do
|
1344
|
-
mock_app {
|
1345
|
-
get '/:foo/:bar/:baz' do |foo|
|
1346
|
-
'quux'
|
1347
|
-
end
|
1348
|
-
}
|
1349
|
-
|
1350
|
-
silence_warnings { get '/a/b/c' }
|
1351
|
-
assert ok?
|
1352
|
-
assert_equal 'quux', body
|
1353
|
-
end
|
1354
|
-
|
1355
|
-
end
|
1356
|
-
|
1357
|
-
it "matches routes defined in superclasses" do
|
1358
|
-
base = Class.new(Sinatra::Base)
|
1359
|
-
base.get('/foo') { 'foo in baseclass' }
|
1360
|
-
|
1361
|
-
mock_app(base) {
|
1362
|
-
get('/bar') { 'bar in subclass' }
|
1363
|
-
}
|
1364
|
-
|
1365
|
-
get '/foo'
|
1366
|
-
assert ok?
|
1367
|
-
assert_equal 'foo in baseclass', body
|
1368
|
-
|
1369
|
-
get '/bar'
|
1370
|
-
assert ok?
|
1371
|
-
assert_equal 'bar in subclass', body
|
1372
|
-
end
|
1373
|
-
|
1374
|
-
it "matches routes in subclasses before superclasses" do
|
1375
|
-
base = Class.new(Sinatra::Base)
|
1376
|
-
base.get('/foo') { 'foo in baseclass' }
|
1377
|
-
base.get('/bar') { 'bar in baseclass' }
|
1378
|
-
|
1379
|
-
mock_app(base) {
|
1380
|
-
get('/foo') { 'foo in subclass' }
|
1381
|
-
}
|
1382
|
-
|
1383
|
-
get '/foo'
|
1384
|
-
assert ok?
|
1385
|
-
assert_equal 'foo in subclass', body
|
1386
|
-
|
1387
|
-
get '/bar'
|
1388
|
-
assert ok?
|
1389
|
-
assert_equal 'bar in baseclass', body
|
1390
|
-
end
|
1391
|
-
|
1392
|
-
it "adds hostname condition when it is in options" do
|
1393
|
-
mock_app {
|
1394
|
-
get '/foo', :host => 'host' do
|
1395
|
-
'foo'
|
1396
|
-
end
|
1397
|
-
}
|
1398
|
-
|
1399
|
-
get '/foo'
|
1400
|
-
assert not_found?
|
1401
|
-
end
|
1402
|
-
|
1403
|
-
it 'allows using call to fire another request internally' do
|
1404
|
-
mock_app do
|
1405
|
-
get '/foo' do
|
1406
|
-
status, headers, body = call env.merge("PATH_INFO" => '/bar')
|
1407
|
-
[status, headers, body.each.map(&:upcase)]
|
1408
|
-
end
|
1409
|
-
|
1410
|
-
get '/bar' do
|
1411
|
-
"bar"
|
1412
|
-
end
|
1413
|
-
end
|
1414
|
-
|
1415
|
-
get '/foo'
|
1416
|
-
assert ok?
|
1417
|
-
assert_body "BAR"
|
1418
|
-
end
|
1419
|
-
|
1420
|
-
it 'plays well with other routing middleware' do
|
1421
|
-
middleware = Sinatra.new
|
1422
|
-
inner_app = Sinatra.new { get('/foo') { 'hello' } }
|
1423
|
-
builder = Rack::Builder.new do
|
1424
|
-
use middleware
|
1425
|
-
map('/test') { run inner_app }
|
1426
|
-
end
|
1427
|
-
|
1428
|
-
@app = builder.to_app
|
1429
|
-
get '/test/foo'
|
1430
|
-
assert ok?
|
1431
|
-
assert_body 'hello'
|
1432
|
-
end
|
1433
|
-
|
1434
|
-
it 'returns the route signature' do
|
1435
|
-
signature = list = nil
|
1436
|
-
|
1437
|
-
mock_app do
|
1438
|
-
signature = post('/') { }
|
1439
|
-
list = routes['POST']
|
1440
|
-
end
|
1441
|
-
|
1442
|
-
assert_equal Array, signature.class
|
1443
|
-
assert_equal 4, signature.length
|
1444
|
-
assert list.include?(signature)
|
1445
|
-
end
|
1446
|
-
|
1447
|
-
it "sets env['sinatra.route'] to the matched route" do
|
1448
|
-
mock_app do
|
1449
|
-
after do
|
1450
|
-
assert_equal 'GET /users/:id/status', env['sinatra.route']
|
1451
|
-
end
|
1452
|
-
get('/users/:id/status') { 'ok' }
|
1453
|
-
end
|
1454
|
-
get '/users/1/status'
|
1455
|
-
end
|
1456
|
-
end
|