sinatra-base 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/AUTHORS +43 -0
  2. data/CHANGES +511 -0
  3. data/LICENSE +22 -0
  4. data/README.jp.rdoc +552 -0
  5. data/README.rdoc +636 -0
  6. data/Rakefile +116 -0
  7. data/lib/sinatra.rb +7 -0
  8. data/lib/sinatra/base.rb +1167 -0
  9. data/lib/sinatra/images/404.png +0 -0
  10. data/lib/sinatra/images/500.png +0 -0
  11. data/lib/sinatra/main.rb +28 -0
  12. data/lib/sinatra/showexceptions.rb +307 -0
  13. data/lib/sinatra/tilt.rb +746 -0
  14. data/sinatra-base.gemspec +94 -0
  15. data/test/base_test.rb +160 -0
  16. data/test/builder_test.rb +65 -0
  17. data/test/contest.rb +64 -0
  18. data/test/erb_test.rb +81 -0
  19. data/test/erubis_test.rb +82 -0
  20. data/test/extensions_test.rb +100 -0
  21. data/test/filter_test.rb +221 -0
  22. data/test/haml_test.rb +95 -0
  23. data/test/helper.rb +76 -0
  24. data/test/helpers_test.rb +582 -0
  25. data/test/less_test.rb +37 -0
  26. data/test/mapped_error_test.rb +197 -0
  27. data/test/middleware_test.rb +68 -0
  28. data/test/public/favicon.ico +0 -0
  29. data/test/request_test.rb +33 -0
  30. data/test/response_test.rb +42 -0
  31. data/test/result_test.rb +98 -0
  32. data/test/route_added_hook_test.rb +59 -0
  33. data/test/routing_test.rb +860 -0
  34. data/test/sass_test.rb +85 -0
  35. data/test/server_test.rb +47 -0
  36. data/test/settings_test.rb +368 -0
  37. data/test/sinatra_test.rb +13 -0
  38. data/test/static_test.rb +93 -0
  39. data/test/templates_test.rb +159 -0
  40. data/test/views/error.builder +3 -0
  41. data/test/views/error.erb +3 -0
  42. data/test/views/error.erubis +3 -0
  43. data/test/views/error.haml +3 -0
  44. data/test/views/error.sass +2 -0
  45. data/test/views/foo/hello.test +1 -0
  46. data/test/views/hello.builder +1 -0
  47. data/test/views/hello.erb +1 -0
  48. data/test/views/hello.erubis +1 -0
  49. data/test/views/hello.haml +1 -0
  50. data/test/views/hello.less +5 -0
  51. data/test/views/hello.sass +2 -0
  52. data/test/views/hello.test +1 -0
  53. data/test/views/layout2.builder +3 -0
  54. data/test/views/layout2.erb +2 -0
  55. data/test/views/layout2.erubis +2 -0
  56. data/test/views/layout2.haml +2 -0
  57. data/test/views/layout2.test +1 -0
  58. metadata +257 -0
