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.
Files changed (101) hide show
  1. data/.yardopts +4 -0
  2. data/AUTHORS +15 -0
  3. data/CHANGES +524 -1
  4. data/Gemfile +82 -0
  5. data/LICENSE +1 -1
  6. data/README.de.rdoc +2093 -0
  7. data/README.es.rdoc +2091 -0
  8. data/README.fr.rdoc +2116 -0
  9. data/README.hu.rdoc +607 -0
  10. data/README.jp.rdoc +514 -23
  11. data/README.pt-br.rdoc +647 -0
  12. data/README.pt-pt.rdoc +646 -0
  13. data/README.rdoc +1580 -205
  14. data/README.ru.rdoc +2015 -0
  15. data/README.zh.rdoc +1816 -0
  16. data/Rakefile +110 -44
  17. data/examples/chat.rb +61 -0
  18. data/examples/simple.rb +3 -0
  19. data/examples/stream.ru +26 -0
  20. data/lib/sinatra.rb +0 -3
  21. data/lib/sinatra/base.rb +923 -393
  22. data/lib/sinatra/main.rb +9 -7
  23. data/lib/sinatra/showexceptions.rb +37 -4
  24. data/lib/sinatra/version.rb +3 -0
  25. data/sinatra-base.gemspec +15 -91
  26. data/test/base_test.rb +2 -2
  27. data/test/builder_test.rb +32 -2
  28. data/test/coffee_test.rb +92 -0
  29. data/test/contest.rb +62 -28
  30. data/test/creole_test.rb +65 -0
  31. data/test/delegator_test.rb +162 -0
  32. data/test/encoding_test.rb +20 -0
  33. data/test/erb_test.rb +25 -2
  34. data/test/extensions_test.rb +1 -1
  35. data/test/filter_test.rb +226 -8
  36. data/test/haml_test.rb +8 -2
  37. data/test/helper.rb +47 -0
  38. data/test/helpers_test.rb +1287 -80
  39. data/test/integration/app.rb +62 -0
  40. data/test/integration_helper.rb +208 -0
  41. data/test/integration_test.rb +82 -0
  42. data/test/less_test.rb +36 -6
  43. data/test/liquid_test.rb +59 -0
  44. data/test/mapped_error_test.rb +84 -7
  45. data/test/markaby_test.rb +80 -0
  46. data/test/markdown_test.rb +81 -0
  47. data/test/middleware_test.rb +1 -1
  48. data/test/nokogiri_test.rb +69 -0
  49. data/test/rack_test.rb +45 -0
  50. data/test/radius_test.rb +59 -0
  51. data/test/rdoc_test.rb +66 -0
  52. data/test/readme_test.rb +136 -0
  53. data/test/request_test.rb +13 -1
  54. data/test/response_test.rb +21 -2
  55. data/test/result_test.rb +5 -5
  56. data/test/route_added_hook_test.rb +1 -1
  57. data/test/routing_test.rb +328 -13
  58. data/test/sass_test.rb +48 -18
  59. data/test/scss_test.rb +88 -0
  60. data/test/server_test.rb +4 -3
  61. data/test/settings_test.rb +191 -21
  62. data/test/sinatra_test.rb +5 -1
  63. data/test/slim_test.rb +88 -0
  64. data/test/static_test.rb +89 -5
  65. data/test/streaming_test.rb +140 -0
  66. data/test/templates_test.rb +143 -4
  67. data/test/textile_test.rb +65 -0
  68. data/test/views/a/in_a.str +1 -0
  69. data/test/views/ascii.erb +2 -0
  70. data/test/views/b/in_b.str +1 -0
  71. data/test/views/calc.html.erb +1 -0
  72. data/test/views/explicitly_nested.str +1 -0
  73. data/test/views/hello.coffee +1 -0
  74. data/test/views/hello.creole +1 -0
  75. data/test/views/hello.liquid +1 -0
  76. data/test/views/hello.mab +1 -0
  77. data/test/views/hello.md +1 -0
  78. data/test/views/hello.nokogiri +1 -0
  79. data/test/views/hello.radius +1 -0
  80. data/test/views/hello.rdoc +1 -0
  81. data/test/views/hello.sass +1 -1
  82. data/test/views/hello.scss +3 -0
  83. data/test/views/hello.slim +1 -0
  84. data/test/views/hello.str +1 -0
  85. data/test/views/hello.textile +1 -0
  86. data/test/views/hello.yajl +1 -0
  87. data/test/views/layout2.liquid +2 -0
  88. data/test/views/layout2.mab +2 -0
  89. data/test/views/layout2.nokogiri +3 -0
  90. data/test/views/layout2.radius +2 -0
  91. data/test/views/layout2.slim +3 -0
  92. data/test/views/layout2.str +2 -0
  93. data/test/views/nested.str +1 -0
  94. data/test/views/utf8.erb +2 -0
  95. data/test/yajl_test.rb +80 -0
  96. metadata +126 -91
  97. data/lib/sinatra/tilt.rb +0 -746
  98. data/test/erubis_test.rb +0 -82
  99. data/test/views/error.erubis +0 -3
  100. data/test/views/hello.erubis +0 -1
  101. data/test/views/layout2.erubis +0 -2
@@ -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
@@ -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
@@ -1,4 +1,5 @@
1
- require File.dirname(__FILE__) + '/helper'
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
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require File.dirname(__FILE__) + '/helper'
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
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
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
- [205, {'Content-Type' => 'foo/bar'}, 'Hello World']
57
+ [203, {'Content-Type' => 'foo/bar'}, 'Hello World']
58
58
  end
59
59
  }
60
60
 
61
61
  get '/'
62
- assert_equal 205, status
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 TypeError when result is a non two or three tuple Array" do
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(TypeError) { get '/' }
86
+ assert_raise(ArgumentError) { get '/' }
87
87
  end
88
88
 
89
89
  it "sets status when result is a Fixnum status code" do
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  module RouteAddedTest
4
4
  @routes, @procs = [], []
@@ -1,4 +1,5 @@
1
- require File.dirname(__FILE__) + '/helper'
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 . in paths" do
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 $ in paths" do
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 + in paths" do
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 "literally matches () in paths" do
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
- request.env['HTTP_ACCEPT']
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
- request.env['HTTP_ACCEPT']
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
- request.env['HTTP_ACCEPT']
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