sinatra-base 1.0 → 1.4.0
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/.yardopts +4 -0
- data/AUTHORS +15 -0
- data/CHANGES +524 -1
- data/Gemfile +82 -0
- data/LICENSE +1 -1
- data/README.de.rdoc +2093 -0
- data/README.es.rdoc +2091 -0
- data/README.fr.rdoc +2116 -0
- data/README.hu.rdoc +607 -0
- data/README.jp.rdoc +514 -23
- data/README.pt-br.rdoc +647 -0
- data/README.pt-pt.rdoc +646 -0
- data/README.rdoc +1580 -205
- data/README.ru.rdoc +2015 -0
- data/README.zh.rdoc +1816 -0
- data/Rakefile +110 -44
- data/examples/chat.rb +61 -0
- data/examples/simple.rb +3 -0
- data/examples/stream.ru +26 -0
- data/lib/sinatra.rb +0 -3
- data/lib/sinatra/base.rb +923 -393
- data/lib/sinatra/main.rb +9 -7
- data/lib/sinatra/showexceptions.rb +37 -4
- data/lib/sinatra/version.rb +3 -0
- data/sinatra-base.gemspec +15 -91
- data/test/base_test.rb +2 -2
- data/test/builder_test.rb +32 -2
- data/test/coffee_test.rb +92 -0
- data/test/contest.rb +62 -28
- data/test/creole_test.rb +65 -0
- data/test/delegator_test.rb +162 -0
- data/test/encoding_test.rb +20 -0
- data/test/erb_test.rb +25 -2
- data/test/extensions_test.rb +1 -1
- data/test/filter_test.rb +226 -8
- data/test/haml_test.rb +8 -2
- data/test/helper.rb +47 -0
- data/test/helpers_test.rb +1287 -80
- data/test/integration/app.rb +62 -0
- data/test/integration_helper.rb +208 -0
- data/test/integration_test.rb +82 -0
- data/test/less_test.rb +36 -6
- data/test/liquid_test.rb +59 -0
- data/test/mapped_error_test.rb +84 -7
- data/test/markaby_test.rb +80 -0
- data/test/markdown_test.rb +81 -0
- data/test/middleware_test.rb +1 -1
- data/test/nokogiri_test.rb +69 -0
- data/test/rack_test.rb +45 -0
- data/test/radius_test.rb +59 -0
- data/test/rdoc_test.rb +66 -0
- data/test/readme_test.rb +136 -0
- data/test/request_test.rb +13 -1
- data/test/response_test.rb +21 -2
- data/test/result_test.rb +5 -5
- data/test/route_added_hook_test.rb +1 -1
- data/test/routing_test.rb +328 -13
- data/test/sass_test.rb +48 -18
- data/test/scss_test.rb +88 -0
- data/test/server_test.rb +4 -3
- data/test/settings_test.rb +191 -21
- data/test/sinatra_test.rb +5 -1
- data/test/slim_test.rb +88 -0
- data/test/static_test.rb +89 -5
- data/test/streaming_test.rb +140 -0
- data/test/templates_test.rb +143 -4
- data/test/textile_test.rb +65 -0
- data/test/views/a/in_a.str +1 -0
- data/test/views/ascii.erb +2 -0
- data/test/views/b/in_b.str +1 -0
- data/test/views/calc.html.erb +1 -0
- data/test/views/explicitly_nested.str +1 -0
- data/test/views/hello.coffee +1 -0
- data/test/views/hello.creole +1 -0
- data/test/views/hello.liquid +1 -0
- data/test/views/hello.mab +1 -0
- data/test/views/hello.md +1 -0
- data/test/views/hello.nokogiri +1 -0
- data/test/views/hello.radius +1 -0
- data/test/views/hello.rdoc +1 -0
- data/test/views/hello.sass +1 -1
- data/test/views/hello.scss +3 -0
- data/test/views/hello.slim +1 -0
- data/test/views/hello.str +1 -0
- data/test/views/hello.textile +1 -0
- data/test/views/hello.yajl +1 -0
- data/test/views/layout2.liquid +2 -0
- data/test/views/layout2.mab +2 -0
- data/test/views/layout2.nokogiri +3 -0
- data/test/views/layout2.radius +2 -0
- data/test/views/layout2.slim +3 -0
- data/test/views/layout2.str +2 -0
- data/test/views/nested.str +1 -0
- data/test/views/utf8.erb +2 -0
- data/test/yajl_test.rb +80 -0
- metadata +126 -91
- data/lib/sinatra/tilt.rb +0 -746
- data/test/erubis_test.rb +0 -82
- data/test/views/error.erubis +0 -3
- data/test/views/hello.erubis +0 -1
- data/test/views/layout2.erubis +0 -2
data/test/rdoc_test.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rdoc'
|
5
|
+
require 'rdoc/markup/to_html'
|
6
|
+
|
7
|
+
class RdocTest < Test::Unit::TestCase
|
8
|
+
def rdoc_app(&block)
|
9
|
+
mock_app do
|
10
|
+
set :views, File.dirname(__FILE__) + '/views'
|
11
|
+
get '/', &block
|
12
|
+
end
|
13
|
+
get '/'
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'renders inline rdoc strings' do
|
17
|
+
rdoc_app { rdoc '= Hiya' }
|
18
|
+
assert ok?
|
19
|
+
assert_body /<h1[^>]*>Hiya<\/h1>/
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'renders .rdoc files in views path' do
|
23
|
+
rdoc_app { rdoc :hello }
|
24
|
+
assert ok?
|
25
|
+
assert_body /<h1[^>]*>Hello From RDoc<\/h1>/
|
26
|
+
end
|
27
|
+
|
28
|
+
it "raises error if template not found" do
|
29
|
+
mock_app { get('/') { rdoc :no_such_template } }
|
30
|
+
assert_raise(Errno::ENOENT) { get('/') }
|
31
|
+
end
|
32
|
+
|
33
|
+
it "renders with inline layouts" do
|
34
|
+
mock_app do
|
35
|
+
layout { 'THIS. IS. #{yield.upcase}!' }
|
36
|
+
get('/') { rdoc 'Sparta', :layout_engine => :str }
|
37
|
+
end
|
38
|
+
get '/'
|
39
|
+
assert ok?
|
40
|
+
assert_like 'THIS. IS. <P>SPARTA</P>!', body
|
41
|
+
end
|
42
|
+
|
43
|
+
it "renders with file layouts" do
|
44
|
+
rdoc_app { rdoc 'Hello World', :layout => :layout2, :layout_engine => :erb }
|
45
|
+
assert ok?
|
46
|
+
assert_body "ERB Layout!\n<p>Hello World</p>"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "can be used in a nested fashion for partials and whatnot" do
|
50
|
+
mock_app do
|
51
|
+
template(:inner) { "hi" }
|
52
|
+
template(:outer) { "<outer><%= rdoc :inner %></outer>" }
|
53
|
+
get '/' do
|
54
|
+
erb :outer
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
get '/'
|
59
|
+
assert ok?
|
60
|
+
assert_like '<outer><p>hi</p></outer>', body
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
rescue LoadError
|
65
|
+
warn "#{$!.to_s}: skipping rdoc tests"
|
66
|
+
end
|
data/test/readme_test.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
# Tests to check if all the README examples work.
|
2
|
+
require File.expand_path('../helper', __FILE__)
|
3
|
+
|
4
|
+
class ReadmeTest < Test::Unit::TestCase
|
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 '/' do
|
15
|
+
".. show something .."
|
16
|
+
end
|
17
|
+
|
18
|
+
post '/' do
|
19
|
+
".. create something .."
|
20
|
+
end
|
21
|
+
|
22
|
+
put '/' do
|
23
|
+
".. replace something .."
|
24
|
+
end
|
25
|
+
|
26
|
+
patch '/' do
|
27
|
+
".. modify something .."
|
28
|
+
end
|
29
|
+
|
30
|
+
delete '/' do
|
31
|
+
".. annihilate something .."
|
32
|
+
end
|
33
|
+
|
34
|
+
options '/' do
|
35
|
+
".. appease something .."
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
get '/'
|
40
|
+
assert_body '.. show something ..'
|
41
|
+
|
42
|
+
post '/'
|
43
|
+
assert_body '.. create something ..'
|
44
|
+
|
45
|
+
put '/'
|
46
|
+
assert_body '.. replace something ..'
|
47
|
+
|
48
|
+
patch '/'
|
49
|
+
assert_body '.. modify something ..'
|
50
|
+
|
51
|
+
delete '/'
|
52
|
+
assert_body '.. annihilate something ..'
|
53
|
+
|
54
|
+
options '/'
|
55
|
+
assert_body '.. appease something ..'
|
56
|
+
end
|
57
|
+
|
58
|
+
example do
|
59
|
+
mock_app do
|
60
|
+
get '/hello/:name' do
|
61
|
+
# matches "GET /hello/foo" and "GET /hello/bar"
|
62
|
+
# params[:name] is 'foo' or 'bar'
|
63
|
+
"Hello #{params[:name]}!"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
get '/hello/foo'
|
68
|
+
assert_body 'Hello foo!'
|
69
|
+
|
70
|
+
get '/hello/bar'
|
71
|
+
assert_body 'Hello bar!'
|
72
|
+
end
|
73
|
+
|
74
|
+
example do
|
75
|
+
mock_app do
|
76
|
+
get '/hello/:name' do |n|
|
77
|
+
"Hello #{n}!"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
get '/hello/foo'
|
82
|
+
assert_body 'Hello foo!'
|
83
|
+
|
84
|
+
get '/hello/bar'
|
85
|
+
assert_body 'Hello bar!'
|
86
|
+
end
|
87
|
+
|
88
|
+
example do
|
89
|
+
mock_app do
|
90
|
+
get '/say/*/to/*' do
|
91
|
+
# matches /say/hello/to/world
|
92
|
+
params[:splat].inspect # => ["hello", "world"]
|
93
|
+
end
|
94
|
+
|
95
|
+
get '/download/*.*' do
|
96
|
+
# matches /download/path/to/file.xml
|
97
|
+
params[:splat].inspect # => ["path/to/file", "xml"]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
get "/say/hello/to/world"
|
102
|
+
assert_body '["hello", "world"]'
|
103
|
+
|
104
|
+
get "/download/path/to/file.xml"
|
105
|
+
assert_body '["path/to/file", "xml"]'
|
106
|
+
end
|
107
|
+
|
108
|
+
example do
|
109
|
+
mock_app do
|
110
|
+
get %r{/hello/([\w]+)} do
|
111
|
+
"Hello, #{params[:captures].first}!"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
get '/hello/foo'
|
116
|
+
assert_body 'Hello, foo!'
|
117
|
+
|
118
|
+
get '/hello/bar'
|
119
|
+
assert_body 'Hello, bar!'
|
120
|
+
end
|
121
|
+
|
122
|
+
example do
|
123
|
+
mock_app do
|
124
|
+
get %r{/hello/([\w]+)} do |c|
|
125
|
+
"Hello, #{c}!"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
get '/hello/foo'
|
130
|
+
assert_body 'Hello, foo!'
|
131
|
+
|
132
|
+
get '/hello/bar'
|
133
|
+
assert_body 'Hello, bar!'
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/test/request_test.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
require 'stringio'
|
2
3
|
|
3
4
|
class RequestTest < Test::Unit::TestCase
|
4
5
|
it 'responds to #user_agent' do
|
@@ -30,4 +31,15 @@ class RequestTest < Test::Unit::TestCase
|
|
30
31
|
request = Sinatra::Request.new('HTTP_X_FORWARDED_PROTO' => 'https')
|
31
32
|
assert request.secure?
|
32
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
|
+
params = 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
|
33
45
|
end
|
data/test/response_test.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require File.
|
3
|
+
require File.expand_path('../helper', __FILE__)
|
4
4
|
|
5
5
|
class ResponseTest < Test::Unit::TestCase
|
6
6
|
setup do
|
@@ -22,7 +22,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
22
22
|
it 'writes to body' do
|
23
23
|
@response.body = 'Hello'
|
24
24
|
@response.write ' World'
|
25
|
-
assert_equal 'Hello World', @response.body
|
25
|
+
assert_equal 'Hello World', @response.body.join
|
26
26
|
end
|
27
27
|
|
28
28
|
[204, 304].each do |status_code|
|
@@ -39,4 +39,23 @@ class ResponseTest < Test::Unit::TestCase
|
|
39
39
|
assert_equal '14', headers['Content-Length']
|
40
40
|
assert_equal @response.body, body
|
41
41
|
end
|
42
|
+
|
43
|
+
it 'does not call #to_ary or #inject on the body' do
|
44
|
+
object = Object.new
|
45
|
+
def object.inject(*) fail 'called' end
|
46
|
+
def object.to_ary(*) fail 'called' end
|
47
|
+
def object.each(*) end
|
48
|
+
@response.body = object
|
49
|
+
assert @response.finish
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'does not nest a Sinatra::Response' do
|
53
|
+
@response.body = Sinatra::Response.new ["foo"]
|
54
|
+
assert_equal @response.body, ["foo"]
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'does not nest a Rack::Response' do
|
58
|
+
@response.body = Rack::Response.new ["foo"]
|
59
|
+
assert_equal @response.body, ["foo"]
|
60
|
+
end
|
42
61
|
end
|
data/test/result_test.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
2
|
|
3
3
|
class ResultTest < Test::Unit::TestCase
|
4
4
|
it "sets response.body when result is a String" do
|
@@ -54,12 +54,12 @@ class ResultTest < Test::Unit::TestCase
|
|
54
54
|
it "sets status, headers, and body when result is a Rack response tuple" do
|
55
55
|
mock_app {
|
56
56
|
get '/' do
|
57
|
-
[
|
57
|
+
[203, {'Content-Type' => 'foo/bar'}, 'Hello World']
|
58
58
|
end
|
59
59
|
}
|
60
60
|
|
61
61
|
get '/'
|
62
|
-
assert_equal
|
62
|
+
assert_equal 203, status
|
63
63
|
assert_equal 'foo/bar', response['Content-Type']
|
64
64
|
assert_equal 'Hello World', body
|
65
65
|
end
|
@@ -76,14 +76,14 @@ class ResultTest < Test::Unit::TestCase
|
|
76
76
|
assert_equal 'formula of', body
|
77
77
|
end
|
78
78
|
|
79
|
-
it "raises a
|
79
|
+
it "raises a ArgumentError when result is a non two or three tuple Array" do
|
80
80
|
mock_app {
|
81
81
|
get '/' do
|
82
82
|
[409, 'formula of', 'something else', 'even more']
|
83
83
|
end
|
84
84
|
}
|
85
85
|
|
86
|
-
assert_raise(
|
86
|
+
assert_raise(ArgumentError) { get '/' }
|
87
87
|
end
|
88
88
|
|
89
89
|
it "sets status when result is a Fixnum status code" do
|
data/test/routing_test.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
# I like coding: UTF-8
|
2
|
+
require File.expand_path('../helper', __FILE__)
|
2
3
|
|
3
4
|
# Helper method for easy route pattern matching testing
|
4
5
|
def route_def(pattern)
|
@@ -22,7 +23,7 @@ class RegexpLookAlike
|
|
22
23
|
end
|
23
24
|
|
24
25
|
class RoutingTest < Test::Unit::TestCase
|
25
|
-
%w[get put post delete].each do |verb|
|
26
|
+
%w[get put post delete options patch].each do |verb|
|
26
27
|
it "defines #{verb.upcase} request handlers with #{verb}" do
|
27
28
|
mock_app {
|
28
29
|
send verb, '/hello' do
|
@@ -69,6 +70,21 @@ class RoutingTest < Test::Unit::TestCase
|
|
69
70
|
assert_equal 'pass', response.headers['X-Cascade']
|
70
71
|
end
|
71
72
|
|
73
|
+
it "allows using unicode" do
|
74
|
+
mock_app do
|
75
|
+
get('/föö') { }
|
76
|
+
end
|
77
|
+
get '/f%C3%B6%C3%B6'
|
78
|
+
assert_equal 200, status
|
79
|
+
end
|
80
|
+
|
81
|
+
it "it handles encoded slashes correctly" do
|
82
|
+
mock_app { get("/:a") { |a| a } }
|
83
|
+
get '/foo%2Fbar'
|
84
|
+
assert_equal 200, status
|
85
|
+
assert_body "foo/bar"
|
86
|
+
end
|
87
|
+
|
72
88
|
it "overrides the content-type in error handlers" do
|
73
89
|
mock_app {
|
74
90
|
before { content_type 'text/plain' }
|
@@ -80,10 +96,40 @@ class RoutingTest < Test::Unit::TestCase
|
|
80
96
|
|
81
97
|
get '/foo'
|
82
98
|
assert_equal 404, status
|
83
|
-
assert_equal 'text/html', response["Content-Type"]
|
99
|
+
assert_equal 'text/html;charset=utf-8', response["Content-Type"]
|
84
100
|
assert_equal "<h1>Not Found</h1>", response.body
|
85
101
|
end
|
86
102
|
|
103
|
+
it 'matches empty PATH_INFO to "/" if no route is defined for ""' do
|
104
|
+
mock_app do
|
105
|
+
get '/' do
|
106
|
+
'worked'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
get '/', {}, "PATH_INFO" => ""
|
111
|
+
assert ok?
|
112
|
+
assert_equal 'worked', body
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'matches empty PATH_INFO to "" if a route is defined for ""' do
|
116
|
+
mock_app do
|
117
|
+
disable :protection
|
118
|
+
|
119
|
+
get '/' do
|
120
|
+
'did not work'
|
121
|
+
end
|
122
|
+
|
123
|
+
get '' do
|
124
|
+
'worked'
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
get '/', {}, "PATH_INFO" => ""
|
129
|
+
assert ok?
|
130
|
+
assert_equal 'worked', body
|
131
|
+
end
|
132
|
+
|
87
133
|
it 'takes multiple definitions of a route' do
|
88
134
|
mock_app {
|
89
135
|
user_agent(/Foo/)
|
@@ -158,6 +204,44 @@ class RoutingTest < Test::Unit::TestCase
|
|
158
204
|
assert_equal "foo=;bar=", body
|
159
205
|
end
|
160
206
|
|
207
|
+
it "supports named captures like %r{/hello/(?<person>[^/?#]+)} on Ruby >= 1.9" do
|
208
|
+
next if RUBY_VERSION < '1.9'
|
209
|
+
mock_app {
|
210
|
+
get Regexp.new('/hello/(?<person>[^/?#]+)') do
|
211
|
+
"Hello #{params['person']}"
|
212
|
+
end
|
213
|
+
}
|
214
|
+
get '/hello/Frank'
|
215
|
+
assert_equal 'Hello Frank', body
|
216
|
+
end
|
217
|
+
|
218
|
+
it "supports optional named captures like %r{/page(?<format>.[^/?#]+)?} on Ruby >= 1.9" do
|
219
|
+
next if RUBY_VERSION < '1.9'
|
220
|
+
mock_app {
|
221
|
+
get Regexp.new('/page(?<format>.[^/?#]+)?') do
|
222
|
+
"format=#{params[:format]}"
|
223
|
+
end
|
224
|
+
}
|
225
|
+
|
226
|
+
get '/page.html'
|
227
|
+
assert ok?
|
228
|
+
assert_equal "format=.html", body
|
229
|
+
|
230
|
+
get '/page.xml'
|
231
|
+
assert ok?
|
232
|
+
assert_equal "format=.xml", body
|
233
|
+
|
234
|
+
get '/page'
|
235
|
+
assert ok?
|
236
|
+
assert_equal "format=", body
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'does not concatinate params with the same name' do
|
240
|
+
mock_app { get('/:foo') { params[:foo] } }
|
241
|
+
get '/a?foo=b'
|
242
|
+
assert_body 'a'
|
243
|
+
end
|
244
|
+
|
161
245
|
it "supports single splat params like /*" do
|
162
246
|
mock_app {
|
163
247
|
get '/*' do
|
@@ -226,7 +310,7 @@ class RoutingTest < Test::Unit::TestCase
|
|
226
310
|
assert_equal 'right on', body
|
227
311
|
end
|
228
312
|
|
229
|
-
it "literally matches
|
313
|
+
it "literally matches dot in paths" do
|
230
314
|
route_def '/test.bar'
|
231
315
|
|
232
316
|
get '/test.bar'
|
@@ -235,14 +319,14 @@ class RoutingTest < Test::Unit::TestCase
|
|
235
319
|
assert not_found?
|
236
320
|
end
|
237
321
|
|
238
|
-
it "literally matches
|
322
|
+
it "literally matches dollar sign in paths" do
|
239
323
|
route_def '/test$/'
|
240
324
|
|
241
325
|
get '/test$/'
|
242
326
|
assert ok?
|
243
327
|
end
|
244
328
|
|
245
|
-
it "literally matches
|
329
|
+
it "literally matches plus sign in paths" do
|
246
330
|
route_def '/te+st/'
|
247
331
|
|
248
332
|
get '/te%2Bst/'
|
@@ -251,7 +335,18 @@ class RoutingTest < Test::Unit::TestCase
|
|
251
335
|
assert not_found?
|
252
336
|
end
|
253
337
|
|
254
|
-
it "
|
338
|
+
it "converts plus sign into space as the value of a named param" do
|
339
|
+
mock_app {
|
340
|
+
get '/:test' do
|
341
|
+
params["test"]
|
342
|
+
end
|
343
|
+
}
|
344
|
+
get '/bob+ross'
|
345
|
+
assert ok?
|
346
|
+
assert_equal 'bob ross', body
|
347
|
+
end
|
348
|
+
|
349
|
+
it "literally matches parens in paths" do
|
255
350
|
route_def '/test(bar)/'
|
256
351
|
|
257
352
|
get '/test(bar)/'
|
@@ -282,6 +377,30 @@ class RoutingTest < Test::Unit::TestCase
|
|
282
377
|
assert_equal 'well, alright', body
|
283
378
|
end
|
284
379
|
|
380
|
+
it "exposes params nested within arrays with indifferent hash" do
|
381
|
+
mock_app {
|
382
|
+
get '/testme' do
|
383
|
+
assert_equal 'baz', params['bar'][0]['foo']
|
384
|
+
assert_equal 'baz', params['bar'][0][:foo]
|
385
|
+
'well, alright'
|
386
|
+
end
|
387
|
+
}
|
388
|
+
get '/testme?bar[][foo]=baz'
|
389
|
+
assert_equal 'well, alright', body
|
390
|
+
end
|
391
|
+
|
392
|
+
it "supports arrays within params" do
|
393
|
+
mock_app {
|
394
|
+
get '/foo' do
|
395
|
+
assert_equal ['A', 'B'], params['bar']
|
396
|
+
'looks good'
|
397
|
+
end
|
398
|
+
}
|
399
|
+
get '/foo?bar[]=A&bar[]=B'
|
400
|
+
assert ok?
|
401
|
+
assert_equal 'looks good', body
|
402
|
+
end
|
403
|
+
|
285
404
|
it "supports deeply nested params" do
|
286
405
|
expected_params = {
|
287
406
|
"emacs" => {
|
@@ -344,6 +463,18 @@ class RoutingTest < Test::Unit::TestCase
|
|
344
463
|
assert_equal 'looks good', body
|
345
464
|
end
|
346
465
|
|
466
|
+
it "matches paths that include ampersands" do
|
467
|
+
mock_app {
|
468
|
+
get '/:name' do
|
469
|
+
'looks good'
|
470
|
+
end
|
471
|
+
}
|
472
|
+
|
473
|
+
get '/foo&bar'
|
474
|
+
assert ok?
|
475
|
+
assert_equal 'looks good', body
|
476
|
+
end
|
477
|
+
|
347
478
|
it "URL decodes named parameters and splats" do
|
348
479
|
mock_app {
|
349
480
|
get '/:foo/*' do
|
@@ -441,6 +572,17 @@ class RoutingTest < Test::Unit::TestCase
|
|
441
572
|
assert_equal 'HelloWorldHowAreYou', body
|
442
573
|
end
|
443
574
|
|
575
|
+
it 'sets response.status with halt' do
|
576
|
+
status_was = nil
|
577
|
+
mock_app do
|
578
|
+
after { status_was = status }
|
579
|
+
get('/') { halt 500, 'error' }
|
580
|
+
end
|
581
|
+
get '/'
|
582
|
+
assert_status 500
|
583
|
+
assert_equal 500, status_was
|
584
|
+
end
|
585
|
+
|
444
586
|
it "transitions to the next matching route on pass" do
|
445
587
|
mock_app {
|
446
588
|
get '/:foo' do
|
@@ -578,6 +720,18 @@ class RoutingTest < Test::Unit::TestCase
|
|
578
720
|
assert_equal 'Hello World', body
|
579
721
|
end
|
580
722
|
|
723
|
+
it "treats missing user agent like an empty string" do
|
724
|
+
mock_app do
|
725
|
+
user_agent(/.*/)
|
726
|
+
get '/' do
|
727
|
+
"Hello World"
|
728
|
+
end
|
729
|
+
end
|
730
|
+
get '/'
|
731
|
+
assert_equal 200, status
|
732
|
+
assert_equal 'Hello World', body
|
733
|
+
end
|
734
|
+
|
581
735
|
it "makes captures in user agent pattern available in params[:agent]" do
|
582
736
|
mock_app {
|
583
737
|
user_agent(/Foo (.*)/)
|
@@ -593,17 +747,52 @@ class RoutingTest < Test::Unit::TestCase
|
|
593
747
|
it "filters by accept header" do
|
594
748
|
mock_app {
|
595
749
|
get '/', :provides => :xml do
|
596
|
-
|
750
|
+
env['HTTP_ACCEPT']
|
751
|
+
end
|
752
|
+
get '/foo', :provides => :html do
|
753
|
+
env['HTTP_ACCEPT']
|
597
754
|
end
|
598
755
|
}
|
599
756
|
|
600
757
|
get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
|
601
758
|
assert ok?
|
602
759
|
assert_equal 'application/xml', body
|
603
|
-
assert_equal 'application/xml', response.headers['Content-Type']
|
760
|
+
assert_equal 'application/xml;charset=utf-8', response.headers['Content-Type']
|
604
761
|
|
605
762
|
get '/', {}, { :accept => 'text/html' }
|
606
763
|
assert !ok?
|
764
|
+
|
765
|
+
get '/foo', {}, { 'HTTP_ACCEPT' => 'text/html;q=0.9' }
|
766
|
+
assert ok?
|
767
|
+
assert_equal 'text/html;q=0.9', body
|
768
|
+
|
769
|
+
get '/foo', {}, { 'HTTP_ACCEPT' => '' }
|
770
|
+
assert !ok?
|
771
|
+
end
|
772
|
+
|
773
|
+
it "filters by current Content-Type" do
|
774
|
+
mock_app do
|
775
|
+
before('/txt') { content_type :txt }
|
776
|
+
get('*', :provides => :txt) { 'txt' }
|
777
|
+
|
778
|
+
before('/html') { content_type :html }
|
779
|
+
get('*', :provides => :html) { 'html' }
|
780
|
+
end
|
781
|
+
|
782
|
+
get '/', {}, { 'HTTP_ACCEPT' => '*' }
|
783
|
+
assert ok?
|
784
|
+
assert_equal 'text/plain;charset=utf-8', response.headers['Content-Type']
|
785
|
+
assert_body 'txt'
|
786
|
+
|
787
|
+
get '/txt', {}, { 'HTTP_ACCEPT' => 'text/plain' }
|
788
|
+
assert ok?
|
789
|
+
assert_equal 'text/plain;charset=utf-8', response.headers['Content-Type']
|
790
|
+
assert_body 'txt'
|
791
|
+
|
792
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'text/html' }
|
793
|
+
assert ok?
|
794
|
+
assert_equal 'text/html;charset=utf-8', response.headers['Content-Type']
|
795
|
+
assert_body 'html'
|
607
796
|
end
|
608
797
|
|
609
798
|
it "allows multiple mime types for accept header" do
|
@@ -611,7 +800,7 @@ class RoutingTest < Test::Unit::TestCase
|
|
611
800
|
|
612
801
|
mock_app {
|
613
802
|
get '/', :provides => types do
|
614
|
-
|
803
|
+
env['HTTP_ACCEPT']
|
615
804
|
end
|
616
805
|
}
|
617
806
|
|
@@ -626,7 +815,7 @@ class RoutingTest < Test::Unit::TestCase
|
|
626
815
|
it 'degrades gracefully when optional accept header is not provided' do
|
627
816
|
mock_app {
|
628
817
|
get '/', :provides => :xml do
|
629
|
-
|
818
|
+
env['HTTP_ACCEPT']
|
630
819
|
end
|
631
820
|
get '/' do
|
632
821
|
'default'
|
@@ -637,6 +826,77 @@ class RoutingTest < Test::Unit::TestCase
|
|
637
826
|
assert_equal 'default', body
|
638
827
|
end
|
639
828
|
|
829
|
+
it 'respects user agent prefferences for the content type' do
|
830
|
+
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
831
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5,text/html;q=0.8' }
|
832
|
+
assert_body 'text/html;charset=utf-8'
|
833
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.8,text/html;q=0.5' }
|
834
|
+
assert_body 'image/png'
|
835
|
+
end
|
836
|
+
|
837
|
+
it 'accepts generic types' do
|
838
|
+
mock_app do
|
839
|
+
get('/', :provides => :xml) { content_type }
|
840
|
+
get('/') { 'no match' }
|
841
|
+
end
|
842
|
+
get '/'
|
843
|
+
assert_body 'no match'
|
844
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'foo/*' }
|
845
|
+
assert_body 'no match'
|
846
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'application/*' }
|
847
|
+
assert_body 'application/xml;charset=utf-8'
|
848
|
+
get '/', {}, { 'HTTP_ACCEPT' => '*/*' }
|
849
|
+
assert_body 'application/xml;charset=utf-8'
|
850
|
+
end
|
851
|
+
|
852
|
+
it 'prefers concrete over partly generic types' do
|
853
|
+
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
854
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'image/*, text/html' }
|
855
|
+
assert_body 'text/html;charset=utf-8'
|
856
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'image/png, text/*' }
|
857
|
+
assert_body 'image/png'
|
858
|
+
end
|
859
|
+
|
860
|
+
it 'prefers concrete over fully generic types' do
|
861
|
+
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
862
|
+
get '/', {}, { 'HTTP_ACCEPT' => '*/*, text/html' }
|
863
|
+
assert_body 'text/html;charset=utf-8'
|
864
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'image/png, */*' }
|
865
|
+
assert_body 'image/png'
|
866
|
+
end
|
867
|
+
|
868
|
+
it 'prefers partly generic over fully generic types' do
|
869
|
+
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
870
|
+
get '/', {}, { 'HTTP_ACCEPT' => '*/*, text/*' }
|
871
|
+
assert_body 'text/html;charset=utf-8'
|
872
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'image/*, */*' }
|
873
|
+
assert_body 'image/png'
|
874
|
+
end
|
875
|
+
|
876
|
+
it 'respects quality with generic types' do
|
877
|
+
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
878
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'image/*;q=1, text/html;q=0' }
|
879
|
+
assert_body 'image/png'
|
880
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5, text/*;q=0.7' }
|
881
|
+
assert_body 'text/html;charset=utf-8'
|
882
|
+
end
|
883
|
+
|
884
|
+
it 'accepts both text/javascript and application/javascript for js' do
|
885
|
+
mock_app { get('/', :provides => :js) { content_type }}
|
886
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'application/javascript' }
|
887
|
+
assert_body 'application/javascript;charset=utf-8'
|
888
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'text/javascript' }
|
889
|
+
assert_body 'text/javascript;charset=utf-8'
|
890
|
+
end
|
891
|
+
|
892
|
+
it 'accepts both text/xml and application/xml for xml' do
|
893
|
+
mock_app { get('/', :provides => :xml) { content_type }}
|
894
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
|
895
|
+
assert_body 'application/xml;charset=utf-8'
|
896
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'text/xml' }
|
897
|
+
assert_body 'text/xml;charset=utf-8'
|
898
|
+
end
|
899
|
+
|
640
900
|
it 'passes a single url param as block parameters when one param is specified' do
|
641
901
|
mock_app {
|
642
902
|
get '/:foo' do |foo|
|
@@ -691,11 +951,11 @@ class RoutingTest < Test::Unit::TestCase
|
|
691
951
|
end
|
692
952
|
|
693
953
|
it 'raises an ArgumentError with block arity > 1 and too many values' do
|
694
|
-
mock_app
|
954
|
+
mock_app do
|
695
955
|
get '/:foo/:bar/:baz' do |foo, bar|
|
696
956
|
'quux'
|
697
957
|
end
|
698
|
-
|
958
|
+
end
|
699
959
|
|
700
960
|
assert_raise(ArgumentError) { get '/a/b/c' }
|
701
961
|
end
|
@@ -857,4 +1117,59 @@ class RoutingTest < Test::Unit::TestCase
|
|
857
1117
|
assert ok?
|
858
1118
|
assert_equal 'bar in baseclass', body
|
859
1119
|
end
|
1120
|
+
|
1121
|
+
it "adds hostname condition when it is in options" do
|
1122
|
+
mock_app {
|
1123
|
+
get '/foo', :host => 'host' do
|
1124
|
+
'foo'
|
1125
|
+
end
|
1126
|
+
}
|
1127
|
+
|
1128
|
+
get '/foo'
|
1129
|
+
assert not_found?
|
1130
|
+
end
|
1131
|
+
|
1132
|
+
it 'allows using call to fire another request internally' do
|
1133
|
+
mock_app do
|
1134
|
+
get '/foo' do
|
1135
|
+
status, headers, body = call env.merge("PATH_INFO" => '/bar')
|
1136
|
+
[status, headers, body.each.map(&:upcase)]
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
get '/bar' do
|
1140
|
+
"bar"
|
1141
|
+
end
|
1142
|
+
end
|
1143
|
+
|
1144
|
+
get '/foo'
|
1145
|
+
assert ok?
|
1146
|
+
assert_body "BAR"
|
1147
|
+
end
|
1148
|
+
|
1149
|
+
it 'plays well with other routing middleware' do
|
1150
|
+
middleware = Sinatra.new
|
1151
|
+
inner_app = Sinatra.new { get('/foo') { 'hello' } }
|
1152
|
+
builder = Rack::Builder.new do
|
1153
|
+
use middleware
|
1154
|
+
map('/test') { run inner_app }
|
1155
|
+
end
|
1156
|
+
|
1157
|
+
@app = builder.to_app
|
1158
|
+
get '/test/foo'
|
1159
|
+
assert ok?
|
1160
|
+
assert_body 'hello'
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
it 'returns the route signature' do
|
1164
|
+
signature = list = nil
|
1165
|
+
|
1166
|
+
mock_app do
|
1167
|
+
signature = post('/') { }
|
1168
|
+
list = routes['POST']
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
assert_equal Array, signature.class
|
1172
|
+
assert_equal 4, signature.length
|
1173
|
+
assert list.include?(signature)
|
1174
|
+
end
|
860
1175
|
end
|