@@ -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
@@ -0,0 +1,860 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ # Helper method for easy route pattern matching testing
4
+ def route_def(pattern)
5
+ mock_app { get(pattern) { } }
6
+ end
7
+
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
25
+ %w[get put post delete].each do |verb|
26
+ it "defines #{verb.upcase} request handlers with #{verb}" do
27
+ mock_app {
28
+ send verb, '/hello' do
29
+ 'Hello World'
30
+ end
31
+ }
32
+
33
+ request = Rack::MockRequest.new(@app)
34
+ response = request.request(verb.upcase, '/hello', {})
35
+ assert response.ok?
36
+ assert_equal 'Hello World', response.body
37
+ end
38
+ end
39
+
40
+ it "defines HEAD request handlers with HEAD" do
41
+ mock_app {
42
+ head '/hello' do
43
+ response['X-Hello'] = 'World!'
44
+ 'remove me'
45
+ end
46
+ }
47
+
48
+ request = Rack::MockRequest.new(@app)
49
+ response = request.request('HEAD', '/hello', {})
50
+ assert response.ok?
51
+ assert_equal 'World!', response['X-Hello']
52
+ assert_equal '', response.body
53
+ end
54
+
55
+ it "404s when no route satisfies the request" do
56
+ mock_app {
57
+ get('/foo') { }
58
+ }
59
+ get '/bar'
60
+ assert_equal 404, status
61
+ end
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
+
87
+ it 'takes multiple definitions of a route' do
88
+ mock_app {
89
+ user_agent(/Foo/)
90
+ get '/foo' do
91
+ 'foo'
92
+ end
93
+
94
+ get '/foo' do
95
+ 'not foo'
96
+ end
97
+ }
98
+
99
+ get '/foo', {}, 'HTTP_USER_AGENT' => 'Foo'
100
+ assert ok?
101
+ assert_equal 'foo', body
102
+
103
+ get '/foo'
104
+ assert ok?
105
+ assert_equal 'not foo', body
106
+ end
107
+
108
+ it "exposes params with indifferent hash" do
109
+ mock_app {
110
+ get '/:foo' do
111
+ assert_equal 'bar', params['foo']
112
+ assert_equal 'bar', params[:foo]
113
+ 'well, alright'
114
+ end
115
+ }
116
+ get '/bar'
117
+ assert_equal 'well, alright', body
118
+ end
119
+
120
+ it "merges named params and query string params in params" do
121
+ mock_app {
122
+ get '/:foo' do
123
+ assert_equal 'bar', params['foo']
124
+ assert_equal 'biz', params['baz']
125
+ end
126
+ }
127
+ get '/bar?baz=biz'
128
+ assert ok?
129
+ end
130
+
131
+ it "supports named params like /hello/:person" do
132
+ mock_app {
133
+ get '/hello/:person' do
134
+ "Hello #{params['person']}"
135
+ end
136
+ }
137
+ get '/hello/Frank'
138
+ assert_equal 'Hello Frank', body
139
+ end
140
+
141
+ it "supports optional named params like /?:foo?/?:bar?" do
142
+ mock_app {
143
+ get '/?:foo?/?:bar?' do
144
+ "foo=#{params[:foo]};bar=#{params[:bar]}"
145
+ end
146
+ }
147
+
148
+ get '/hello/world'
149
+ assert ok?
150
+ assert_equal "foo=hello;bar=world", body
151
+
152
+ get '/hello'
153
+ assert ok?
154
+ assert_equal "foo=hello;bar=", body
155
+
156
+ get '/'
157
+ assert ok?
158
+ assert_equal "foo=;bar=", body
159
+ end
160
+
161
+ it "supports single splat params like /*" do
162
+ mock_app {
163
+ get '/*' do
164
+ assert params['splat'].kind_of?(Array)
165
+ params['splat'].join "\n"
166
+ end
167
+ }
168
+
169
+ get '/foo'
170
+ assert_equal "foo", body
171
+
172
+ get '/foo/bar/baz'
173
+ assert_equal "foo/bar/baz", body
174
+ end
175
+
176
+ it "supports mixing multiple splat params like /*/foo/*/*" do
177
+ mock_app {
178
+ get '/*/foo/*/*' do
179
+ assert params['splat'].kind_of?(Array)
180
+ params['splat'].join "\n"
181
+ end
182
+ }
183
+
184
+ get '/bar/foo/bling/baz/boom'
185
+ assert_equal "bar\nbling\nbaz/boom", body
186
+
187
+ get '/bar/foo/baz'
188
+ assert not_found?
189
+ end
190
+
191
+ it "supports mixing named and splat params like /:foo/*" do
192
+ mock_app {
193
+ get '/:foo/*' do
194
+ assert_equal 'foo', params['foo']
195
+ assert_equal ['bar/baz'], params['splat']
196
+ end
197
+ }
198
+
199
+ get '/foo/bar/baz'
200
+ assert ok?
201
+ end
202
+
203
+ it "matches a dot ('.') as part of a named param" do
204
+ mock_app {
205
+ get '/:foo/:bar' do
206
+ params[:foo]
207
+ end
208
+ }
209
+
210
+ get '/user@example.com/name'
211
+ assert_equal 200, response.status
212
+ assert_equal 'user@example.com', body
213
+ end
214
+
215
+ it "matches a literal dot ('.') outside of named params" do
216
+ mock_app {
217
+ get '/:file.:ext' do
218
+ assert_equal 'pony', params[:file]
219
+ assert_equal 'jpg', params[:ext]
220
+ 'right on'
221
+ end
222
+ }
223
+
224
+ get '/pony.jpg'
225
+ assert_equal 200, response.status
226
+ assert_equal 'right on', body
227
+ end
228
+
229
+ it "literally matches . in paths" do
230
+ route_def '/test.bar'
231
+
232
+ get '/test.bar'
233
+ assert ok?
234
+ get 'test0bar'
235
+ assert not_found?
236
+ end
237
+
238
+ it "literally matches $ in paths" do
239
+ route_def '/test$/'
240
+
241
+ get '/test$/'
242
+ assert ok?
243
+ end
244
+
245
+ it "literally matches + in paths" do
246
+ route_def '/te+st/'
247
+
248
+ get '/te%2Bst/'
249
+ assert ok?
250
+ get '/teeeeeeest/'
251
+ assert not_found?
252
+ end
253
+
254
+ it "literally matches () in paths" do
255
+ route_def '/test(bar)/'
256
+
257
+ get '/test(bar)/'
258
+ assert ok?
259
+ end
260
+
261
+ it "supports basic nested params" do
262
+ mock_app {
263
+ get '/hi' do
264
+ params["person"]["name"]
265
+ end
266
+ }
267
+
268
+ get "/hi?person[name]=John+Doe"
269
+ assert ok?
270
+ assert_equal "John Doe", body
271
+ end
272
+
273
+ it "exposes nested params with indifferent hash" do
274
+ mock_app {
275
+ get '/testme' do
276
+ assert_equal 'baz', params['bar']['foo']
277
+ assert_equal 'baz', params['bar'][:foo]
278
+ 'well, alright'
279
+ end
280
+ }
281
+ get '/testme?bar[foo]=baz'
282
+ assert_equal 'well, alright', body
283
+ end
284
+
285
+ it "supports deeply nested params" do
286
+ expected_params = {
287
+ "emacs" => {
288
+ "map" => { "goto-line" => "M-g g" },
289
+ "version" => "22.3.1"
290
+ },
291
+ "browser" => {
292
+ "firefox" => {"engine" => {"name"=>"spidermonkey", "version"=>"1.7.0"}},
293
+ "chrome" => {"engine" => {"name"=>"V8", "version"=>"1.0"}}
294
+ },
295
+ "paste" => {"name"=>"hello world", "syntax"=>"ruby"}
296
+ }
297
+ mock_app {
298
+ get '/foo' do
299
+ assert_equal expected_params, params
300
+ 'looks good'
301
+ end
302
+ }
303
+ get '/foo', expected_params
304
+ assert ok?
305
+ assert_equal 'looks good', body
306
+ end
307
+
308
+ it "preserves non-nested params" do
309
+ mock_app {
310
+ get '/foo' do
311
+ assert_equal "2", params["article_id"]
312
+ assert_equal "awesome", params['comment']['body']
313
+ assert_nil params['comment[body]']
314
+ 'looks good'
315
+ end
316
+ }
317
+
318
+ get '/foo?article_id=2&comment[body]=awesome'
319
+ assert ok?
320
+ assert_equal 'looks good', body
321
+ end
322
+
323
+ it "matches paths that include spaces encoded with %20" do
324
+ mock_app {
325
+ get '/path with spaces' do
326
+ 'looks good'
327
+ end
328
+ }
329
+
330
+ get '/path%20with%20spaces'
331
+ assert ok?
332
+ assert_equal 'looks good', body
333
+ end
334
+
335
+ it "matches paths that include spaces encoded with +" do
336
+ mock_app {
337
+ get '/path with spaces' do
338
+ 'looks good'
339
+ end
340
+ }
341
+
342
+ get '/path+with+spaces'
343
+ assert ok?
344
+ assert_equal 'looks good', body
345
+ end
346
+
347
+ it "URL decodes named parameters and splats" do
348
+ mock_app {
349
+ get '/:foo/*' do
350
+ assert_equal 'hello world', params['foo']
351
+ assert_equal ['how are you'], params['splat']
352
+ nil
353
+ end
354
+ }
355
+
356
+ get '/hello%20world/how%20are%20you'
357
+ assert ok?
358
+ end
359
+
360
+ it 'supports regular expressions' do
361
+ mock_app {
362
+ get(/^\/foo...\/bar$/) do
363
+ 'Hello World'
364
+ end
365
+ }
366
+
367
+ get '/foooom/bar'
368
+ assert ok?
369
+ assert_equal 'Hello World', body
370
+ end
371
+
372
+ it 'makes regular expression captures available in params[:captures]' do
373
+ mock_app {
374
+ get(/^\/fo(.*)\/ba(.*)/) do
375
+ assert_equal ['orooomma', 'f'], params[:captures]
376
+ 'right on'
377
+ end
378
+ }
379
+
380
+ get '/foorooomma/baf'
381
+ assert ok?
382
+ assert_equal 'right on', body
383
+ end
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
+
401
+ it 'raises a TypeError when pattern is not a String or Regexp' do
402
+ assert_raise(TypeError) {
403
+ mock_app { get(42){} }
404
+ }
405
+ end
406
+
407
+ it "returns response immediately on halt" do
408
+ mock_app {
409
+ get '/' do
410
+ halt 'Hello World'
411
+ 'Boo-hoo World'
412
+ end
413
+ }
414
+
415
+ get '/'
416
+ assert ok?
417
+ assert_equal 'Hello World', body
418
+ end
419
+
420
+ it "halts with a response tuple" do
421
+ mock_app {
422
+ get '/' do
423
+ halt 295, {'Content-Type' => 'text/plain'}, 'Hello World'
424
+ end
425
+ }
426
+
427
+ get '/'
428
+ assert_equal 295, status
429
+ assert_equal 'text/plain', response['Content-Type']
430
+ assert_equal 'Hello World', body
431
+ end
432
+
433
+ it "halts with an array of strings" do
434
+ mock_app {
435
+ get '/' do
436
+ halt %w[Hello World How Are You]
437
+ end
438
+ }
439
+
440
+ get '/'
441
+ assert_equal 'HelloWorldHowAreYou', body
442
+ end
443
+
444
+ it "transitions to the next matching route on pass" do
445
+ mock_app {
446
+ get '/:foo' do
447
+ pass
448
+ 'Hello Foo'
449
+ end
450
+
451
+ get '/*' do
452
+ assert !params.include?('foo')
453
+ 'Hello World'
454
+ end
455
+ }
456
+
457
+ get '/bar'
458
+ assert ok?
459
+ assert_equal 'Hello World', body
460
+ end
461
+
462
+ it "transitions to 404 when passed and no subsequent route matches" do
463
+ mock_app {
464
+ get '/:foo' do
465
+ pass
466
+ 'Hello Foo'
467
+ end
468
+ }
469
+
470
+ get '/bar'
471
+ assert not_found?
472
+ end
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
+
506
+ it "passes when matching condition returns false" do
507
+ mock_app {
508
+ condition { params[:foo] == 'bar' }
509
+ get '/:foo' do
510
+ 'Hello World'
511
+ end
512
+ }
513
+
514
+ get '/bar'
515
+ assert ok?
516
+ assert_equal 'Hello World', body
517
+
518
+ get '/foo'
519
+ assert not_found?
520
+ end
521
+
522
+ it "does not pass when matching condition returns nil" do
523
+ mock_app {
524
+ condition { nil }
525
+ get '/:foo' do
526
+ 'Hello World'
527
+ end
528
+ }
529
+
530
+ get '/bar'
531
+ assert ok?
532
+ assert_equal 'Hello World', body
533
+ end
534
+
535
+ it "passes to next route when condition calls pass explicitly" do
536
+ mock_app {
537
+ condition { pass unless params[:foo] == 'bar' }
538
+ get '/:foo' do
539
+ 'Hello World'
540
+ end
541
+ }
542
+
543
+ get '/bar'
544
+ assert ok?
545
+ assert_equal 'Hello World', body
546
+
547
+ get '/foo'
548
+ assert not_found?
549
+ end
550
+
551
+ it "passes to the next route when host_name does not match" do
552
+ mock_app {
553
+ host_name 'example.com'
554
+ get '/foo' do
555
+ 'Hello World'
556
+ end
557
+ }
558
+ get '/foo'
559
+ assert not_found?
560
+
561
+ get '/foo', {}, { 'HTTP_HOST' => 'example.com' }
562
+ assert_equal 200, status
563
+ assert_equal 'Hello World', body
564
+ end
565
+
566
+ it "passes to the next route when user_agent does not match" do
567
+ mock_app {
568
+ user_agent(/Foo/)
569
+ get '/foo' do
570
+ 'Hello World'
571
+ end
572
+ }
573
+ get '/foo'
574
+ assert not_found?
575
+
576
+ get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' }
577
+ assert_equal 200, status
578
+ assert_equal 'Hello World', body
579
+ end
580
+
581
+ it "makes captures in user agent pattern available in params[:agent]" do
582
+ mock_app {
583
+ user_agent(/Foo (.*)/)
584
+ get '/foo' do
585
+ 'Hello ' + params[:agent].first
586
+ end
587
+ }
588
+ get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' }
589
+ assert_equal 200, status
590
+ assert_equal 'Hello Bar', body
591
+ end
592
+
593
+ it "filters by accept header" do
594
+ mock_app {
595
+ get '/', :provides => :xml do
596
+ request.env['HTTP_ACCEPT']
597
+ end
598
+ }
599
+
600
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
601
+ assert ok?
602
+ assert_equal 'application/xml', body
603
+ assert_equal 'application/xml', response.headers['Content-Type']
604
+
605
+ get '/', {}, { :accept => 'text/html' }
606
+ assert !ok?
607
+ end
608
+
609
+ it "allows multiple mime types for accept header" do
610
+ types = ['image/jpeg', 'image/pjpeg']
611
+
612
+ mock_app {
613
+ get '/', :provides => types do
614
+ request.env['HTTP_ACCEPT']
615
+ end
616
+ }
617
+
618
+ types.each do |type|
619
+ get '/', {}, { 'HTTP_ACCEPT' => type }
620
+ assert ok?
621
+ assert_equal type, body
622
+ assert_equal type, response.headers['Content-Type']
623
+ end
624
+ end
625
+
626
+ it 'degrades gracefully when optional accept header is not provided' do
627
+ mock_app {
628
+ get '/', :provides => :xml do
629
+ request.env['HTTP_ACCEPT']
630
+ end
631
+ get '/' do
632
+ 'default'
633
+ end
634
+ }
635
+ get '/'
636
+ assert ok?
637
+ assert_equal 'default', body
638
+ end
639
+
640
+ it 'passes a single url param as block parameters when one param is specified' do
641
+ mock_app {
642
+ get '/:foo' do |foo|
643
+ assert_equal 'bar', foo
644
+ end
645
+ }
646
+
647
+ get '/bar'
648
+ assert ok?
649
+ end
650
+
651
+ it 'passes multiple params as block parameters when many are specified' do
652
+ mock_app {
653
+ get '/:foo/:bar/:baz' do |foo, bar, baz|
654
+ assert_equal 'abc', foo
655
+ assert_equal 'def', bar
656
+ assert_equal 'ghi', baz
657
+ end
658
+ }
659
+
660
+ get '/abc/def/ghi'
661
+ assert ok?
662
+ end
663
+
664
+ it 'passes regular expression captures as block parameters' do
665
+ mock_app {
666
+ get(/^\/fo(.*)\/ba(.*)/) do |foo, bar|
667
+ assert_equal 'orooomma', foo
668
+ assert_equal 'f', bar
669
+ 'looks good'
670
+ end
671
+ }
672
+
673
+ get '/foorooomma/baf'
674
+ assert ok?
675
+ assert_equal 'looks good', body
676
+ end
677
+
678
+ it "supports mixing multiple splat params like /*/foo/*/* as block parameters" do
679
+ mock_app {
680
+ get '/*/foo/*/*' do |foo, bar, baz|
681
+ assert_equal 'bar', foo
682
+ assert_equal 'bling', bar
683
+ assert_equal 'baz/boom', baz
684
+ 'looks good'
685
+ end
686
+ }
687
+
688
+ get '/bar/foo/bling/baz/boom'
689
+ assert ok?
690
+ assert_equal 'looks good', body
691
+ end
692
+
693
+ it 'raises an ArgumentError with block arity > 1 and too many values' do
694
+ mock_app {
695
+ get '/:foo/:bar/:baz' do |foo, bar|
696
+ 'quux'
697
+ end
698
+ }
699
+
700
+ assert_raise(ArgumentError) { get '/a/b/c' }
701
+ end
702
+
703
+ it 'raises an ArgumentError with block param arity > 1 and too few values' do
704
+ mock_app {
705
+ get '/:foo/:bar' do |foo, bar, baz|
706
+ 'quux'
707
+ end
708
+ }
709
+
710
+ assert_raise(ArgumentError) { get '/a/b' }
711
+ end
712
+
713
+ it 'succeeds if no block parameters are specified' do
714
+ mock_app {
715
+ get '/:foo/:bar' do
716
+ 'quux'
717
+ end
718
+ }
719
+
720
+ get '/a/b'
721
+ assert ok?
722
+ assert_equal 'quux', body
723
+ end
724
+
725
+ it 'passes all params with block param arity -1 (splat args)' do
726
+ mock_app {
727
+ get '/:foo/:bar' do |*args|
728
+ args.join
729
+ end
730
+ }
731
+
732
+ get '/a/b'
733
+ assert ok?
734
+ assert_equal 'ab', body
735
+ end
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
+
771
+ # NOTE Block params behaves differently under 1.8 and 1.9. Under 1.8, block
772
+ # param arity is lax: declaring a mismatched number of block params results
773
+ # in a warning. Under 1.9, block param arity is strict: mismatched block
774
+ # arity raises an ArgumentError.
775
+
776
+ if RUBY_VERSION >= '1.9'
777
+
778
+ it 'raises an ArgumentError with block param arity 1 and no values' do
779
+ mock_app {
780
+ get '/foo' do |foo|
781
+ 'quux'
782
+ end
783
+ }
784
+
785
+ assert_raise(ArgumentError) { get '/foo' }
786
+ end
787
+
788
+ it 'raises an ArgumentError with block param arity 1 and too many values' do
789
+ mock_app {
790
+ get '/:foo/:bar/:baz' do |foo|
791
+ 'quux'
792
+ end
793
+ }
794
+
795
+ assert_raise(ArgumentError) { get '/a/b/c' }
796
+ end
797
+
798
+ else
799
+
800
+ it 'does not raise an ArgumentError with block param arity 1 and no values' do
801
+ mock_app {
802
+ get '/foo' do |foo|
803
+ 'quux'
804
+ end
805
+ }
806
+
807
+ silence_warnings { get '/foo' }
808
+ assert ok?
809
+ assert_equal 'quux', body
810
+ end
811
+
812
+ it 'does not raise an ArgumentError with block param arity 1 and too many values' do
813
+ mock_app {
814
+ get '/:foo/:bar/:baz' do |foo|
815
+ 'quux'
816
+ end
817
+ }
818
+
819
+ silence_warnings { get '/a/b/c' }
820
+ assert ok?
821
+ assert_equal 'quux', body
822
+ end
823
+
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
860
+ end