sinatra-base 1.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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