scorched 0.22 → 0.23

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.
File without changes
File without changes
@@ -1,3 +1,3 @@
1
1
  module Scorched
2
- VERSION = '0.22'
3
- end
2
+ VERSION = '0.23'
3
+ end
@@ -15,9 +15,10 @@ Gem::Specification.new 'scorched', Scorched::VERSION do |s|
15
15
  s.required_ruby_version = '>= 2.0.0'
16
16
 
17
17
  s.add_dependency 'rack', '~> 1.4'
18
- s.add_dependency 'rack-accept', '~> 0.4'
18
+ s.add_dependency 'rack-accept', '~> 0.4' # Used for Accept-Charset, Accept-Encoding and Accept-Language headers.
19
+ s.add_dependency 'scorched-accept', '~> 0.1' # Used for Accept header.
19
20
  s.add_dependency 'tilt', '~> 1.4'
20
21
  s.add_development_dependency 'rack-test', '~> 0.6'
21
22
  s.add_development_dependency 'rspec', '~> 2.9'
22
- s.add_development_dependency 'rake', '>= 10'
23
- end
23
+ s.add_development_dependency 'rake', '~> 10.4'
24
+ end
@@ -5,30 +5,30 @@ module Scorched
5
5
  let(:generic_handler) do
6
6
  proc { |env| [200, {}, ['ok']] }
7
7
  end
8
-
8
+
9
9
  it "contains a default set of configuration options" do
10
10
  app.config.should be_a(Options)
11
11
  app.config.length.should > 0
12
12
  end
13
-
13
+
14
14
  it "contains a set of default conditions" do
15
15
  app.conditions.should be_a(Options)
16
16
  app.conditions.length.should > 0
17
17
  app.conditions[:method].should be_a(Proc)
18
18
  end
19
-
19
+
20
20
  describe "basic route handling" do
21
21
  it "gracefully handles 404 errors" do
22
22
  response = rt.get '/'
23
23
  response.status.should == 404
24
24
  end
25
-
25
+
26
26
  it "handles a root rack call correctly" do
27
27
  app << {pattern: '/$', target: generic_handler}
28
28
  response = rt.get '/'
29
29
  response.status.should == 200
30
30
  end
31
-
31
+
32
32
  it "does not maintain state between requests" do
33
33
  app << {pattern: '/state', target: proc { |env| [200, {}, [@state = 1 + @state.to_i]] }}
34
34
  response = rt.get '/state'
@@ -36,7 +36,7 @@ module Scorched
36
36
  response = rt.get '/state'
37
37
  response.body.should == '1'
38
38
  end
39
-
39
+
40
40
  it "raises exception when invalid mapping hash given" do
41
41
  expect {
42
42
  app << {pattern: '/'}
@@ -46,14 +46,14 @@ module Scorched
46
46
  }.to raise_error(ArgumentError)
47
47
  end
48
48
  end
49
-
49
+
50
50
  describe "URL matching" do
51
51
  it 'always matches from the beginning of the URL' do
52
52
  app << {pattern: 'about', target: generic_handler}
53
53
  response = rt.get '/about'
54
54
  response.status.should == 404
55
55
  end
56
-
56
+
57
57
  it "matches eagerly by default" do
58
58
  req = nil
