darkhelmet-sinatra 0.9.1.1 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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