darkhelmet-sinatra 0.9.1.1 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/AUTHORS +2 -0
  2. data/CHANGES +180 -0
  3. data/LICENSE +1 -1
  4. data/README.jp.rdoc +552 -0
  5. data/README.rdoc +177 -38
  6. data/Rakefile +18 -25
  7. data/lib/sinatra.rb +1 -2
  8. data/lib/sinatra/base.rb +405 -305
  9. data/lib/sinatra/main.rb +5 -24
  10. data/lib/sinatra/showexceptions.rb +303 -0
  11. data/lib/sinatra/tilt.rb +509 -0
  12. data/sinatra.gemspec +21 -51
  13. data/test/base_test.rb +123 -93
  14. data/test/builder_test.rb +2 -1
  15. data/test/contest.rb +64 -0
  16. data/test/erb_test.rb +1 -1
  17. data/test/erubis_test.rb +82 -0
  18. data/test/extensions_test.rb +24 -8
  19. data/test/filter_test.rb +99 -3
  20. data/test/haml_test.rb +25 -3
  21. data/test/helper.rb +43 -48
  22. data/test/helpers_test.rb +500 -424
  23. data/test/mapped_error_test.rb +163 -137
  24. data/test/middleware_test.rb +3 -3
  25. data/test/request_test.rb +16 -1
  26. data/test/response_test.rb +2 -2
  27. data/test/result_test.rb +1 -1
  28. data/test/route_added_hook_test.rb +59 -0
  29. data/test/routing_test.rb +170 -22
  30. data/test/sass_test.rb +44 -1
  31. data/test/server_test.rb +19 -13
  32. data/test/sinatra_test.rb +1 -1
  33. data/test/static_test.rb +9 -2
  34. data/test/templates_test.rb +78 -11
  35. data/test/views/error.builder +3 -0
  36. data/test/views/error.erb +3 -0
  37. data/test/views/error.erubis +3 -0
  38. data/test/views/error.haml +3 -0
  39. data/test/views/error.sass +2 -0
  40. data/test/views/foo/hello.test +1 -0
  41. data/test/views/hello.erubis +1 -0
  42. data/test/views/layout2.erubis +2 -0
  43. metadata +37 -55
  44. data/compat/app_test.rb +0 -282
  45. data/compat/application_test.rb +0 -262
  46. data/compat/builder_test.rb +0 -101
  47. data/compat/compat_test.rb +0 -12
  48. data/compat/custom_error_test.rb +0 -62
  49. data/compat/erb_test.rb +0 -136
  50. data/compat/events_test.rb +0 -78
  51. data/compat/filter_test.rb +0 -30
  52. data/compat/haml_test.rb +0 -233
  53. data/compat/helper.rb +0 -30
  54. data/compat/mapped_error_test.rb +0 -72
  55. data/compat/pipeline_test.rb +0 -45
  56. data/compat/public/foo.xml +0 -1
  57. data/compat/sass_test.rb +0 -57
  58. data/compat/sessions_test.rb +0 -42
  59. data/compat/streaming_test.rb +0 -133
  60. data/compat/sym_params_test.rb +0 -19
  61. data/compat/template_test.rb +0 -30
  62. data/compat/use_in_file_templates_test.rb +0 -47
  63. data/compat/views/foo.builder +0 -1
  64. data/compat/views/foo.erb +0 -1
  65. data/compat/views/foo.haml +0 -1
  66. data/compat/views/foo.sass +0 -2
  67. data/compat/views/foo_layout.erb +0 -2
  68. data/compat/views/foo_layout.haml +0 -2
  69. data/compat/views/layout_test/foo.builder +0 -1
  70. data/compat/views/layout_test/foo.erb +0 -1
  71. data/compat/views/layout_test/foo.haml +0 -1
  72. data/compat/views/layout_test/foo.sass +0 -2
  73. data/compat/views/layout_test/layout.builder +0 -3
  74. data/compat/views/layout_test/layout.erb +0 -1
  75. data/compat/views/layout_test/layout.haml +0 -1
  76. data/compat/views/layout_test/layout.sass +0 -2
  77. data/compat/views/no_layout/no_layout.builder +0 -1
  78. data/compat/views/no_layout/no_layout.haml +0 -1
  79. data/lib/sinatra/compat.rb +0 -250
  80. data/lib/sinatra/test.rb +0 -126
  81. data/lib/sinatra/test/bacon.rb +0 -19
  82. data/lib/sinatra/test/rspec.rb +0 -13
  83. data/lib/sinatra/test/spec.rb +0 -11
  84. data/lib/sinatra/test/unit.rb +0 -13
  85. data/test/data/reload_app_file.rb +0 -3
  86. data/test/options_test.rb +0 -374
  87. data/test/reload_test.rb +0 -68
  88. data/test/test_test.rb +0 -144
