adamwiggins-sinatra 0.8.9 → 0.10.1
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/CHANGES +211 -1
- data/LICENSE +1 -1
- data/README.rdoc +183 -139
- data/Rakefile +20 -81
- data/lib/sinatra.rb +5 -1
- data/lib/sinatra/base.rb +569 -278
- data/lib/sinatra/main.rb +12 -25
- data/lib/sinatra/showexceptions.rb +303 -0
- data/sinatra.gemspec +20 -44
- data/test/base_test.rb +140 -52
- data/test/builder_test.rb +14 -17
- data/test/contest.rb +64 -0
- data/test/erb_test.rb +42 -16
- data/test/extensions_test.rb +100 -0
- data/test/filter_test.rb +85 -13
- data/test/haml_test.rb +39 -21
- data/test/helper.rb +76 -0
- data/test/helpers_test.rb +219 -84
- data/test/mapped_error_test.rb +168 -146
- data/test/middleware_test.rb +22 -17
- data/test/options_test.rb +323 -54
- data/test/render_backtrace_test.rb +145 -0
- data/test/request_test.rb +28 -6
- data/test/response_test.rb +42 -0
- data/test/result_test.rb +27 -21
- data/test/route_added_hook_test.rb +59 -0
- data/test/routing_test.rb +558 -77
- data/test/sass_test.rb +52 -13
- data/test/server_test.rb +47 -0
- data/test/sinatra_test.rb +3 -5
- data/test/static_test.rb +57 -30
- data/test/templates_test.rb +74 -25
- data/test/views/error.builder +3 -0
- data/test/views/error.erb +3 -0
- data/test/views/error.haml +3 -0
- data/test/views/error.sass +2 -0
- data/test/views/foo/hello.test +1 -0
- metadata +50 -46
- data/compat/app_test.rb +0 -300
- data/compat/application_test.rb +0 -334
- data/compat/builder_test.rb +0 -101
- data/compat/custom_error_test.rb +0 -62
- data/compat/erb_test.rb +0 -136
- data/compat/events_test.rb +0 -75
- data/compat/filter_test.rb +0 -30
- data/compat/haml_test.rb +0 -233
- data/compat/helper.rb +0 -21
- data/compat/mapped_error_test.rb +0 -72
- data/compat/pipeline_test.rb +0 -71
- data/compat/public/foo.xml +0 -1
- data/compat/sass_test.rb +0 -57
- data/compat/sessions_test.rb +0 -39
- data/compat/streaming_test.rb +0 -121
- data/compat/sym_params_test.rb +0 -19
- data/compat/template_test.rb +0 -30
- data/compat/use_in_file_templates_test.rb +0 -47
- data/compat/views/foo.builder +0 -1
- data/compat/views/foo.erb +0 -1
- data/compat/views/foo.haml +0 -1
- data/compat/views/foo.sass +0 -2
- data/compat/views/foo_layout.erb +0 -2
- data/compat/views/foo_layout.haml +0 -2
- data/compat/views/layout_test/foo.builder +0 -1
- data/compat/views/layout_test/foo.erb +0 -1
- data/compat/views/layout_test/foo.haml +0 -1
- data/compat/views/layout_test/foo.sass +0 -2
- data/compat/views/layout_test/layout.builder +0 -3
- data/compat/views/layout_test/layout.erb +0 -1
- data/compat/views/layout_test/layout.haml +0 -1
- data/compat/views/layout_test/layout.sass +0 -2
- data/compat/views/no_layout/no_layout.builder +0 -1
- data/compat/views/no_layout/no_layout.haml +0 -1
- data/lib/sinatra/compat.rb +0 -239
- data/lib/sinatra/test.rb +0 -112
- data/lib/sinatra/test/rspec.rb +0 -2
- data/lib/sinatra/test/spec.rb +0 -2
- data/lib/sinatra/test/unit.rb +0 -11
- data/test/reload_test.rb +0 -65
@@ -0,0 +1,145 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
require 'sass/error'
|
4
|
+
|
5
|
+
class RenderBacktraceTest < Test::Unit::TestCase
|
6
|
+
VIEWS = File.dirname(__FILE__) + '/views'
|
7
|
+
|
8
|
+
def assert_raise_at(filename, line, exception = RuntimeError)
|
9
|
+
f, l = nil
|
10
|
+
assert_raise(exception) do
|
11
|
+
begin
|
12
|
+
get('/')
|
13
|
+
rescue => e
|
14
|
+
f, l = e.backtrace.first.split(':')
|
15
|
+
raise
|
16
|
+
end
|
17
|
+
end
|
18
|
+
assert_equal(filename, f, "expected #{exception.name} in #{filename}, was #{f}")
|
19
|
+
assert_equal(line, l.to_i, "expected #{exception.name} in #{filename} at line #{line}, was at line #{l}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def backtrace_app(&block)
|
23
|
+
mock_app {
|
24
|
+
use_in_file_templates!
|
25
|
+
set :views, RenderBacktraceTest::VIEWS
|
26
|
+
template :builder_template do
|
27
|
+
'raise "error"'
|
28
|
+
end
|
29
|
+
template :erb_template do
|
30
|
+
'<% raise "error" %>'
|
31
|
+
end
|
32
|
+
template :haml_template do
|
33
|
+
'%h1= raise "error"'
|
34
|
+
end
|
35
|
+
template :sass_template do
|
36
|
+
'+syntax-error'
|
37
|
+
end
|
38
|
+
get '/', &block
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
it "provides backtrace for Builder template" do
|
43
|
+
backtrace_app { builder :error }
|
44
|
+
assert_raise_at(File.join(VIEWS,'error.builder'), 2)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "provides backtrace for ERB template" do
|
48
|
+
backtrace_app { erb :error }
|
49
|
+
assert_raise_at(File.join(VIEWS,'error.erb'), 2)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "provides backtrace for HAML template" do
|
53
|
+
backtrace_app { haml :error }
|
54
|
+
assert_raise_at(File.join(VIEWS,'error.haml'), 2)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "provides backtrace for Sass template" do
|
58
|
+
backtrace_app { sass :error }
|
59
|
+
assert_raise_at(File.join(VIEWS,'error.sass'), 2, Sass::SyntaxError)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "provides backtrace for ERB template with locals" do
|
63
|
+
backtrace_app { erb :error, {}, :french => true }
|
64
|
+
assert_raise_at(File.join(VIEWS,'error.erb'), 3)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "provides backtrace for HAML template with locals" do
|
68
|
+
backtrace_app { haml :error, {}, :french => true }
|
69
|
+
assert_raise_at(File.join(VIEWS,'error.haml'), 3)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "provides backtrace for inline Builder string" do
|
73
|
+
backtrace_app { builder "raise 'Ack! Thbbbt!'"}
|
74
|
+
assert_raise_at(__FILE__, (__LINE__-1))
|
75
|
+
end
|
76
|
+
|
77
|
+
it "provides backtrace for inline ERB string" do
|
78
|
+
backtrace_app { erb "<% raise 'bidi-bidi-bidi' %>" }
|
79
|
+
assert_raise_at(__FILE__, (__LINE__-1))
|
80
|
+
end
|
81
|
+
|
82
|
+
it "provides backtrace for inline HAML string" do
|
83
|
+
backtrace_app { haml "%h1= raise 'Lions and tigers and bears! Oh, my!'" }
|
84
|
+
assert_raise_at(__FILE__, (__LINE__-1))
|
85
|
+
end
|
86
|
+
|
87
|
+
# it "provides backtrace for inline Sass string" do
|
88
|
+
# backtrace_app { sass '+buh-bye' }
|
89
|
+
# assert_raise_at(__FILE__, (__LINE__-1), Sass::SyntaxError)
|
90
|
+
# end
|
91
|
+
|
92
|
+
it "provides backtrace for named Builder template" do
|
93
|
+
backtrace_app { builder :builder_template }
|
94
|
+
assert_raise_at(__FILE__, (__LINE__-68))
|
95
|
+
end
|
96
|
+
|
97
|
+
it "provides backtrace for named ERB template" do
|
98
|
+
backtrace_app { erb :erb_template }
|
99
|
+
assert_raise_at(__FILE__, (__LINE__-70))
|
100
|
+
end
|
101
|
+
|
102
|
+
it "provides backtrace for named HAML template" do
|
103
|
+
backtrace_app { haml :haml_template }
|
104
|
+
assert_raise_at(__FILE__, (__LINE__-72))
|
105
|
+
end
|
106
|
+
|
107
|
+
# it "provides backtrace for named Sass template" do
|
108
|
+
# backtrace_app { sass :sass_template }
|
109
|
+
# assert_raise_at(__FILE__, (__LINE__-74), Sass::SyntaxError)
|
110
|
+
# end
|
111
|
+
|
112
|
+
it "provides backtrace for in file Builder template" do
|
113
|
+
backtrace_app { builder :builder_in_file }
|
114
|
+
assert_raise_at(__FILE__, (__LINE__+22))
|
115
|
+
end
|
116
|
+
|
117
|
+
it "provides backtrace for in file ERB template" do
|
118
|
+
backtrace_app { erb :erb_in_file }
|
119
|
+
assert_raise_at(__FILE__, (__LINE__+20))
|
120
|
+
end
|
121
|
+
|
122
|
+
it "provides backtrace for in file HAML template" do
|
123
|
+
backtrace_app { haml :haml_in_file }
|
124
|
+
assert_raise_at(__FILE__, (__LINE__+18))
|
125
|
+
end
|
126
|
+
|
127
|
+
# it "provides backtrace for in file Sass template" do
|
128
|
+
# backtrace_app { sass :sass_in_file }
|
129
|
+
# assert_raise_at(__FILE__, (__LINE__+16), Sass::SyntaxError)
|
130
|
+
# end
|
131
|
+
end
|
132
|
+
|
133
|
+
__END__
|
134
|
+
|
135
|
+
@@ builder_in_file
|
136
|
+
raise "bif"
|
137
|
+
|
138
|
+
@@ erb_in_file
|
139
|
+
<% raise "bam" %>
|
140
|
+
|
141
|
+
@@ haml_in_file
|
142
|
+
%h1= raise "pow"
|
143
|
+
|
144
|
+
@@ sass_in_file
|
145
|
+
+blam
|
data/test/request_test.rb
CHANGED
@@ -1,11 +1,33 @@
|
|
1
|
-
require '
|
2
|
-
require 'sinatra/base'
|
3
|
-
require 'sinatra/test'
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
4
2
|
|
5
|
-
|
3
|
+
class RequestTest < Test::Unit::TestCase
|
6
4
|
it 'responds to #user_agent' do
|
7
5
|
request = Sinatra::Request.new({'HTTP_USER_AGENT' => 'Test'})
|
8
|
-
request.
|
9
|
-
|
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
|
+
|
19
|
+
it 'is secure when the url scheme is https' do
|
20
|
+
request = Sinatra::Request.new('rack.url_scheme' => 'https')
|
21
|
+
assert request.secure?
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'is not secure when the url scheme is http' do
|
25
|
+
request = Sinatra::Request.new('rack.url_scheme' => 'http')
|
26
|
+
assert !request.secure?
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'respects X-Forwarded-Proto header for proxied SSL' do
|
30
|
+
request = Sinatra::Request.new('HTTP_X_FORWARDED_PROTO' => 'https')
|
31
|
+
assert request.secure?
|
10
32
|
end
|
11
33
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/helper'
|
4
|
+
|
5
|
+
class ResponseTest < Test::Unit::TestCase
|
6
|
+
setup 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
CHANGED
@@ -1,10 +1,6 @@
|
|
1
|
-
require '
|
2
|
-
require 'sinatra/base'
|
3
|
-
require 'sinatra/test'
|
4
|
-
|
5
|
-
describe 'Result Handling' do
|
6
|
-
include Sinatra::Test
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
7
2
|
|
3
|
+
class ResultTest < Test::Unit::TestCase
|
8
4
|
it "sets response.body when result is a String" do
|
9
5
|
mock_app {
|
10
6
|
get '/' do
|
@@ -13,8 +9,8 @@ describe 'Result Handling' do
|
|
13
9
|
}
|
14
10
|
|
15
11
|
get '/'
|
16
|
-
|
17
|
-
|
12
|
+
assert ok?
|
13
|
+
assert_equal 'Hello World', body
|
18
14
|
end
|
19
15
|
|
20
16
|
it "sets response.body when result is an Array of Strings" do
|
@@ -25,8 +21,8 @@ describe 'Result Handling' do
|
|
25
21
|
}
|
26
22
|
|
27
23
|
get '/'
|
28
|
-
|
29
|
-
|
24
|
+
assert ok?
|
25
|
+
assert_equal 'HelloWorld', body
|
30
26
|
end
|
31
27
|
|
32
28
|
it "sets response.body when result responds to #each" do
|
@@ -39,8 +35,8 @@ describe 'Result Handling' do
|
|
39
35
|
}
|
40
36
|
|
41
37
|
get '/'
|
42
|
-
|
43
|
-
|
38
|
+
assert ok?
|
39
|
+
assert_equal 'Hello World', body
|
44
40
|
end
|
45
41
|
|
46
42
|
it "sets response.body to [] when result is nil" do
|
@@ -51,8 +47,8 @@ describe 'Result Handling' do
|
|
51
47
|
}
|
52
48
|
|
53
49
|
get '/'
|
54
|
-
|
55
|
-
|
50
|
+
assert ok?
|
51
|
+
assert_equal '', body
|
56
52
|
end
|
57
53
|
|
58
54
|
it "sets status, headers, and body when result is a Rack response tuple" do
|
@@ -63,9 +59,9 @@ describe 'Result Handling' do
|
|
63
59
|
}
|
64
60
|
|
65
61
|
get '/'
|
66
|
-
|
67
|
-
response['Content-Type']
|
68
|
-
|
62
|
+
assert_equal 205, status
|
63
|
+
assert_equal 'foo/bar', response['Content-Type']
|
64
|
+
assert_equal 'Hello World', body
|
69
65
|
end
|
70
66
|
|
71
67
|
it "sets status and body when result is a two-tuple" do
|
@@ -76,8 +72,18 @@ describe 'Result Handling' do
|
|
76
72
|
}
|
77
73
|
|
78
74
|
get '/'
|
79
|
-
|
80
|
-
|
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 '/' }
|
81
87
|
end
|
82
88
|
|
83
89
|
it "sets status when result is a Fixnum status code" do
|
@@ -86,7 +92,7 @@ describe 'Result Handling' do
|
|
86
92
|
}
|
87
93
|
|
88
94
|
get '/'
|
89
|
-
|
90
|
-
body
|
95
|
+
assert_equal 205, status
|
96
|
+
assert_equal '', body
|
91
97
|
end
|
92
98
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
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 < Test::Unit::TestCase
|
14
|
+
setup {
|
15
|
+
RouteAddedTest.routes.clear
|
16
|
+
RouteAddedTest.procs.clear
|
17
|
+
}
|
18
|
+
|
19
|
+
it "should be notified of an added route" do
|
20
|
+
mock_app(Class.new(Sinatra::Base)) {
|
21
|
+
register RouteAddedTest
|
22
|
+
get('/') {}
|
23
|
+
}
|
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)) {
|
42
|
+
register RouteAddedTest
|
43
|
+
register RouteAddedTest
|
44
|
+
get('/') {}
|
45
|
+
}
|
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)) {
|
53
|
+
register RouteAddedTest
|
54
|
+
get('/') {}
|
55
|
+
}
|
56
|
+
|
57
|
+
assert_kind_of Proc, RouteAddedTest.procs.first
|
58
|
+
end
|
59
|
+
end
|
data/test/routing_test.rb
CHANGED
@@ -1,11 +1,28 @@
|
|
1
|
-
require '
|
2
|
-
require 'sinatra/base'
|
3
|
-
require 'sinatra/test'
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
4
2
|
|
5
|
-
|
6
|
-
|
3
|
+
# Helper method for easy route pattern matching testing
|
4
|
+
def route_def(pattern)
|
5
|
+
mock_app { get(pattern) { } }
|
6
|
+
end
|
7
|
+
|
8
|
+
class RegexpLookAlike
|
9
|
+
class MatchData
|
10
|
+
def captures
|
11
|
+
["this", "is", "a", "test"]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def match(string)
|
16
|
+
::RegexpLookAlike::MatchData.new if string == "/this/is/a/test/"
|
17
|
+
end
|
18
|
+
|
19
|
+
def keys
|
20
|
+
["one", "two", "three", "four"]
|
21
|
+
end
|
22
|
+
end
|
7
23
|
|
8
|
-
|
24
|
+
class RoutingTest < Test::Unit::TestCase
|
25
|
+
%w[get put post delete].each do |verb|
|
9
26
|
it "defines #{verb.upcase} request handlers with #{verb}" do
|
10
27
|
mock_app {
|
11
28
|
send verb, '/hello' do
|
@@ -15,40 +32,91 @@ describe "Routing" do
|
|
15
32
|
|
16
33
|
request = Rack::MockRequest.new(@app)
|
17
34
|
response = request.request(verb.upcase, '/hello', {})
|
18
|
-
response.
|
19
|
-
|
35
|
+
assert response.ok?
|
36
|
+
assert_equal 'Hello World', response.body
|
20
37
|
end
|
21
38
|
end
|
22
39
|
|
40
|
+
it "defines HEAD request handlers with HEAD" do
|
41
|
+
mock_app {
|
42
|
+
head '/hello' do
|
43
|
+
response['X-Hello'] = 'World!'
|
44
|
+
'remove me'
|
45
|
+
end
|
46
|
+
}
|
47
|
+
|
48
|
+
request = Rack::MockRequest.new(@app)
|
49
|
+
response = request.request('HEAD', '/hello', {})
|
50
|
+
assert response.ok?
|
51
|
+
assert_equal 'World!', response['X-Hello']
|
52
|
+
assert_equal '', response.body
|
53
|
+
end
|
54
|
+
|
23
55
|
it "404s when no route satisfies the request" do
|
24
56
|
mock_app {
|
25
57
|
get('/foo') { }
|
26
58
|
}
|
27
59
|
get '/bar'
|
28
|
-
|
60
|
+
assert_equal 404, status
|
61
|
+
end
|
62
|
+
|
63
|
+
it "overrides the content-type in error handlers" do
|
64
|
+
mock_app {
|
65
|
+
before { content_type 'text/plain' }
|
66
|
+
error Sinatra::NotFound do
|
67
|
+
content_type "text/html"
|
68
|
+
"<h1>Not Found</h1>"
|
69
|
+
end
|
70
|
+
}
|
71
|
+
|
72
|
+
get '/foo'
|
73
|
+
assert_equal 404, status
|
74
|
+
assert_equal 'text/html', response["Content-Type"]
|
75
|
+
assert_equal "<h1>Not Found</h1>", response.body
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'takes multiple definitions of a route' do
|
79
|
+
mock_app {
|
80
|
+
user_agent(/Foo/)
|
81
|
+
get '/foo' do
|
82
|
+
'foo'
|
83
|
+
end
|
84
|
+
|
85
|
+
get '/foo' do
|
86
|
+
'not foo'
|
87
|
+
end
|
88
|
+
}
|
89
|
+
|
90
|
+
get '/foo', {}, 'HTTP_USER_AGENT' => 'Foo'
|
91
|
+
assert ok?
|
92
|
+
assert_equal 'foo', body
|
93
|
+
|
94
|
+
get '/foo'
|
95
|
+
assert ok?
|
96
|
+
assert_equal 'not foo', body
|
29
97
|
end
|
30
98
|
|
31
99
|
it "exposes params with indifferent hash" do
|
32
100
|
mock_app {
|
33
101
|
get '/:foo' do
|
34
|
-
params['foo']
|
35
|
-
params[:foo]
|
102
|
+
assert_equal 'bar', params['foo']
|
103
|
+
assert_equal 'bar', params[:foo]
|
36
104
|
'well, alright'
|
37
105
|
end
|
38
106
|
}
|
39
107
|
get '/bar'
|
40
|
-
|
108
|
+
assert_equal 'well, alright', body
|
41
109
|
end
|
42
110
|
|
43
111
|
it "merges named params and query string params in params" do
|
44
112
|
mock_app {
|
45
113
|
get '/:foo' do
|
46
|
-
params['foo']
|
47
|
-
params['baz']
|
114
|
+
assert_equal 'bar', params['foo']
|
115
|
+
assert_equal 'biz', params['baz']
|
48
116
|
end
|
49
117
|
}
|
50
118
|
get '/bar?baz=biz'
|
51
|
-
|
119
|
+
assert ok?
|
52
120
|
end
|
53
121
|
|
54
122
|
it "supports named params like /hello/:person" do
|
@@ -58,7 +126,7 @@ describe "Routing" do
|
|
58
126
|
end
|
59
127
|
}
|
60
128
|
get '/hello/Frank'
|
61
|
-
|
129
|
+
assert_equal 'Hello Frank', body
|
62
130
|
end
|
63
131
|
|
64
132
|
it "supports optional named params like /?:foo?/?:bar?" do
|
@@ -69,61 +137,181 @@ describe "Routing" do
|
|
69
137
|
}
|
70
138
|
|
71
139
|
get '/hello/world'
|
72
|
-
|
73
|
-
|
140
|
+
assert ok?
|
141
|
+
assert_equal "foo=hello;bar=world", body
|
74
142
|
|
75
143
|
get '/hello'
|
76
|
-
|
77
|
-
|
144
|
+
assert ok?
|
145
|
+
assert_equal "foo=hello;bar=", body
|
78
146
|
|
79
147
|
get '/'
|
80
|
-
|
81
|
-
|
148
|
+
assert ok?
|
149
|
+
assert_equal "foo=;bar=", body
|
82
150
|
end
|
83
151
|
|
84
152
|
it "supports single splat params like /*" do
|
85
153
|
mock_app {
|
86
154
|
get '/*' do
|
87
|
-
params['splat'].
|
155
|
+
assert params['splat'].kind_of?(Array)
|
88
156
|
params['splat'].join "\n"
|
89
157
|
end
|
90
158
|
}
|
91
159
|
|
92
160
|
get '/foo'
|
93
|
-
|
161
|
+
assert_equal "foo", body
|
94
162
|
|
95
163
|
get '/foo/bar/baz'
|
96
|
-
|
164
|
+
assert_equal "foo/bar/baz", body
|
97
165
|
end
|
98
166
|
|
99
167
|
it "supports mixing multiple splat params like /*/foo/*/*" do
|
100
168
|
mock_app {
|
101
169
|
get '/*/foo/*/*' do
|
102
|
-
params['splat'].
|
170
|
+
assert params['splat'].kind_of?(Array)
|
103
171
|
params['splat'].join "\n"
|
104
172
|
end
|
105
173
|
}
|
106
174
|
|
107
175
|
get '/bar/foo/bling/baz/boom'
|
108
|
-
|
176
|
+
assert_equal "bar\nbling\nbaz/boom", body
|
109
177
|
|
110
178
|
get '/bar/foo/baz'
|
111
|
-
|
179
|
+
assert not_found?
|
112
180
|
end
|
113
181
|
|
114
182
|
it "supports mixing named and splat params like /:foo/*" do
|
115
183
|
mock_app {
|
116
184
|
get '/:foo/*' do
|
117
|
-
|
118
|
-
|
185
|
+
assert_equal 'foo', params['foo']
|
186
|
+
assert_equal ['bar/baz'], params['splat']
|
119
187
|
end
|
120
188
|
}
|
121
189
|
|
122
190
|
get '/foo/bar/baz'
|
123
|
-
|
191
|
+
assert ok?
|
192
|
+
end
|
193
|
+
|
194
|
+
it "matches a dot ('.') as part of a named param" do
|
195
|
+
mock_app {
|
196
|
+
get '/:foo/:bar' do
|
197
|
+
params[:foo]
|
198
|
+
end
|
199
|
+
}
|
200
|
+
|
201
|
+
get '/user@example.com/name'
|
202
|
+
assert_equal 200, response.status
|
203
|
+
assert_equal 'user@example.com', body
|
204
|
+
end
|
205
|
+
|
206
|
+
it "matches a literal dot ('.') outside of named params" do
|
207
|
+
mock_app {
|
208
|
+
get '/:file.:ext' do
|
209
|
+
assert_equal 'pony', params[:file]
|
210
|
+
assert_equal 'jpg', params[:ext]
|
211
|
+
'right on'
|
212
|
+
end
|
213
|
+
}
|
214
|
+
|
215
|
+
get '/pony.jpg'
|
216
|
+
assert_equal 200, response.status
|
217
|
+
assert_equal 'right on', body
|
218
|
+
end
|
219
|
+
|
220
|
+
it "literally matches . in paths" do
|
221
|
+
route_def '/test.bar'
|
222
|
+
|
223
|
+
get '/test.bar'
|
224
|
+
assert ok?
|
225
|
+
get 'test0bar'
|
226
|
+
assert not_found?
|
227
|
+
end
|
228
|
+
|
229
|
+
it "literally matches $ in paths" do
|
230
|
+
route_def '/test$/'
|
231
|
+
|
232
|
+
get '/test$/'
|
233
|
+
assert ok?
|
234
|
+
end
|
235
|
+
|
236
|
+
it "literally matches + in paths" do
|
237
|
+
route_def '/te+st/'
|
238
|
+
|
239
|
+
get '/te%2Bst/'
|
240
|
+
assert ok?
|
241
|
+
get '/teeeeeeest/'
|
242
|
+
assert not_found?
|
243
|
+
end
|
244
|
+
|
245
|
+
it "literally matches () in paths" do
|
246
|
+
route_def '/test(bar)/'
|
247
|
+
|
248
|
+
get '/test(bar)/'
|
249
|
+
assert ok?
|
250
|
+
end
|
251
|
+
|
252
|
+
it "supports basic nested params" do
|
253
|
+
mock_app {
|
254
|
+
get '/hi' do
|
255
|
+
params["person"]["name"]
|
256
|
+
end
|
257
|
+
}
|
258
|
+
|
259
|
+
get "/hi?person[name]=John+Doe"
|
260
|
+
assert ok?
|
261
|
+
assert_equal "John Doe", body
|
262
|
+
end
|
263
|
+
|
264
|
+
it "exposes nested params with indifferent hash" do
|
265
|
+
mock_app {
|
266
|
+
get '/testme' do
|
267
|
+
assert_equal 'baz', params['bar']['foo']
|
268
|
+
assert_equal 'baz', params['bar'][:foo]
|
269
|
+
'well, alright'
|
270
|
+
end
|
271
|
+
}
|
272
|
+
get '/testme?bar[foo]=baz'
|
273
|
+
assert_equal 'well, alright', body
|
124
274
|
end
|
125
275
|
|
126
|
-
it "supports
|
276
|
+
it "supports deeply nested params" do
|
277
|
+
expected_params = {
|
278
|
+
"emacs" => {
|
279
|
+
"map" => { "goto-line" => "M-g g" },
|
280
|
+
"version" => "22.3.1"
|
281
|
+
},
|
282
|
+
"browser" => {
|
283
|
+
"firefox" => {"engine" => {"name"=>"spidermonkey", "version"=>"1.7.0"}},
|
284
|
+
"chrome" => {"engine" => {"name"=>"V8", "version"=>"1.0"}}
|
285
|
+
},
|
286
|
+
"paste" => {"name"=>"hello world", "syntax"=>"ruby"}
|
287
|
+
}
|
288
|
+
mock_app {
|
289
|
+
get '/foo' do
|
290
|
+
assert_equal expected_params, params
|
291
|
+
'looks good'
|
292
|
+
end
|
293
|
+
}
|
294
|
+
get '/foo', expected_params
|
295
|
+
assert ok?
|
296
|
+
assert_equal 'looks good', body
|
297
|
+
end
|
298
|
+
|
299
|
+
it "preserves non-nested params" do
|
300
|
+
mock_app {
|
301
|
+
get '/foo' do
|
302
|
+
assert_equal "2", params["article_id"]
|
303
|
+
assert_equal "awesome", params['comment']['body']
|
304
|
+
assert_nil params['comment[body]']
|
305
|
+
'looks good'
|
306
|
+
end
|
307
|
+
}
|
308
|
+
|
309
|
+
get '/foo?article_id=2&comment[body]=awesome'
|
310
|
+
assert ok?
|
311
|
+
assert_equal 'looks good', body
|
312
|
+
end
|
313
|
+
|
314
|
+
it "matches paths that include spaces encoded with %20" do
|
127
315
|
mock_app {
|
128
316
|
get '/path with spaces' do
|
129
317
|
'looks good'
|
@@ -131,21 +319,33 @@ describe "Routing" do
|
|
131
319
|
}
|
132
320
|
|
133
321
|
get '/path%20with%20spaces'
|
134
|
-
|
135
|
-
|
322
|
+
assert ok?
|
323
|
+
assert_equal 'looks good', body
|
324
|
+
end
|
325
|
+
|
326
|
+
it "matches paths that include spaces encoded with +" do
|
327
|
+
mock_app {
|
328
|
+
get '/path with spaces' do
|
329
|
+
'looks good'
|
330
|
+
end
|
331
|
+
}
|
332
|
+
|
333
|
+
get '/path+with+spaces'
|
334
|
+
assert ok?
|
335
|
+
assert_equal 'looks good', body
|
136
336
|
end
|
137
337
|
|
138
338
|
it "URL decodes named parameters and splats" do
|
139
339
|
mock_app {
|
140
340
|
get '/:foo/*' do
|
141
|
-
params['foo']
|
142
|
-
|
341
|
+
assert_equal 'hello world', params['foo']
|
342
|
+
assert_equal ['how are you'], params['splat']
|
143
343
|
nil
|
144
344
|
end
|
145
345
|
}
|
146
346
|
|
147
347
|
get '/hello%20world/how%20are%20you'
|
148
|
-
|
348
|
+
assert ok?
|
149
349
|
end
|
150
350
|
|
151
351
|
it 'supports regular expressions' do
|
@@ -156,21 +356,43 @@ describe "Routing" do
|
|
156
356
|
}
|
157
357
|
|
158
358
|
get '/foooom/bar'
|
159
|
-
|
160
|
-
|
359
|
+
assert ok?
|
360
|
+
assert_equal 'Hello World', body
|
161
361
|
end
|
162
362
|
|
163
363
|
it 'makes regular expression captures available in params[:captures]' do
|
164
364
|
mock_app {
|
165
365
|
get(/^\/fo(.*)\/ba(.*)/) do
|
166
|
-
|
366
|
+
assert_equal ['orooomma', 'f'], params[:captures]
|
167
367
|
'right on'
|
168
368
|
end
|
169
369
|
}
|
170
370
|
|
171
371
|
get '/foorooomma/baf'
|
172
|
-
|
173
|
-
|
372
|
+
assert ok?
|
373
|
+
assert_equal 'right on', body
|
374
|
+
end
|
375
|
+
|
376
|
+
it 'supports regular expression look-alike routes' do
|
377
|
+
mock_app {
|
378
|
+
get(RegexpLookAlike.new) do
|
379
|
+
assert_equal 'this', params[:one]
|
380
|
+
assert_equal 'is', params[:two]
|
381
|
+
assert_equal 'a', params[:three]
|
382
|
+
assert_equal 'test', params[:four]
|
383
|
+
'right on'
|
384
|
+
end
|
385
|
+
}
|
386
|
+
|
387
|
+
get '/this/is/a/test/'
|
388
|
+
assert ok?
|
389
|
+
assert_equal 'right on', body
|
390
|
+
end
|
391
|
+
|
392
|
+
it 'raises a TypeError when pattern is not a String or Regexp' do
|
393
|
+
assert_raise(TypeError) {
|
394
|
+
mock_app { get(42){} }
|
395
|
+
}
|
174
396
|
end
|
175
397
|
|
176
398
|
it "returns response immediately on halt" do
|
@@ -182,8 +404,32 @@ describe "Routing" do
|
|
182
404
|
}
|
183
405
|
|
184
406
|
get '/'
|
185
|
-
|
186
|
-
|
407
|
+
assert ok?
|
408
|
+
assert_equal 'Hello World', body
|
409
|
+
end
|
410
|
+
|
411
|
+
it "halts with a response tuple" do
|
412
|
+
mock_app {
|
413
|
+
get '/' do
|
414
|
+
halt 295, {'Content-Type' => 'text/plain'}, 'Hello World'
|
415
|
+
end
|
416
|
+
}
|
417
|
+
|
418
|
+
get '/'
|
419
|
+
assert_equal 295, status
|
420
|
+
assert_equal 'text/plain', response['Content-Type']
|
421
|
+
assert_equal 'Hello World', body
|
422
|
+
end
|
423
|
+
|
424
|
+
it "halts with an array of strings" do
|
425
|
+
mock_app {
|
426
|
+
get '/' do
|
427
|
+
halt %w[Hello World How Are You]
|
428
|
+
end
|
429
|
+
}
|
430
|
+
|
431
|
+
get '/'
|
432
|
+
assert_equal 'HelloWorldHowAreYou', body
|
187
433
|
end
|
188
434
|
|
189
435
|
it "transitions to the next matching route on pass" do
|
@@ -194,14 +440,14 @@ describe "Routing" do
|
|
194
440
|
end
|
195
441
|
|
196
442
|
get '/*' do
|
197
|
-
params.
|
443
|
+
assert !params.include?('foo')
|
198
444
|
'Hello World'
|
199
445
|
end
|
200
446
|
}
|
201
447
|
|
202
448
|
get '/bar'
|
203
|
-
|
204
|
-
|
449
|
+
assert ok?
|
450
|
+
assert_equal 'Hello World', body
|
205
451
|
end
|
206
452
|
|
207
453
|
it "transitions to 404 when passed and no subsequent route matches" do
|
@@ -213,7 +459,7 @@ describe "Routing" do
|
|
213
459
|
}
|
214
460
|
|
215
461
|
get '/bar'
|
216
|
-
|
462
|
+
assert not_found?
|
217
463
|
end
|
218
464
|
|
219
465
|
it "passes when matching condition returns false" do
|
@@ -225,11 +471,11 @@ describe "Routing" do
|
|
225
471
|
}
|
226
472
|
|
227
473
|
get '/bar'
|
228
|
-
|
229
|
-
|
474
|
+
assert ok?
|
475
|
+
assert_equal 'Hello World', body
|
230
476
|
|
231
477
|
get '/foo'
|
232
|
-
|
478
|
+
assert not_found?
|
233
479
|
end
|
234
480
|
|
235
481
|
it "does not pass when matching condition returns nil" do
|
@@ -241,8 +487,8 @@ describe "Routing" do
|
|
241
487
|
}
|
242
488
|
|
243
489
|
get '/bar'
|
244
|
-
|
245
|
-
|
490
|
+
assert ok?
|
491
|
+
assert_equal 'Hello World', body
|
246
492
|
end
|
247
493
|
|
248
494
|
it "passes to next route when condition calls pass explicitly" do
|
@@ -254,11 +500,11 @@ describe "Routing" do
|
|
254
500
|
}
|
255
501
|
|
256
502
|
get '/bar'
|
257
|
-
|
258
|
-
|
503
|
+
assert ok?
|
504
|
+
assert_equal 'Hello World', body
|
259
505
|
|
260
506
|
get '/foo'
|
261
|
-
|
507
|
+
assert not_found?
|
262
508
|
end
|
263
509
|
|
264
510
|
it "passes to the next route when host_name does not match" do
|
@@ -269,11 +515,11 @@ describe "Routing" do
|
|
269
515
|
end
|
270
516
|
}
|
271
517
|
get '/foo'
|
272
|
-
|
518
|
+
assert not_found?
|
273
519
|
|
274
|
-
get '/foo',
|
275
|
-
|
276
|
-
|
520
|
+
get '/foo', {}, { 'HTTP_HOST' => 'example.com' }
|
521
|
+
assert_equal 200, status
|
522
|
+
assert_equal 'Hello World', body
|
277
523
|
end
|
278
524
|
|
279
525
|
it "passes to the next route when user_agent does not match" do
|
@@ -284,11 +530,11 @@ describe "Routing" do
|
|
284
530
|
end
|
285
531
|
}
|
286
532
|
get '/foo'
|
287
|
-
|
533
|
+
assert not_found?
|
288
534
|
|
289
|
-
get '/foo',
|
290
|
-
|
291
|
-
|
535
|
+
get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' }
|
536
|
+
assert_equal 200, status
|
537
|
+
assert_equal 'Hello World', body
|
292
538
|
end
|
293
539
|
|
294
540
|
it "makes captures in user agent pattern available in params[:agent]" do
|
@@ -298,9 +544,9 @@ describe "Routing" do
|
|
298
544
|
'Hello ' + params[:agent].first
|
299
545
|
end
|
300
546
|
}
|
301
|
-
get '/foo',
|
302
|
-
|
303
|
-
|
547
|
+
get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' }
|
548
|
+
assert_equal 200, status
|
549
|
+
assert_equal 'Hello Bar', body
|
304
550
|
end
|
305
551
|
|
306
552
|
it "filters by accept header" do
|
@@ -310,13 +556,13 @@ describe "Routing" do
|
|
310
556
|
end
|
311
557
|
}
|
312
558
|
|
313
|
-
get '/',
|
314
|
-
|
315
|
-
|
316
|
-
response.headers['Content-Type']
|
559
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
|
560
|
+
assert ok?
|
561
|
+
assert_equal 'application/xml', body
|
562
|
+
assert_equal 'application/xml', response.headers['Content-Type']
|
317
563
|
|
318
|
-
get '/',
|
319
|
-
|
564
|
+
get '/', {}, { :accept => 'text/html' }
|
565
|
+
assert !ok?
|
320
566
|
end
|
321
567
|
|
322
568
|
it "allows multiple mime types for accept header" do
|
@@ -329,10 +575,245 @@ describe "Routing" do
|
|
329
575
|
}
|
330
576
|
|
331
577
|
types.each do |type|
|
332
|
-
get '/',
|
333
|
-
|
334
|
-
|
335
|
-
response.headers['Content-Type']
|
578
|
+
get '/', {}, { 'HTTP_ACCEPT' => type }
|
579
|
+
assert ok?
|
580
|
+
assert_equal type, body
|
581
|
+
assert_equal type, response.headers['Content-Type']
|
336
582
|
end
|
337
583
|
end
|
584
|
+
|
585
|
+
it 'degrades gracefully when optional accept header is not provided' do
|
586
|
+
mock_app {
|
587
|
+
get '/', :provides => :xml do
|
588
|
+
request.env['HTTP_ACCEPT']
|
589
|
+
end
|
590
|
+
get '/' do
|
591
|
+
'default'
|
592
|
+
end
|
593
|
+
}
|
594
|
+
get '/'
|
595
|
+
assert ok?
|
596
|
+
assert_equal 'default', body
|
597
|
+
end
|
598
|
+
|
599
|
+
it 'passes a single url param as block parameters when one param is specified' do
|
600
|
+
mock_app {
|
601
|
+
get '/:foo' do |foo|
|
602
|
+
assert_equal 'bar', foo
|
603
|
+
end
|
604
|
+
}
|
605
|
+
|
606
|
+
get '/bar'
|
607
|
+
assert ok?
|
608
|
+
end
|
609
|
+
|
610
|
+
it 'passes multiple params as block parameters when many are specified' do
|
611
|
+
mock_app {
|
612
|
+
get '/:foo/:bar/:baz' do |foo, bar, baz|
|
613
|
+
assert_equal 'abc', foo
|
614
|
+
assert_equal 'def', bar
|
615
|
+
assert_equal 'ghi', baz
|
616
|
+
end
|
617
|
+
}
|
618
|
+
|
619
|
+
get '/abc/def/ghi'
|
620
|
+
assert ok?
|
621
|
+
end
|
622
|
+
|
623
|
+
it 'passes regular expression captures as block parameters' do
|
624
|
+
mock_app {
|
625
|
+
get(/^\/fo(.*)\/ba(.*)/) do |foo, bar|
|
626
|
+
assert_equal 'orooomma', foo
|
627
|
+
assert_equal 'f', bar
|
628
|
+
'looks good'
|
629
|
+
end
|
630
|
+
}
|
631
|
+
|
632
|
+
get '/foorooomma/baf'
|
633
|
+
assert ok?
|
634
|
+
assert_equal 'looks good', body
|
635
|
+
end
|
636
|
+
|
637
|
+
it "supports mixing multiple splat params like /*/foo/*/* as block parameters" do
|
638
|
+
mock_app {
|
639
|
+
get '/*/foo/*/*' do |foo, bar, baz|
|
640
|
+
assert_equal 'bar', foo
|
641
|
+
assert_equal 'bling', bar
|
642
|
+
assert_equal 'baz/boom', baz
|
643
|
+
'looks good'
|
644
|
+
end
|
645
|
+
}
|
646
|
+
|
647
|
+
get '/bar/foo/bling/baz/boom'
|
648
|
+
assert ok?
|
649
|
+
assert_equal 'looks good', body
|
650
|
+
end
|
651
|
+
|
652
|
+
it 'raises an ArgumentError with block arity > 1 and too many values' do
|
653
|
+
mock_app {
|
654
|
+
get '/:foo/:bar/:baz' do |foo, bar|
|
655
|
+
'quux'
|
656
|
+
end
|
657
|
+
}
|
658
|
+
|
659
|
+
assert_raise(ArgumentError) { get '/a/b/c' }
|
660
|
+
end
|
661
|
+
|
662
|
+
it 'raises an ArgumentError with block param arity > 1 and too few values' do
|
663
|
+
mock_app {
|
664
|
+
get '/:foo/:bar' do |foo, bar, baz|
|
665
|
+
'quux'
|
666
|
+
end
|
667
|
+
}
|
668
|
+
|
669
|
+
assert_raise(ArgumentError) { get '/a/b' }
|
670
|
+
end
|
671
|
+
|
672
|
+
it 'succeeds if no block parameters are specified' do
|
673
|
+
mock_app {
|
674
|
+
get '/:foo/:bar' do
|
675
|
+
'quux'
|
676
|
+
end
|
677
|
+
}
|
678
|
+
|
679
|
+
get '/a/b'
|
680
|
+
assert ok?
|
681
|
+
assert_equal 'quux', body
|
682
|
+
end
|
683
|
+
|
684
|
+
it 'passes all params with block param arity -1 (splat args)' do
|
685
|
+
mock_app {
|
686
|
+
get '/:foo/:bar' do |*args|
|
687
|
+
args.join
|
688
|
+
end
|
689
|
+
}
|
690
|
+
|
691
|
+
get '/a/b'
|
692
|
+
assert ok?
|
693
|
+
assert_equal 'ab', body
|
694
|
+
end
|
695
|
+
|
696
|
+
it 'allows custom route-conditions to be set via route options' do
|
697
|
+
protector = Module.new {
|
698
|
+
def protect(*args)
|
699
|
+
condition {
|
700
|
+
unless authorize(params["user"], params["password"])
|
701
|
+
halt 403, "go away"
|
702
|
+
end
|
703
|
+
}
|
704
|
+
end
|
705
|
+
}
|
706
|
+
|
707
|
+
mock_app {
|
708
|
+
register protector
|
709
|
+
|
710
|
+
helpers do
|
711
|
+
def authorize(username, password)
|
712
|
+
username == "foo" && password == "bar"
|
713
|
+
end
|
714
|
+
end
|
715
|
+
|
716
|
+
get "/", :protect => true do
|
717
|
+
"hey"
|
718
|
+
end
|
719
|
+
}
|
720
|
+
|
721
|
+
get "/"
|
722
|
+
assert forbidden?
|
723
|
+
assert_equal "go away", body
|
724
|
+
|
725
|
+
get "/", :user => "foo", :password => "bar"
|
726
|
+
assert ok?
|
727
|
+
assert_equal "hey", body
|
728
|
+
end
|
729
|
+
|
730
|
+
# NOTE Block params behaves differently under 1.8 and 1.9. Under 1.8, block
|
731
|
+
# param arity is lax: declaring a mismatched number of block params results
|
732
|
+
# in a warning. Under 1.9, block param arity is strict: mismatched block
|
733
|
+
# arity raises an ArgumentError.
|
734
|
+
|
735
|
+
if RUBY_VERSION >= '1.9'
|
736
|
+
|
737
|
+
it 'raises an ArgumentError with block param arity 1 and no values' do
|
738
|
+
mock_app {
|
739
|
+
get '/foo' do |foo|
|
740
|
+
'quux'
|
741
|
+
end
|
742
|
+
}
|
743
|
+
|
744
|
+
assert_raise(ArgumentError) { get '/foo' }
|
745
|
+
end
|
746
|
+
|
747
|
+
it 'raises an ArgumentError with block param arity 1 and too many values' do
|
748
|
+
mock_app {
|
749
|
+
get '/:foo/:bar/:baz' do |foo|
|
750
|
+
'quux'
|
751
|
+
end
|
752
|
+
}
|
753
|
+
|
754
|
+
assert_raise(ArgumentError) { get '/a/b/c' }
|
755
|
+
end
|
756
|
+
|
757
|
+
else
|
758
|
+
|
759
|
+
it 'does not raise an ArgumentError with block param arity 1 and no values' do
|
760
|
+
mock_app {
|
761
|
+
get '/foo' do |foo|
|
762
|
+
'quux'
|
763
|
+
end
|
764
|
+
}
|
765
|
+
|
766
|
+
silence_warnings { get '/foo' }
|
767
|
+
assert ok?
|
768
|
+
assert_equal 'quux', body
|
769
|
+
end
|
770
|
+
|
771
|
+
it 'does not raise an ArgumentError with block param arity 1 and too many values' do
|
772
|
+
mock_app {
|
773
|
+
get '/:foo/:bar/:baz' do |foo|
|
774
|
+
'quux'
|
775
|
+
end
|
776
|
+
}
|
777
|
+
|
778
|
+
silence_warnings { get '/a/b/c' }
|
779
|
+
assert ok?
|
780
|
+
assert_equal 'quux', body
|
781
|
+
end
|
782
|
+
|
783
|
+
end
|
784
|
+
|
785
|
+
it "matches routes defined in superclasses" do
|
786
|
+
base = Class.new(Sinatra::Base)
|
787
|
+
base.get('/foo') { 'foo in baseclass' }
|
788
|
+
|
789
|
+
mock_app(base) {
|
790
|
+
get('/bar') { 'bar in subclass' }
|
791
|
+
}
|
792
|
+
|
793
|
+
get '/foo'
|
794
|
+
assert ok?
|
795
|
+
assert_equal 'foo in baseclass', body
|
796
|
+
|
797
|
+
get '/bar'
|
798
|
+
assert ok?
|
799
|
+
assert_equal 'bar in subclass', body
|
800
|
+
end
|
801
|
+
|
802
|
+
it "matches routes in subclasses before superclasses" do
|
803
|
+
base = Class.new(Sinatra::Base)
|
804
|
+
base.get('/foo') { 'foo in baseclass' }
|
805
|
+
base.get('/bar') { 'bar in baseclass' }
|
806
|
+
|
807
|
+
mock_app(base) {
|
808
|
+
get('/foo') { 'foo in subclass' }
|
809
|
+
}
|
810
|
+
|
811
|
+
get '/foo'
|
812
|
+
assert ok?
|
813
|
+
assert_equal 'foo in subclass', body
|
814
|
+
|
815
|
+
get '/bar'
|
816
|
+
assert ok?
|
817
|
+
assert_equal 'bar in baseclass', body
|
818
|
+
end
|
338
819
|
end
|