59
59
  app << {pattern: '/*', target: proc do |env|
@@ -62,7 +62,7 @@ module Scorched
62
62
  response = rt.get '/about'
63
63
  req.captures.should == ['about']
64
64
  end
65
-
65
+
66
66
  it "can be forced to match end of URL" do
67
67
  app << {pattern: '/about$', target: generic_handler}
68
68
  response = rt.get '/about/us'
@@ -71,7 +71,7 @@ module Scorched
71
71
  response = rt.get '/about/us'
72
72
  response.status.should == 200
73
73
  end
74
-
74
+
75
75
  it "unescapes all characters except for the forward-slash and percent sign" do
76
76
  app << {pattern: '/a (quite) big fish', target: generic_handler}
77
77
  rt.get('/a%20%28quite%29%20big%20fish').status.should == 200
@@ -81,7 +81,7 @@ module Scorched
81
81
  rt.get('/page%2Fabout').status.should == 200
82
82
  rt.get('/page/about').status.should == 404
83
83
  end
84
-
84
+
85
85
  it "unmatched path doesn't always begin with a forward slash" do
86
86
  gh = generic_handler
87
87
  app << {pattern: '/ab', target: Class.new(Scorched::Controller) do
@@ -91,7 +91,7 @@ module Scorched
91
91
  resp.status.should == 200
92
92
  resp.body.should == "ok"
93
93
  end
94
-
94
+
95
95
  it "unmatched path begins with forward slash if last match was up to or included a forward slash" do
96
96
  gh = generic_handler
97
97
  app << {pattern: '/about/', target: Class.new(Scorched::Controller) do
@@ -103,7 +103,7 @@ module Scorched
103
103
  rt.get('/about/us').body.should == "ok"
104
104
  rt.get('/contact/us').body.should == "ok"
105
105
  end
106
-
106
+
107
107
  it "can match anonymous wildcards" do
108
108
  req = nil
109
109
  app << {pattern: '/anon/*/**', target: proc do |env|
@@ -112,7 +112,7 @@ module Scorched
112
112
  response = rt.get '/anon/jeff/has/crabs'
113
113
  req.captures.should == ['jeff', 'has/crabs']
114
114
  end
115
-
115
+
116
116
  it "can match named wildcards (ignoring anonymous captures)" do
117
117
  req = nil
118
118
  app << {pattern: '/anon/:name/*/::infliction', target: proc do |env|
@@ -121,7 +121,7 @@ module Scorched
121
121
  response = rt.get '/anon/jeff/smith/has/crabs'
122
122
  req.captures.should == {name: 'jeff', infliction: 'has/crabs'}
123
123
  end
124
-
124
+
125
125
  example "wildcards match one or more characters" do
126
126
  app << {pattern: '/*', target: proc { |env| [200, {}, ['ok']] }}
127
127
  rt.get('/').status.should == 404
@@ -139,7 +139,7 @@ module Scorched
139
139
  rt.get('/').status.should == 404
140
140
  rt.get('/dog/cat').status.should == 200
141
141
  end
142
-
142
+
143
143
  example "wildcards can optionally match zero or more characters" do
144
144
  app << {pattern: '/*?', target: proc { |env| [200, {}, ['ok']] }}
145
145
  rt.get('/').status.should == 200
@@ -157,7 +157,7 @@ module Scorched
157
157
  rt.get('/').status.should == 200
158
158
  rt.get('/dog/cat').status.should == 200
159
159
  end
160
-
160
+
161
161
  it "can match regex and preserve anonymous captures" do
162
162
  req = nil
163
163
  app << {pattern: %r{/anon/([^/]+)/(.+)}, target: proc do |env|
@@ -166,7 +166,7 @@ module Scorched
166
166
  response = rt.get '/anon/jeff/has/crabs'
167
167
  req.captures.should == ['jeff', 'has/crabs']
168
168
  end
169
-
169
+
170
170
  it "can match regex and preserve named captures (ignoring anonymous captures)" do
171
171
  req = nil
172
172
  app << {pattern: %r{/anon/(?<name>[^/]+)/([^/]+)/(?<infliction>.+)}, target: proc do |env|
@@ -175,7 +175,7 @@ module Scorched
175
175
  response = rt.get '/anon/jeff/smith/has/crabs'
176
176
  req.captures.should == {name: 'jeff', infliction: 'has/crabs'}
177
177
  end
178
-
178
+
179
179
  it "matches routes based on priority, otherwise giving precedence to those defined first" do
180
180
  order = []
181
181
  app << {pattern: '/', priority: -1, target: proc { |env| order << 'four'; [200, {}, ['ok']] }}
@@ -185,38 +185,35 @@ module Scorched
185
185
  rt.get('/').body.should == 'ok'
186
186
  order.should == %w{one two three four}
187
187
  end
188
-
189
- it "finds the best match for media type whilst respecting priority and definition order" do
190
- app << {pattern: '/', conditions: {media_type: 'text/html'}, target: proc { |env|
191
- [200, {}, ['text/html']]
192
- }}
193
- app << {pattern: '/', conditions: {media_type: 'application/json'}, target: proc { |env|
194
- [200, {}, ['application/json']]
195
- }}
196
- app << {pattern: '/', priority: 1, target: proc { |env|
197
- [200, {}, ['anything']]
198
- }}
199
- rt.get('/', media_type: 'application/json, */*;q=0.5').body.should == 'anything'
188
+
189
+ it "finds the best match for the media type whilst respecting priority and definition order" do
190
+ app << {pattern: '/', target: proc { |env| [200, {}, ['anything']] }}
191
+ app << {pattern: '/', conditions: {media_type: 'application/json'}, target: proc { |env| [200, {}, ['application/json']] }}
192
+ app << {pattern: '/', conditions: {media_type: 'text/html'}, target: proc { |env| [200, {}, ['text/html']] }}
193
+ app << {pattern: '/', priority: 1, target: proc { |env| [200, {}, ['anything_high_priority']] }}
194
+ rt.get('/', {}, 'HTTP_ACCEPT' => 'application/json, */*;q=0.5').body.should == 'anything_high_priority'
200
195
  app.mappings.pop
201
- rt.get('/', {}, 'HTTP_ACCEPT' => 'text/html;q=0.5, application/json').body.should == 'application/json'
202
- rt.get('/', {}, 'HTTP_ACCEPT' => 'text/html, */*;q=0.5').body.should == 'text/html'
196
+ rt.get('/', {}, 'HTTP_ACCEPT' => 'application/json;q=0.5, text/html').body.should == 'text/html'
197
+ rt.get('/', {}, 'HTTP_ACCEPT' => 'application/json, */*').body.should == 'application/json'
198
+ rt.get('/', {}, 'HTTP_ACCEPT' => '*/*, text/*').body.should == 'text/html'
199
+ rt.get('/', {}, 'HTTP_ACCEPT' => '*/*').body.should == 'anything'
203
200
  end
204
201
  end
205
-
202
+
206
203
  describe "conditions" do
207
204
  it "contains a default set of conditions" do
208
205
  app.conditions.should be_a(Options)
209
206
  app.conditions.should include(:method, :media_type)
210
207
  app.conditions.each { |k,v| v.should be_a(Proc) }
211
208
  end
212
-
209
+
213
210
  it "executes route only if all conditions return true" do
214
211
  app << {pattern: '/$', conditions: {method: 'POST'}, target: generic_handler}
215
212
  response = rt.get "/"
216
213
  response.status.should be_between(400, 499)
217
214
  response = rt.post "/"
218
215
  response.status.should == 200
219
-
216
+
220
217
  app.conditions[:has_name] = proc { |name| request.GET['name'] }
221
218
  app << {pattern: '/about', conditions: {method: ['GET', 'POST'], has_name: 'Ronald'}, target: generic_handler}
222
219
  response = rt.get "/about"
@@ -224,28 +221,28 @@ module Scorched
224
221
  response = rt.get "/about", name: 'Ronald'
225
222
  response.status.should == 200
226
223
  end
227
-
224
+
228
225
  it "raises exception when condition doesn't exist or is invalid" do
229
226
  app << {pattern: '/', conditions: {surprise_christmas_turkey: true}, target: generic_handler}
230
227
  expect {
231
228
  rt.get "/"
232
229
  }.to raise_error(Scorched::Error)
233
230
  end
234
-
231
+
235
232
  it "falls through to next route when conditions are not met" do
236
233
  app << {pattern: '/', conditions: {method: 'POST'}, target: proc { |env| [200, {}, ['post']] }}
237
234
  app << {pattern: '/', conditions: {method: 'GET'}, target: proc { |env| [200, {}, ['get']] }}
238
235
  rt.get("/").body.should == 'get'
239
236
  rt.post("/").body.should == 'post'
240
237
  end
241
-
238
+
242
239
  it "inverts the conditions if it's referenced with a trailing exclamation mark" do
243
240
  app << {pattern: '/', conditions: {method!: 'GET'}, target: proc { |env| [200, {}, ['ok']] }}
244
241
  rt.get("/").status.should == 405
245
242
  rt.post("/").status.should == 200
246
243
  end
247
244
  end
248
-
245
+
249
246
  describe "route helpers" do
250
247
  it "allows end points to be defined more succinctly" do
251
248
  route_proc = app.route('/*', 2, method: 'GET') { |capture| capture }
@@ -253,7 +250,7 @@ module Scorched
253
250
  mapping.should == {pattern: mapping[:pattern], priority: 2, conditions: {method: 'GET'}, target: route_proc}
254
251
  rt.get('/about').body.should == 'about'
255
252
  end
256
-
253
+
257
254
  it "can provide a mapping proc without mapping it" do
258
255
  block = proc { |capture| capture }
259
256
  wrapped_block = app.route(&block)
@@ -262,32 +259,32 @@ module Scorched
262
259
  app << {pattern: '/*', target: wrapped_block}
263
260
  rt.get('/turkey').body.should == 'turkey'
264
261
  end
265
-
262
+
266
263
  it "provides a method for every HTTP method" do
267
264
  [:get, :post, :put, :delete, :options, :head, :patch].each do |m|
268
265
  app.send(m, '/say_cool') { 'cool' }
269
266
  rt.send(m, '/say_cool').body.should == (m == :head ? '' : 'cool')
270
267
  end
271
268
  end
272
-
269
+
273
270
  it "provides wildcard captures as arguments" do
274
271
  app.get('/*/**') { |a,b| "#{a} #{b}" }
275
272
  rt.get('/hello/there/dude').body.should == 'hello there/dude'
276
273
  end
277
-
274
+
278
275
  it "provides named captures as individual arguments for each value" do
279
276
  app.get('/:given_name') { |a| a }
280
277
  app.get('/:given_name/::surname') { |a,b| "#{a} #{b}" }
281
278
  rt.get('/bob').body.should == 'bob'
282
279
  rt.get('/bob/smith').body.should == 'bob smith'
283
280
  end
284
-
281
+
285
282
  it "always matches to the end of the URL (implied $)" do
286
283
  app.get('/') { 'awesome '}
287
284
  rt.get('/dog').status.should == 404
288
285
  rt.get('/').status.should == 200
289
286
  end
290
-
287
+
291
288
  it "leaves body empty if nil is returned" do
292
289
  app.get('/') { }
293
290
  app.after do
@@ -295,7 +292,7 @@ module Scorched
295
292
  end
296
293
  rt.get('/')
297
294
  end
298
-
295
+
299
296
  it "can take an array of patterns" do
300
297
  app.get(['/', '/dog']) { 'rad' }
301
298
  rt.get('/dog').status.should == 200
@@ -303,7 +300,7 @@ module Scorched
303
300
  rt.get('/cat').status.should == 404
304
301
  end
305
302
  end
306
-
303
+
307
304
  describe "sub-controllers" do
308
305
  it "should ignore the already matched portions of the path" do
309
306
  app << {pattern: '/article', target: Class.new(Scorched::Controller) do
@@ -321,7 +318,7 @@ module Scorched
321
318
  'hello'
322
319
  end
323
320
  end
324
-
321
+
325
322
  resp = rt.get('/article/name')
326
323
  resp.status.should == 200
327
324
  outer_env['SCRIPT_NAME'].should == ''
@@ -329,7 +326,7 @@ module Scorched
329
326
  inner_env['SCRIPT_NAME'].should == '/article'
330
327
  inner_env['PATH_INFO'].should == '/name'
331
328
  end
332
-
329
+
333
330
  example "PATH_INFO and SCRIPT_NAME joined, should produce a full path" do
334
331
  app.controller '/article/' do
335
332
  get '/name' do
@@ -354,7 +351,7 @@ module Scorched
354
351
  response.status.should == 200
355
352
  response.body.should == 'hello'
356
353
  end
357
-
354
+
358
355
  it "can be given a pattern" do
359
356
  app.controller '/dog' do
360
357
  get('/') { 'roof' }
@@ -363,12 +360,12 @@ module Scorched
363
360
  response.status.should == 200
364
361
  response.body.should == 'roof'
365
362
  end
366
-
363
+
367
364
  it "inherits from parent class, or otherwise the specified class" do
368
365
  app.controller{}.superclass.should == app
369
366
  app.controller('/', String){}.superclass.should == String
370
367
  end
371
-
368
+
372
369
  it "can take mapping options" do
373
370
  app.controller priority: -1, conditions: {method: 'POST'} do
374
371
  route('/') { 'ok' }
@@ -377,7 +374,7 @@ module Scorched
377
374
  rt.get('/').status.should be_between(400, 499)
378
375
  rt.post('/').body.should == 'ok'
379
376
  end
380
-
377
+
381
378
  it "automatically passes to the outer controller when no match" do
382
379
  filters_run = 0
383
380
  app.controller do
@@ -389,7 +386,7 @@ module Scorched
389
386
  rt.get('/').body.should == 'hello'
390
387
  filters_run.should == 0
391
388
  end
392
-
389
+
393
390
  it "can be used to map a predefined controller" do
394
391
  person_controller = Class.new(Scorched::Controller) do
395
392
  get('/name') { 'George' }
@@ -399,7 +396,7 @@ module Scorched
399
396
  end
400
397
  end
401
398
  end
402
-
399
+
403
400
  describe "before/after filters" do
404
401
  they "run directly before and after the target action" do
405
402
  order = []
@@ -409,7 +406,7 @@ module Scorched
409
406
  rt.get('/')
410
407
  order.should == [:before, :action, :after]
411
408
  end
412
-
409
+
413
410
  they "run in the context of the controller (same as the route)" do
414
411
  route_instance = nil
415
412
  before_instance = nil
@@ -421,7 +418,7 @@ module Scorched
421
418
  route_instance.should == before_instance
422
419
  route_instance.should == after_instance
423
420
  end
424
-
421
+
425
422
  they "should run even if no route matches" do
426
423
  counter = 0
427
424
  app.before { counter += 1 }
@@ -429,7 +426,7 @@ module Scorched
429
426
  rt.delete('/').status.should == 404
430
427
  counter.should == 2
431
428
  end
432
-
429
+
433
430
  they "can take an optional set of conditions" do
434
431
  counter = 0
435
432
  app.before(method: ['GET', 'PUT']) { counter += 1 }
@@ -439,7 +436,7 @@ module Scorched
439
436
  rt.put('/')
440
437
  counter.should == 4
441
438
  end
442
-
439
+
443
440
  they "execute in the order they're defined" do
444
441
  order = []
445
442
  app.before { order << :first }
@@ -449,7 +446,7 @@ module Scorched
449
446
  rt.get('/')
450
447
  order.should == %i{first second third fourth}
451
448
  end
452
-
449
+
453
450
  describe "nesting" do
454
451
  example "filters inherit but only run once" do
455
452
  before_counter, after_counter = 0, 0
@@ -458,18 +455,18 @@ module Scorched
458
455
  subcontroller = app.controller { get('/') { 'wow' } }
459
456
  subcontroller.filters[:before].should == app.filters[:before]
460
457
  subcontroller.filters[:after].should == app.filters[:after]
461
-
458
+
462
459
  rt.get('/')
463
460
  before_counter.should == 1
464
461
  after_counter.should == 1
465
-
462
+
466
463
  # Hitting the subcontroller directly should yield the same results.
467
464
  before_counter, after_counter = 0, 0
468
465
  Rack::Test::Session.new(subcontroller).get('/')
469
466
  before_counter.should == 1
470
467
  after_counter.should == 1
471
468
  end
472
-
469
+
473
470
  example "before filters run from outermost to innermost" do
474
471
  order = []
475
472
  app.before { order << :outer }
@@ -482,7 +479,7 @@ module Scorched
482
479
  rt.get('/')
483
480
  order.should == %i{outer outer2 inner inner2}
484
481
  end
485
-
482
+
486
483
  example "after filters run from innermost to outermost" do
487
484
  order = []
488
485
  app.after { order << :outer }
@@ -495,7 +492,7 @@ module Scorched
495
492
  rt.get('/')
496
493
  order.should == %i{inner inner2 outer outer2}
497
494
  end
498
-
495
+
499
496
  example "inherited filters which fail to satisfy their conditions are re-evaluated at every level" do
500
497
  order = []
501
498
  sub_class = app.controller do
@@ -518,7 +515,7 @@ module Scorched
518
515
  end
519
516
  end
520
517
  end
521
-
518
+
522
519
  describe "error filters" do
523
520
  let(:app) do
524
521
  Class.new(Scorched::Controller) do
@@ -527,26 +524,26 @@ module Scorched
527
524
  end
528
525
  end
529
526
  end
530
-
527
+
531
528
  they "catch exceptions" do
532
529
  app.error { response.status = 500 }
533
530
  rt.get('/').status.should == 500
534
531
  end
535
-
532
+
536
533
  they "receive the exception object as their first argument" do
537
534
  error = nil
538
535
  app.error { |e| error = e }
539
536
  rt.get('/')
540
537
  error.should be_a(StandardError)
541
538
  end
542
-
539
+
543
540
  they "try the next handler if the previous handler returns false" do
544
541
  handlers_called = 0
545
542
  app.error { handlers_called += 1 }
546
543
  app.error { handlers_called += 1 }
547
544
  rt.get '/'
548
545
  handlers_called.should == 1
549
-
546
+
550
547
  app.error_filters.clear
551
548
  handlers_called = 0
552
549
  app.error { handlers_called += 1; false }
@@ -554,45 +551,45 @@ module Scorched
554
551
  rt.get '/'
555
552
  handlers_called.should == 2
556
553
  end
557
-
554
+
558
555
  they "still runs after filters if route error is handled" do
559
556
  app.after { response.status = 111 }
560
557
  app.error { true }
561
558
  rt.get('/').status.should == 111
562
559
  end
563
-
560
+
564
561
  they "can handle exceptions in before/after filters" do
565
562
  app.error { |e| response.write e.class.name }
566
563
  app.after { raise ArgumentError }
567
564
  rt.get('/').body.should == 'StandardErrorArgumentError'
568
565
  end
569
-
566
+
570
567
  they "swallow halts when executed in an outer context" do
571
568
  app.before { raise "Big bad error" }
572
569
  app.error { throw :halt }
573
570
  rt.get('/') # Would otherwise bomb out with uncaught throw.
574
571
  end
575
-
572
+
576
573
  they "only get called once per error" do
577
574
  times_called = 0
578
575
  app.error { times_called += 1 }
579
576
  rt.get '/'
580
577
  times_called.should == 1
581
578
  end
582
-
579
+
583
580
  they "fall through when unhandled" do
584
581
  expect {
585
582
  rt.get '/'
586
583
  }.to raise_error(StandardError)
587
584
  end
588
-
585
+
589
586
  they "can optionally filter on one or more exception types" do
590
587
  app.get('/arg_error') { raise ArgumentError }
591
-
588
+
592
589
  app.error(StandardError, ArgumentError) { true }
593
590
  rt.get '/'
594
591
  rt.get '/arg_error'
595
-
592
+
596
593
  app.error_filters.clear
597
594
  app.error(ArgumentError) { true }
598
595
  expect {
@@ -600,7 +597,7 @@ module Scorched
600
597
  }.to raise_error(StandardError)
601
598
  rt.get '/arg_error'
602
599
  end
603
-
600
+
604
601
  they "can take an optional set of conditions" do
605
602
  app.error(method: ['GET', 'PUT']) { true }
606
603
  expect {
@@ -610,7 +607,7 @@ module Scorched
610
607
  rt.put('/')
611
608
  end
612
609
  end
613
-
610
+
614
611
  describe "middleware" do
615
612
  let(:app) do
616
613
  Class.new(Scorched::Controller) do
@@ -625,56 +622,56 @@ module Scorched
625
622
  end
626
623
  end
627
624
  end
628
-
625
+
629
626
  it "is only included once by default" do
630
627
  rt.get('/').body.should == '1'
631
628
  rt.get('/sub_controller').body.should == '1'
632
629
  end
633
-
630
+
634
631
  it "can be explicitly included more than once in sub-controllers" do
635
632
  app.mappings[-1][:target].middleware << proc { use Scorched::SimpleCounter }
636
633
  rt.get('/').body.should == '1'
637
634
  rt.get('/sub_controller').body.should == '2'
638
635
  end
639
636
  end
640
-
637
+
641
638
  describe "halting" do
642
639
  it "short circuits current request" do
643
640
  has_run = false
644
641
  app.get('/') { halt; has_run = true }
645
642
  rt.get '/'
646
- has_run.should be_false
643
+ has_run.should be_falsey
647
644
  end
648
-
645
+
649
646
  it "takes an optional status" do
650
647
  app.get('/') { halt 600 }
651
648
  rt.get('/').status.should == 600
652
649
  end
653
-
650
+
654
651
  it "takes an optional response body" do
655
652
  app.get('/') { halt 'cool' }
656
653
  rt.get('/').body.should == 'cool'
657
654
  end
658
-
655
+
659
656
  it "can take a status and a response body" do
660
657
  app.get('/') { halt 600, 'cool' }
661
658
  rt.get('/').status.should == 600
662
659
  rt.get('/').body.should == 'cool'
663
660
  end
664
-
661
+
665
662
  it "still processes filters" do
666
663
  app.after { response.status = 600 }
667
664
  app.get('/') { halt }
668
665
  rt.get('/').status.should == 600
669
666
  end
670
-
667
+
671
668
  describe "within filters" do
672
669
  it "short circuits filters if halted within filter" do
673
670
  app.before { halt }
674
671
  app.after { response.status = 600 }
675
672
  rt.get('/').status.should_not == 600
676
673
  end
677
-
674
+
678
675
  it "forced filters are always run" do
679
676
  app.before { halt }
680
677
  app.after(force: true) { response.status = 600 }
@@ -682,7 +679,7 @@ module Scorched
682
679
  app.get('/') { 'hello' }
683
680
  rt.get('/').status.should == 600
684
681
  end
685
-
682
+
686
683
  it "halting within a forced filter still runs other forced filters" do
687
684
  app.before { halt }
688
685
  app.before(force: true) { halt }
@@ -694,7 +691,7 @@ module Scorched
694
691
  end
695
692
  end
696
693
  end
697
-
694
+
698
695
  describe 'redirecting' do
699
696
  it "redirects using 303 or 302 by default, depending on HTTP version" do
700
697
  app.get('/cat') { redirect '/dog' }
@@ -705,12 +702,12 @@ module Scorched
705
702
  response.status.should == 302
706
703
  response.location.should == '/dog'
707
704
  end
708
-
705
+
709
706
  it "allows the HTTP status to be overridden" do
710
707
  app.get('/') { redirect '/somewhere', 308 }
711
708
  rt.get('/').status.should == 308
712
709
  end
713
-
710
+
714
711
  it "halts the request after redirect" do
715
712
  var = false
716
713
  app.get('/') do
@@ -720,7 +717,7 @@ module Scorched
720
717
  rt.get('/')
721
718
  var.should == false
722
719
  end
723
-
720
+
724
721
  it "works in filters" do
725
722
  app.error { redirect '/somewhere' }
726
723
  app.get('/') { raise "Some error" }
@@ -729,7 +726,7 @@ module Scorched
729
726
  rt.get('/').location.should == '/somewhere_else'
730
727
  end
731
728
  end
732
-
729
+
733
730
  describe "passing" do
734
731
  it "invokes the next match" do
735
732
  app.get('/') { response.body << 'hello'; pass }
@@ -738,7 +735,7 @@ module Scorched
738
735
  app.get('/') { response.body << '!' } # Shouldn't be hit
739
736
  rt.get('/').body.should == 'hello there sir'
740
737
  end
741
-
738
+
742
739
  it "invokes the next match in parent controller if passed from filter" do
743
740
  effects = []
744
741
  app.controller '/sub' do
@@ -756,7 +753,7 @@ module Scorched
756
753
  rt.get('/sub').body.should == 'y'
757
754
  effects.should == [1, 2]
758
755
  end
759
-
756
+
760
757
  it "results in uncaught symbol if passing within filter of root controller " do
761
758
  app.before { pass }
762
759
  expect {
@@ -764,36 +761,36 @@ module Scorched
764
761
  rt.get('/')
765
762
  }.to raise_error(ArgumentError)
766
763
  end
767
-
764
+
768
765
  it "is not considered a match if a mapping passes the request" do
769
766
  app.get('/*') { pass }
770
767
  app.get('/nopass') { }
771
768
  handled = nil
772
769
  app.after { handled = @_handled }
773
770
  rt.get('/').status.should == 404 # 404 if matched, but passed
774
- handled.should_not be_true
771
+ handled.should be_falsey
775
772
  rt.get('/nopass').status.should == 200
776
- handled.should be_true
773
+ handled.should be_truthy
777
774
  end
778
775
  end
779
-
776
+
780
777
  describe "status codes" do
781
778
  it "returns 405 when :method condition fails" do
782
779
  app.get('/') { }
783
780
  rt.post('/').status.should == 405
784
781
  end
785
-
782
+
786
783
  it "returns 404 when :host condition fails" do
787
784
  app.get('/', host: 'somehost') { }
788
785
  rt.get('/').status.should == 404
789
786
  end
790
-
787
+
791
788
  it "returns 406 when accept-related conditions fail" do
792
789
  app.get('/media_type', media_type: 'application/json') { }
793
790
  app.get('/charset', charset: 'iso-8859-5') { }
794
791
  app.get('/encoding', encoding: 'gzip') { }
795
792
  app.get('/language', language: 'en') { }
796
-
793
+
797
794
  rt.get('/media_type', {}, 'HTTP_ACCEPT' => 'application/json').status.should == 200
798
795
  rt.get('/media_type', {}, 'HTTP_ACCEPT' => 'text/html').status.should == 406
799
796
  rt.get('/charset', {}, 'HTTP_ACCEPT_CHARSET' => 'iso-8859-5').status.should == 200
@@ -804,7 +801,7 @@ module Scorched
804
801
  rt.get('/language', {}, 'HTTP_ACCEPT_LANGUAGE' => 'da').status.should == 406
805
802
  end
806
803
  end
807
-
804
+
808
805
  describe "configuration" do
809
806
  describe :strip_trailing_slash do
810
807
  it "can be set to strip trailing slash and redirect" do
@@ -814,7 +811,7 @@ module Scorched
814
811
  response.status.should == 307
815
812
  response['Location'].should == '/test'
816
813
  end
817
-
814
+
818
815
  it "can be set to ignore trailing slash while pattern matching" do
819
816
  app.config[:strip_trailing_slash] = :ignore
820
817
  hit = false
@@ -822,17 +819,17 @@ module Scorched
822
819
  rt.get('/test/').status.should == 200
823
820
  hit.should == true
824
821
  end
825
-
822
+
826
823
  it "can be set not do nothing with a trailing slash" do
827
824
  app.config[:strip_trailing_slash] = false
828
825
  app.get('/test') { }
829
826
  rt.get('/test/').status.should == 404
830
-
827
+
831
828
  app.get('/test/') { }
832
829
  rt.get('/test/').status.should == 200
833
830
  end
834
831
  end
835
-
832
+
836
833
  describe :static_dir do
837
834
  it "can serve static file from the specific directory" do
838
835
  app.config[:static_dir] = 'public'
@@ -840,23 +837,23 @@ module Scorched
840
837
  response.status.should == 200
841
838
  response.body.should == 'My static file!'
842
839
  end
843
-
840
+
844
841
  it "can be disabled" do
845
842
  app.config[:static_dir] = false
846
843
  response = rt.get('/static.txt')
847
844
  response.status.should == 404
848
845
  end
849
846
  end
850
-
847
+
851
848
  describe :show_exceptions do
852
849
  it "shows debug-friendly error page for unhandled exceptions" do
853
850
  app.config[:show_exceptions] = true
854
851
  app.get('/') { raise RuntimeError, "Kablamo!" }
855
- response = rt.get('/')
852
+ response = rt.get('/', {}, 'HTTP_ACCEPT' => 'text/html')
856
853
  response.status.should == 500
857
854
  response.body.should include('Rack::ShowExceptions')
858
855
  end
859
-
856
+
860
857
  it "can be disabled" do
861
858
  app.config[:show_exceptions] = false
862
859
  app.get('/') { raise RuntimeError, "Kablamo!" }
@@ -865,7 +862,7 @@ module Scorched
865
862
  }.to raise_error(RuntimeError)
866
863
  end
867
864
  end
868
-
865
+
869
866
  describe :show_http_error_pages do
870
867
  it "shows HTTP error pages for errors 400 to 599" do
871
868
  app.config[:show_http_error_pages] = true
@@ -875,7 +872,7 @@ module Scorched
875
872
  rt.post('/').body.should include('405 Method Not Allowed')
876
873
  rt.get('/unknown').body.should include('480 ')
877
874
  end
878
-
875
+
879
876
  it "can be disabled" do
880
877
  app.config[:show_http_error_pages] = false
881
878
  app.get('/') { response.status = 501; '' }
@@ -885,7 +882,7 @@ module Scorched
885
882
  rt.post('/unknown').body.should_not include('408 ')
886
883
  end
887
884
  end
888
-
885
+
889
886
  describe :auto_pass do
890
887
  it "passes to the outer controller without running any filters, if no match" do
891
888
  sub = Class.new(Scorched::Controller) do
@@ -900,22 +897,22 @@ module Scorched
900
897
  rt.get('/').status.should == 200
901
898
  rt.get('/hello').body.should == 'hello'
902
899
  rt.get('/hello').status.should == 600
903
-
900
+
904
901
  sub.config[:auto_pass] = false
905
902
  rt.get('/').body.should == ''
906
903
  rt.get('/').status.should == 600
907
904
  end
908
905
  end
909
-
906
+
910
907
  describe :cache_templates do
911
908
  before(:each) do
912
909
  File.open('views/temp.str', 'w') { |f| f.write 'hello world' }
913
910
  end
914
-
911
+
915
912
  after(:all) {
916
913
  File.unlink 'views/temp.str'
917
914
  }
918
-
915
+
919
916
  it "can cache templates" do
920
917
  app.config[:cache_templates] = true
921
918
  app.get('/') { render :'temp.str' }
@@ -923,7 +920,7 @@ module Scorched
923
920
  File.open('views/temp.str', 'a') { |f| f.write '!!!' }
924
921
  rt.get('/').body.should == 'hello world'
925
922
  end
926
-
923
+
927
924
  it "can be set not to cache templates" do
928
925
  app.config[:cache_templates] = false
929
926
  app.get('/') { render :'temp.str' }
@@ -933,7 +930,7 @@ module Scorched
933
930
  end
934
931
  end
935
932
  end
936
-
933
+
937
934
  describe "sessions" do
938
935
  it "provides convenience method for accessing the Rack session" do
939
936
  rack_session = nil
@@ -944,49 +941,49 @@ module Scorched
944
941
  rt.get('/')
945
942
  rack_session.should be_a(Rack::Session::Abstract::SessionHash)
946
943
  end
947
-
944
+
948
945
  describe "flash" do
949
946
  before(:each) do
950
947
  app.middleware << proc { use Rack::Session::Cookie, secret: 'test' }
951
948
  end
952
-
949
+
953
950
  it "keeps session variables that live for one page load" do
954
951
  app.get('/set') { flash[:cat] = 'meow' }
955
952
  app.get('/get') { flash[:cat] }
956
-
953
+
957
954
  rt.get('/set')
958
955
  rt.get('/get').body.should == 'meow'
959
956
  rt.get('/get').body.should == ''
960
957
  end
961
-
958
+
962
959
  it "always reads from the original request flash" do
963
960
  app.get('/') do
964
961
  flash[:counter] = flash[:counter] ? flash[:counter] + 1 : 0
965
962
  flash[:counter].to_s
966
963
  end
967
-
964
+
968
965
  rt.get('/').body.should == ''
969
966
  rt.get('/').body.should == '0'
970
967
  rt.get('/').body.should == '1'
971
968
  end
972
-
969
+
973
970
  it "can only remove flash variables if the flash object is accessed" do
974
971
  app.get('/set') { flash[:cat] = 'meow' }
975
972
  app.get('/get') { flash[:cat] }
976
973
  app.get('/null') { }
977
-
974
+
978
975
  rt.get('/set')
979
976
  rt.get('/null')
980
977
  rt.get('/get').body.should == 'meow'
981
978
  rt.get('/get').body.should == ''
982
979
  end
983
-
980
+
984
981
  it "can keep multiple sets of flash session variables" do
985
982
  app.get('/set_animal') { flash(:animals)[:cat] = 'meow' }
986
983
  app.get('/get_animal') { flash(:animals)[:cat] }
987
984
  app.get('/set_name') { flash(:names)[:jeff] = 'male' }
988
985
  app.get('/get_name') { flash(:names)[:jeff] }
989
-
986
+
990
987
  rt.get('/set_animal')
991
988
  rt.get('/set_name')
992
989
  rt.get('/get_animal').body.should == 'meow'
@@ -996,7 +993,7 @@ module Scorched
996
993
  end
997
994
  end
998
995
  end
999
-
996
+
1000
997
  describe "cookie helper" do
1001
998
  it "sets, retrieves and deletes cookies" do
1002
999
  app.get('/') { cookie :test }
@@ -1004,7 +1001,7 @@ module Scorched
1004
1001
  app.post('/goodbye') { cookie :test, {value: 'goodbye', expires: Time.now() + 999999 } }
1005
1002
  app.delete('/') { cookie :test, nil }
1006
1003
  app.delete('/alt') { cookie :test, {value: nil} }
1007
-
1004
+
1008
1005
  rt.get('/').body.should == ''
1009
1006
  rt.post('/')
1010
1007
  rt.get('/').body.should == 'hello'
@@ -1016,7 +1013,7 @@ module Scorched
1016
1013
  rt.get('/').body.should == ''
1017
1014
  end
1018
1015
  end
1019
-
1016
+
1020
1017
  describe "rendering" do
1021
1018
  before(:each) do
1022
1019
  app.render_defaults.each { |k,v| app.render_defaults[k] = nil }
@@ -1042,7 +1039,7 @@ module Scorched
1042
1039
  end
1043
1040
  rt.get('/')
1044
1041
  end
1045
-
1042
+
1046
1043
  it "properly respects absolute and relative file paths in respect to the view directory" do
1047
1044
  app.get('/relative') do
1048
1045
  render(:'../views/main.erb', dir: 'views')
@@ -1105,14 +1102,14 @@ module Scorched
1105
1102
  end
1106
1103
  rt.get('/').body.should == '({1 for none}{1 for none})'
1107
1104
  end
1108
-
1105
+
1109
1106
  it "can pass local variables through to view" do
1110
1107
  app.get '/' do
1111
1108
  render '<%= var %>', engine: 'erb', dir: 'views', locals: {var: 'hello sailor'}
1112
1109
  end
1113
1110
  rt.get('/').body.should == 'hello sailor'
1114
1111
  end
1115
-
1112
+
1116
1113
  it "provides a means for passing options directly to tilt" do
1117
1114
  Tilt.register(Class.new(Tilt::ERBTemplate) do
1118
1115
  def prepare
@@ -1120,13 +1117,13 @@ module Scorched
1120
1117
  super
1121
1118
  end
1122
1119
  end, 'test')
1123
-
1120
+
1124
1121
  app.get '/safe' do
1125
1122
  render '<%= var %>', engine: 'test', dir: 'views', locals: {var: 'hello sailor'}
1126
1123
  render '<%= var %>', engine: 'test', dir: 'views', locals: {var: 'hello sailor'}, tilt: {engine: Class.new}
1127
1124
  end
1128
1125
  rt.get('/safe').body.should == 'hello sailor'
1129
-
1126
+
1130
1127
  app.get '/boomer' do
1131
1128
  render '<%= var %>', engine: 'test', dir: 'views', locals: {var: 'hello sailor'}, tilt: {engine: 'invalid'}
1132
1129
  end
@@ -1135,12 +1132,12 @@ module Scorched
1135
1132
  }.to raise_error(NoMethodError)
1136
1133
  end
1137
1134
  end
1138
-
1135
+
1139
1136
  describe "url helpers" do
1140
1137
  let(:my_app) do
1141
1138
  Class.new(Scorched::Controller)
1142
1139
  end
1143
-
1140
+
1144
1141
  let(:root_app) do
1145
1142
  Class.new(Scorched::Controller)
1146
1143
  end
@@ -1152,7 +1149,7 @@ module Scorched
1152
1149
  builder.map('/') { run this.root_app }
1153
1150
  builder.to_app
1154
1151
  end
1155
-
1152
+
1156
1153
  it "can determine the root path of the current Scorched application" do
1157
1154
  my_app.controller '/article' do
1158
1155
  get '/name' do
@@ -1161,26 +1158,26 @@ module Scorched
1161
1158
  end
1162
1159
  rt.get('/myapp/article/name').body.should == '/myapp'
1163
1160
  end
1164
-
1161
+
1165
1162
  describe "url" do
1166
1163
  it "returns the fully qualified URL" do
1167
1164
  my_app.get('/') { url }
1168
1165
  rt.get('https://scorchedrb.com:73/myapp?something=true').body.should ==
1169
1166
  'https://scorchedrb.com:73/myapp'
1170
1167
  end
1171
-
1168
+
1172
1169
  it "can append an optional path" do
1173
1170
  my_app.get('/') { url('hello') }
1174
1171
  rt.get('https://scorchedrb.com:73/myapp?something=true').body.should ==
1175
1172
  'https://scorchedrb.com:73/myapp/hello'
1176
1173
  end
1177
-
1174
+
1178
1175
  it "returns the given URL if scheme detected" do
1179
1176
  test_url = 'http://google.com/blah'
1180
1177
  my_app.get('/') { url(test_url) }
1181
1178
  rt.get('/myapp').body.should == test_url
1182
1179
  end
1183
-
1180
+
1184
1181
  it "generates URL from inside subcontroller defined with controller helper" do
1185
1182
  root_app.controller '/sub2' do
1186
1183
  get('/') { url('hi') }
@@ -1188,29 +1185,29 @@ module Scorched
1188
1185
  rt.get('https://scorchedrb.com:73/sub2').body.should == 'https://scorchedrb.com:73/hi'
1189
1186
  end
1190
1187
  end
1191
-
1188
+
1192
1189
  describe "absolute" do
1193
1190
  it "returns an absolute URL path" do
1194
1191
  my_app.get('/absolute') { absolute }
1195
1192
  rt.get('http://scorchedrb.com/myapp/absolute?something=true').body.should == '/myapp'
1196
1193
  end
1197
-
1194
+
1198
1195
  it "returns a forward slash if script name is the root of the URL path" do
1199
1196
  root_app.get('/') { absolute }
1200
1197
  rt.get('http://scorchedrb.com').body.should == '/'
1201
1198
  end
1202
-
1199
+
1203
1200
  it "can append an optional path" do
1204
1201
  my_app.get('/absolute') { absolute('hello') }
1205
1202
  rt.get('http://scorchedrb.com/myapp/absolute?something=true').body.should == '/myapp/hello'
1206
1203
  end
1207
-
1204
+
1208
1205
  it "returns the given URL if scheme detected" do
1209
1206
  test_url = 'http://google.com/blah'
1210
1207
  my_app.get('/') { absolute(test_url) }
1211
1208
  rt.get('/myapp').body.should == test_url
1212
1209
  end
1213
-
1210
+
1214
1211
  it "returns an absolute URL path for subcontroller defined with controller helper" do
1215
1212
  root_app.controller '/sub2' do
1216
1213
  get('/') { absolute }
@@ -1219,7 +1216,7 @@ module Scorched
1219
1216
  end
1220
1217
  end
1221
1218
  end
1222
-
1219
+
1223
1220
  describe "delegators" do
1224
1221
  it "delegates captures" do
1225
1222
  app.get('/:id') { captures[:id] }