@@ -6,155 +6,181 @@ end
6
6
  class FooNotFound < Sinatra::NotFound
7
7
  end
8
8
 
9
- describe 'Exception Mappings' do
10
- it 'invokes handlers registered with ::error when raised' do
11
- mock_app {
12
- set :raise_errors, false
13
- error(FooError) { 'Foo!' }
14
- get '/' do
15
- raise FooError
16
- end
17
- }
18
- get '/'
19
- assert_equal 500, status
20
- assert_equal 'Foo!', body
9
+ class MappedErrorTest < Test::Unit::TestCase
10
+ def test_default
11
+ assert true
21
12
  end
22
13
 
23
- it 'uses the Exception handler if no matching handler found' do
24
- mock_app {
25
- set :raise_errors, false
26
- error(Exception) { 'Exception!' }
27
- get '/' do
28
- raise FooError
29
- end
30
- }
31
- get '/'
32
- assert_equal 500, status
33
- assert_equal 'Exception!', body
34
- end
35
-
36
- it "sets env['sinatra.error'] to the rescued exception" do
37
- mock_app {
38
- set :raise_errors, false
39
- error(FooError) {
40
- assert env.include?('sinatra.error')
41
- assert env['sinatra.error'].kind_of?(FooError)
42
- 'looks good'
14
+ describe 'Exception Mappings' do
15
+ it 'invokes handlers registered with ::error when raised' do
16
+ mock_app {
17
+ set :raise_errors, false
18
+ error(FooError) { 'Foo!' }
19
+ get '/' do
20
+ raise FooError
21
+ end
22
+ }
23
+ get '/'
24
+ assert_equal 500, status
25
+ assert_equal 'Foo!', body
26
+ end
27
+
28
+ it 'uses the Exception handler if no matching handler found' do
29
+ mock_app {
30
+ set :raise_errors, false
31
+ error(Exception) { 'Exception!' }
32
+ get '/' do
33
+ raise FooError
34
+ end
43
35
  }
44
- get '/' do
45
- raise FooError
46
- end
47
- }
48
- get '/'
49
- assert_equal 'looks good', body
50
- end
51
-
52
- it 'dumps errors to rack.errors when dump_errors is enabled' do
53
- mock_app {
54
- set :raise_errors, false
55
- set :dump_errors, true
56
- get('/') { raise FooError, 'BOOM!' }
57
- }
58
-
59
- get '/'
60
- assert_equal 500, status
61
- assert @response.errors =~ /FooError - BOOM!:/
62
- end
63
36
 
64
- it "raises without calling the handler when the raise_errors options is set" do
65
- mock_app {
66
- set :raise_errors, true
67
- error(FooError) { "she's not there." }
68
- get '/' do
69
- raise FooError
70
- end
71
- }
72
- assert_raise(FooError) { get '/' }
73
- end
37
+ get '/'
38
+ assert_equal 500, status
39
+ assert_equal 'Exception!', body
40
+ end
41
+
42
+ it "sets env['sinatra.error'] to the rescued exception" do
43
+ mock_app {
44
+ set :raise_errors, false
45
+ error(FooError) {
46
+ assert env.include?('sinatra.error')
47
+ assert env['sinatra.error'].kind_of?(FooError)
48
+ 'looks good'
49
+ }
50
+ get '/' do
51
+ raise FooError
52
+ end
53
+ }
54
+ get '/'
55
+ assert_equal 'looks good', body
56
+ end
57
+
58
+ it "raises without calling the handler when the raise_errors options is set" do
59
+ mock_app {
60
+ set :raise_errors, true
61
+ error(FooError) { "she's not there." }
62
+ get '/' do
63
+ raise FooError
64
+ end
65
+ }
66
+ assert_raise(FooError) { get '/' }
67
+ end
68
+
69
+ it "never raises Sinatra::NotFound beyond the application" do
70
+ mock_app {
71
+ set :raise_errors, true
72
+ get '/' do
73
+ raise Sinatra::NotFound
74
+ end
75
+ }
76
+ assert_nothing_raised { get '/' }
77
+ assert_equal 404, status
78
+ end
79
+
80
+ it "cascades for subclasses of Sinatra::NotFound" do
81
+ mock_app {
82
+ set :raise_errors, true
83
+ error(FooNotFound) { "foo! not found." }
84
+ get '/' do
85
+ raise FooNotFound
86
+ end
87
+ }
88
+ assert_nothing_raised { get '/' }
89
+ assert_equal 404, status
90
+ assert_equal 'foo! not found.', body
91
+ end
92
+
93
+ it 'has a not_found method for backwards compatibility' do
94
+ mock_app {
95
+ not_found do
96
+ "Lost, are we?"
97
+ end
98
+ }
74
99
 
75
- it "never raises Sinatra::NotFound beyond the application" do
76
- mock_app {
77
- set :raise_errors, true
78
- get '/' do
79
- raise Sinatra::NotFound
80
- end
81
- }
82
- assert_nothing_raised { get '/' }
83
- assert_equal 404, status
84
- end
100
+ get '/test'
101
+ assert_equal 404, status
102
+ assert_equal "Lost, are we?", body
103
+ end
85
104
 
86
- it "cascades for subclasses of Sinatra::NotFound" do
87
- mock_app {
88
- set :raise_errors, true
89
- error(FooNotFound) { "foo! not found." }
90
- get '/' do
91
- raise FooNotFound
92
- end
93
- }
94
- assert_nothing_raised { get '/' }
95
- assert_equal 404, status
96
- assert_equal 'foo! not found.', body
97
- end
105
+ it 'inherits error mappings from base class' do
106
+ base = Class.new(Sinatra::Base)
107
+ base.error(FooError) { 'base class' }
98
108
 
99
- it 'has a not_found method for backwards compatibility' do
100
- mock_app {
101
- not_found do
102
- "Lost, are we?"
103
- end
104
- }
109
+ mock_app(base) {
110
+ set :raise_errors, false
111
+ get '/' do
112
+ raise FooError
113
+ end
114
+ }
105
115
 
106
- get '/test'
107
- assert_equal 404, status
108
- assert_equal "Lost, are we?", body
109
- end
110
- end
116
+ get '/'
117
+ assert_equal 'base class', body
118
+ end
111
119
 
112
- describe 'Custom Error Pages' do
113
- it 'allows numeric status code mappings to be registered with ::error' do
114
- mock_app {
115
- set :raise_errors, false
116
- error(500) { 'Foo!' }
117
- get '/' do
118
- [500, {}, 'Internal Foo Error']
119
- end
120
- }
121
- get '/'
122
- assert_equal 500, status
123
- assert_equal 'Foo!', body
124
- end
120
+ it 'overrides error mappings in base class' do
121
+ base = Class.new(Sinatra::Base)
122
+ base.error(FooError) { 'base class' }
125
123
 
126
- it 'allows ranges of status code mappings to be registered with :error' do
127
- mock_app {
128
- set :raise_errors, false
129
- error(500..550) { "Error: #{response.status}" }
130
- get '/' do
131
- [507, {}, 'A very special error']
132
- end
133
- }
134
- get '/'
135
- assert_equal 507, status
136
- assert_equal 'Error: 507', body
137
- end
124
+ mock_app(base) {
125
+ set :raise_errors, false
126
+ error(FooError) { 'subclass' }
127
+ get '/' do
128
+ raise FooError
129
+ end
130
+ }
138
131
 
139
- class FooError < RuntimeError
132
+ get '/'
133
+ assert_equal 'subclass', body
134
+ end
140
135
  end
141
136
 
142
- it 'runs after exception mappings and overwrites body' do
143
- mock_app {
144
- set :raise_errors, false
145
- error FooError do
146
- response.status = 502
147
- 'from exception mapping'
148
- end
149
- error(500) { 'from 500 handler' }
150
- error(502) { 'from custom error page' }
151
-
152
- get '/' do
153
- raise FooError
154
- end
155
- }
156
- get '/'
157
- assert_equal 502, status
158
- assert_equal 'from custom error page', body
137
+ describe 'Custom Error Pages' do
138
+ it 'allows numeric status code mappings to be registered with ::error' do
139
+ mock_app {
140
+ set :raise_errors, false
141
+ error(500) { 'Foo!' }
142
+ get '/' do
143
+ [500, {}, 'Internal Foo Error']
144
+ end
145
+ }
146
+ get '/'
147
+ assert_equal 500, status
148
+ assert_equal 'Foo!', body
149
+ end
150
+
151
+ it 'allows ranges of status code mappings to be registered with :error' do
152
+ mock_app {
153
+ set :raise_errors, false
154
+ error(500..550) { "Error: #{response.status}" }
155
+ get '/' do
156
+ [507, {}, 'A very special error']
157
+ end
158
+ }
159
+ get '/'
160
+ assert_equal 507, status
161
+ assert_equal 'Error: 507', body
162
+ end
163
+
164
+ class FooError < RuntimeError
165
+ end
166
+
167
+ it 'runs after exception mappings and overwrites body' do
168
+ mock_app {
169
+ set :raise_errors, false
170
+ error FooError do
171
+ response.status = 502
172
+ 'from exception mapping'
173
+ end
174
+ error(500) { 'from 500 handler' }
175
+ error(502) { 'from custom error page' }
176
+
177
+ get '/' do
178
+ raise FooError
179
+ end
180
+ }
181
+ get '/'
182
+ assert_equal 502, status
183
+ assert_equal 'from custom error page', body
184
+ end
159
185
  end
160
186
  end
@@ -1,8 +1,8 @@
1
1
  require File.dirname(__FILE__) + '/helper'
2
2
 
3
- describe "Middleware" do
4
- before do
5
- @app = mock_app(Sinatra::Default) {
3
+ class MiddlewareTest < Test::Unit::TestCase
4
+ setup do
5
+ @app = mock_app(Sinatra::Application) {
6
6
  get '/*' do
7
7
  response.headers['X-Tests'] = env['test.ran'].
8
8
  map { |n| n.split('::').last }.
@@ -1,6 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/helper'
2
2
 
3
- describe 'Sinatra::Request' do
3
+ class RequestTest < Test::Unit::TestCase
4
4
  it 'responds to #user_agent' do
5
5
  request = Sinatra::Request.new({'HTTP_USER_AGENT' => 'Test'})
6
6
  assert request.respond_to?(:user_agent)
@@ -15,4 +15,19 @@ describe 'Sinatra::Request' do
15
15
  )
16
16
  assert_equal 'bar', request.params['foo']
17
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?
32
+ end
18
33
  end
@@ -2,8 +2,8 @@
2
2
 
3
3
  require File.dirname(__FILE__) + '/helper'
4
4
 
5
- describe 'Sinatra::Response' do
6
- before do
5
+ class ResponseTest < Test::Unit::TestCase
6
+ setup do
7
7
  @response = Sinatra::Response.new
8
8
  end
9
9
 
@@ -1,6 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/helper'
2
2
 
3
- describe 'Result Handling' do
3
+ class ResultTest < Test::Unit::TestCase
4
4
  it "sets response.body when result is a String" do
5
5
  mock_app {
6
6
  get '/' do
@@ -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
@@ -5,7 +5,23 @@ def route_def(pattern)
5
5
  mock_app { get(pattern) { } }
6
6
  end
7
7
 
8
- describe "Routing" do
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
23
+
24
+ class RoutingTest < Test::Unit::TestCase
9
25
  %w[get put post delete].each do |verb|
10
26
  it "defines #{verb.upcase} request handlers with #{verb}" do
11
27
  mock_app {
@@ -44,6 +60,30 @@ describe "Routing" do
44
60
  assert_equal 404, status
45
61
  end
46
62
 
63
+ it "404s and sets X-Cascade header when no route satisfies the request" do
64
+ mock_app {
65
+ get('/foo') { }
66
+ }
67
+ get '/bar'
68
+ assert_equal 404, status
69
+ assert_equal 'pass', response.headers['X-Cascade']
70
+ end
71
+
72
+ it "overrides the content-type in error handlers" do
73
+ mock_app {
74
+ before { content_type 'text/plain' }
75
+ error Sinatra::NotFound do
76
+ content_type "text/html"
77
+ "<h1>Not Found</h1>"
78
+ end
79
+ }
80
+
81
+ get '/foo'
82
+ assert_equal 404, status
83
+ assert_equal 'text/html', response["Content-Type"]
84
+ assert_equal "<h1>Not Found</h1>", response.body
85
+ end
86
+
47
87
  it 'takes multiple definitions of a route' do
48
88
  mock_app {
49
89
  user_agent(/Foo/)
@@ -243,17 +283,7 @@ describe "Routing" do
243
283
  end
244
284
 
245
285
  it "supports deeply nested params" do
246
- input = {
247
- 'browser[chrome][engine][name]' => 'V8',
248
- 'browser[chrome][engine][version]' => '1.0',
249
- 'browser[firefox][engine][name]' => 'spidermonkey',
250
- 'browser[firefox][engine][version]' => '1.7.0',
251
- 'emacs[map][goto-line]' => 'M-g g',
252
- 'emacs[version]' => '22.3.1',
253
- 'paste[name]' => 'hello world',
254
- 'paste[syntax]' => 'ruby'
255
- }
256
- expected = {
286
+ expected_params = {
257
287
  "emacs" => {
258
288
  "map" => { "goto-line" => "M-g g" },
259
289
  "version" => "22.3.1"
@@ -266,11 +296,11 @@ describe "Routing" do
266
296
  }
267
297
  mock_app {
268
298
  get '/foo' do
269
- assert_equal expected, params
299
+ assert_equal expected_params, params
270
300
  'looks good'
271
301
  end
272
302
  }
273
- get "/foo?#{build_query(input)}"
303
+ get '/foo', expected_params
274
304
  assert ok?
275
305
  assert_equal 'looks good', body
276
306
  end
@@ -352,9 +382,26 @@ describe "Routing" do
352
382
  assert_equal 'right on', body
353
383
  end
354
384
 
385
+ it 'supports regular expression look-alike routes' do
386
+ mock_app {
387
+ get(RegexpLookAlike.new) do
388
+ assert_equal 'this', params[:one]
389
+ assert_equal 'is', params[:two]
390
+ assert_equal 'a', params[:three]
391
+ assert_equal 'test', params[:four]
392
+ 'right on'
393
+ end
394
+ }
395
+
396
+ get '/this/is/a/test/'
397
+ assert ok?
398
+ assert_equal 'right on', body
399
+ end
400
+
355
401
  it 'raises a TypeError when pattern is not a String or Regexp' do
356
- @app = mock_app
357
- assert_raise(TypeError) { @app.get(42){} }
402
+ assert_raise(TypeError) {
403
+ mock_app { get(42){} }
404
+ }
358
405
  end
359
406
 
360
407
  it "returns response immediately on halt" do
@@ -424,6 +471,38 @@ describe "Routing" do
424
471
  assert not_found?
425
472
  end
426
473
 
474
+ it "transitions to 404 and sets X-Cascade header when passed and no subsequent route matches" do
475
+ mock_app {
476
+ get '/:foo' do
477
+ pass
478
+ 'Hello Foo'
479
+ end
480
+
481
+ get '/bar' do
482
+ 'Hello Bar'
483
+ end
484
+ }
485
+
486
+ get '/foo'
487
+ assert not_found?
488
+ assert_equal 'pass', response.headers['X-Cascade']
489
+ end
490
+
491
+ it "uses optional block passed to pass as route block if no other route is found" do
492
+ mock_app {
493
+ get "/" do
494
+ pass do
495
+ "this"
496
+ end
497
+ "not this"
498
+ end
499
+ }
500
+
501
+ get "/"
502
+ assert ok?
503
+ assert "this", body
504
+ end
505
+
427
506
  it "passes when matching condition returns false" do
428
507
  mock_app {
429
508
  condition { params[:foo] == 'bar' }
@@ -479,7 +558,7 @@ describe "Routing" do
479
558
  get '/foo'
480
559
  assert not_found?
481
560
 
482
- get '/foo', :env => { 'HTTP_HOST' => 'example.com' }
561
+ get '/foo', {}, { 'HTTP_HOST' => 'example.com' }
483
562
  assert_equal 200, status
484
563
  assert_equal 'Hello World', body
485
564
  end
@@ -494,7 +573,7 @@ describe "Routing" do
494
573
  get '/foo'
495
574
  assert not_found?
496
575
 
497
- get '/foo', :env => { 'HTTP_USER_AGENT' => 'Foo Bar' }
576
+ get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' }
498
577
  assert_equal 200, status
499
578
  assert_equal 'Hello World', body
500
579
  end
@@ -506,7 +585,7 @@ describe "Routing" do
506
585
  'Hello ' + params[:agent].first
507
586
  end
508
587
  }
509
- get '/foo', :env => { 'HTTP_USER_AGENT' => 'Foo Bar' }
588
+ get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' }
510
589
  assert_equal 200, status
511
590
  assert_equal 'Hello Bar', body
512
591
  end
@@ -518,12 +597,12 @@ describe "Routing" do
518
597
  end
519
598
  }
520
599
 
521
- get '/', :env => { :accept => 'application/xml' }
600
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
522
601
  assert ok?
523
602
  assert_equal 'application/xml', body
524
603
  assert_equal 'application/xml', response.headers['Content-Type']
525
604
 
526
- get '/', :env => { :accept => 'text/html' }
605
+ get '/', {}, { :accept => 'text/html' }
527
606
  assert !ok?
528
607
  end
529
608
 
@@ -537,7 +616,7 @@ describe "Routing" do
537
616
  }
538
617
 
539
618
  types.each do |type|
540
- get '/', :env => { :accept => type }
619
+ get '/', {}, { 'HTTP_ACCEPT' => type }
541
620
  assert ok?
542
621
  assert_equal type, body
543
622
  assert_equal type, response.headers['Content-Type']
@@ -655,6 +734,40 @@ describe "Routing" do
655
734
  assert_equal 'ab', body
656
735
  end
657
736
 
737
+ it 'allows custom route-conditions to be set via route options' do
738
+ protector = Module.new {
739
+ def protect(*args)
740
+ condition {
741
+ unless authorize(params["user"], params["password"])
742
+ halt 403, "go away"
743
+ end
744
+ }
745
+ end
746
+ }
747
+
748
+ mock_app {
749
+ register protector
750
+
751
+ helpers do
752
+ def authorize(username, password)
753
+ username == "foo" && password == "bar"
754
+ end
755
+ end
756
+
757
+ get "/", :protect => true do
758
+ "hey"
759
+ end
760
+ }
761
+
762
+ get "/"
763
+ assert forbidden?
764
+ assert_equal "go away", body
765
+
766
+ get "/", :user => "foo", :password => "bar"
767
+ assert ok?
768
+ assert_equal "hey", body
769
+ end
770
+
658
771
  # NOTE Block params behaves differently under 1.8 and 1.9. Under 1.8, block
659
772
  # param arity is lax: declaring a mismatched number of block params results
660
773
  # in a warning. Under 1.9, block param arity is strict: mismatched block
@@ -709,4 +822,39 @@ describe "Routing" do
709
822
  end
710
823
 
711
824
  end
825
+
826
+ it "matches routes defined in superclasses" do
827
+ base = Class.new(Sinatra::Base)
828
+ base.get('/foo') { 'foo in baseclass' }
829
+
830
+ mock_app(base) {
831
+ get('/bar') { 'bar in subclass' }
832
+ }
833
+
834
+ get '/foo'
835
+ assert ok?
836
+ assert_equal 'foo in baseclass', body
837
+
838
+ get '/bar'
839
+ assert ok?
840
+ assert_equal 'bar in subclass', body
841
+ end
842
+
843
+ it "matches routes in subclasses before superclasses" do
844
+ base = Class.new(Sinatra::Base)
845
+ base.get('/foo') { 'foo in baseclass' }
846
+ base.get('/bar') { 'bar in baseclass' }
847
+
848
+ mock_app(base) {
849
+ get('/foo') { 'foo in subclass' }
850
+ }
851
+
852
+ get '/foo'
853
+ assert ok?
854
+ assert_equal 'foo in subclass', body
855
+
856
+ get '/bar'
857
+ assert ok?
858
+ assert_equal 'bar in baseclass', body
859
+ end
712
860
  end