regal 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/.yardopts +1 -1
- data/README.md +145 -0
- data/lib/regal/app.rb +395 -137
- data/lib/regal/request.rb +17 -14
- data/lib/regal/response.rb +24 -0
- data/lib/regal/version.rb +2 -1
- data/spec/regal/app_spec.rb +606 -146
- data/spec/regal/request_spec.rb +12 -19
- data/spec/spec_helper.rb +7 -0
- metadata +4 -2
data/spec/regal/app_spec.rb
CHANGED
@@ -47,6 +47,8 @@ module Regal
|
|
47
47
|
it 'responds with 404 when the path does not match any route' do
|
48
48
|
get '/hello/fnord'
|
49
49
|
expect(last_response.status).to eq(404)
|
50
|
+
get '/foo/bar/baz/qux'
|
51
|
+
expect(last_response.status).to eq(404)
|
50
52
|
end
|
51
53
|
|
52
54
|
it 'responds with 405 when the path matches a route but there is no handler for the HTTP method' do
|
@@ -129,10 +131,31 @@ module Regal
|
|
129
131
|
end
|
130
132
|
|
131
133
|
context 'an app doing work before route handlers' do
|
132
|
-
|
133
|
-
|
134
|
+
MountedBeforeApp = App.create do
|
135
|
+
before do |request|
|
136
|
+
request.attributes[:some_key] << 2
|
137
|
+
end
|
138
|
+
|
139
|
+
route 'in-mounted-app' do
|
134
140
|
before do |request|
|
141
|
+
request.attributes[:some_key] << 3
|
142
|
+
end
|
143
|
+
|
144
|
+
get do |request|
|
145
|
+
request.attributes[:some_key].join(',')
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
let :state do
|
151
|
+
{}
|
152
|
+
end
|
153
|
+
|
154
|
+
let :app do
|
155
|
+
App.new(state: state) do
|
156
|
+
before do |request, _|
|
135
157
|
request.attributes[:some_key] = [1]
|
158
|
+
request.attributes[:state][:before] = :called
|
136
159
|
end
|
137
160
|
|
138
161
|
get do |request|
|
@@ -185,7 +208,8 @@ module Regal
|
|
185
208
|
response.body = 'whoopiedoo'
|
186
209
|
end
|
187
210
|
|
188
|
-
get do
|
211
|
+
get do |_, response|
|
212
|
+
response.headers['X-HandlerCalled'] = 'yes'
|
189
213
|
"I'm not called!"
|
190
214
|
end
|
191
215
|
|
@@ -193,6 +217,8 @@ module Regal
|
|
193
217
|
response.headers['WasAfterCalled'] = 'yes'
|
194
218
|
end
|
195
219
|
end
|
220
|
+
|
221
|
+
mount MountedBeforeApp
|
196
222
|
end
|
197
223
|
end
|
198
224
|
|
@@ -221,6 +247,14 @@ module Regal
|
|
221
247
|
expect(last_response.status).to eq(307)
|
222
248
|
end
|
223
249
|
|
250
|
+
context 'when the path does not match any route' do
|
251
|
+
it 'does not run any before blocks' do
|
252
|
+
get '/does-not-exist'
|
253
|
+
expect(last_response.status).to eq(404)
|
254
|
+
expect(state).to be_empty
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
224
258
|
context 'when the response is marked as finished' do
|
225
259
|
before do
|
226
260
|
get '/redirect-before'
|
@@ -228,20 +262,49 @@ module Regal
|
|
228
262
|
|
229
263
|
it 'does not call further handlers or before blocks when the response is marked as finished' do
|
230
264
|
expect(last_response.body).to eq('Go somewhere else')
|
265
|
+
expect(last_response.headers).to_not have_key('X-HandlerCalled')
|
231
266
|
end
|
232
267
|
|
233
268
|
it 'calls after blocks' do
|
234
269
|
expect(last_response.headers).to include('WasAfterCalled' => 'yes')
|
235
270
|
end
|
236
271
|
end
|
272
|
+
|
273
|
+
context 'with a mounted app' do
|
274
|
+
it 'runs the before blocks from both the mounting and the mounted app' do
|
275
|
+
get '/in-mounted-app'
|
276
|
+
expect(last_response.body).to eq('1,2,3')
|
277
|
+
end
|
278
|
+
end
|
237
279
|
end
|
238
280
|
|
239
281
|
context 'an app doing work after route handlers' do
|
240
|
-
|
241
|
-
|
282
|
+
MountedAfterApp = App.create do
|
283
|
+
after do |_, response|
|
284
|
+
response.body['list'] << 1
|
285
|
+
end
|
286
|
+
|
287
|
+
route 'in-mounted-app' do
|
242
288
|
after do |_, response|
|
289
|
+
response.body['list'] << 2
|
290
|
+
end
|
291
|
+
|
292
|
+
get do
|
293
|
+
{'list' => []}
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
let :state do
|
299
|
+
{}
|
300
|
+
end
|
301
|
+
|
302
|
+
let :app do
|
303
|
+
App.new(state: state) do
|
304
|
+
after do |request, response|
|
243
305
|
response.headers['Content-Type'] = 'application/json'
|
244
|
-
response.body = JSON.dump(response.body)
|
306
|
+
response.body = response.body.is_a?(String) ? %("#{response.body}") : JSON.dump(response.body)
|
307
|
+
request.attributes[:state][:after] = :called
|
245
308
|
end
|
246
309
|
|
247
310
|
get do |request|
|
@@ -260,11 +323,11 @@ module Regal
|
|
260
323
|
|
261
324
|
route 'two-after' do
|
262
325
|
after do |request, response|
|
263
|
-
response.body['list'] <<
|
326
|
+
response.body['list'] << 2
|
264
327
|
end
|
265
328
|
|
266
329
|
after do |request, response|
|
267
|
-
response.body['list'] <<
|
330
|
+
response.body['list'] << 1
|
268
331
|
end
|
269
332
|
|
270
333
|
get do |request|
|
@@ -281,6 +344,74 @@ module Regal
|
|
281
344
|
end
|
282
345
|
end
|
283
346
|
end
|
347
|
+
|
348
|
+
route 'stops-early' do
|
349
|
+
before do |_, response|
|
350
|
+
response.body = 'before1'
|
351
|
+
response.finish
|
352
|
+
end
|
353
|
+
|
354
|
+
after do |_, response|
|
355
|
+
response.body << '|after1'
|
356
|
+
end
|
357
|
+
|
358
|
+
route 'not-called' do
|
359
|
+
before do |_, response|
|
360
|
+
response.body << '|before2'
|
361
|
+
end
|
362
|
+
|
363
|
+
after do |_, response|
|
364
|
+
response.body << '|after2'
|
365
|
+
end
|
366
|
+
|
367
|
+
get do |_, response|
|
368
|
+
response.body << '|handler'
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
route 'raises' do
|
374
|
+
before do |_, response|
|
375
|
+
response.body = 'before1'
|
376
|
+
end
|
377
|
+
|
378
|
+
after do |_, response|
|
379
|
+
response.body << '|after1'
|
380
|
+
end
|
381
|
+
|
382
|
+
rescue_from RuntimeError do
|
383
|
+
end
|
384
|
+
|
385
|
+
route 'not-called' do
|
386
|
+
before do |_, response|
|
387
|
+
response.body << '|before2'
|
388
|
+
end
|
389
|
+
|
390
|
+
after do |_, response|
|
391
|
+
response.body << '|after2'
|
392
|
+
end
|
393
|
+
|
394
|
+
get do |_, response|
|
395
|
+
response.body << '|handler'
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
route 'raises' do
|
400
|
+
before do |_, response|
|
401
|
+
response.body << '|before2'
|
402
|
+
raise 'Burk!'
|
403
|
+
end
|
404
|
+
|
405
|
+
after do |_, response|
|
406
|
+
response.body << '|after2'
|
407
|
+
end
|
408
|
+
|
409
|
+
get do
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
mount MountedAfterApp
|
284
415
|
end
|
285
416
|
end
|
286
417
|
|
@@ -303,6 +434,112 @@ module Regal
|
|
303
434
|
get '/two-after/another-after'
|
304
435
|
expect(last_response.body).to eq('{"list":[3,2,1]}')
|
305
436
|
end
|
437
|
+
|
438
|
+
context 'when the path does not match any route' do
|
439
|
+
it 'does not run any before blocks' do
|
440
|
+
get '/does-not-exist'
|
441
|
+
expect(last_response.status).to eq(404)
|
442
|
+
expect(state).to be_empty
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
context 'with a mounted app' do
|
447
|
+
it 'runs the after blocks from both the mounting and the mounted app' do
|
448
|
+
get '/in-mounted-app'
|
449
|
+
expect(last_response.body).to eq('{"list":[2,1]}')
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
context 'when the request is finished by a before block' do
|
454
|
+
it 'runs only the after blocks from the same level and up' do
|
455
|
+
get '/stops-early/not-called'
|
456
|
+
expect(last_response.body).to eq('"before1|after1"')
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
context 'when a before block raises an error and it is handled by a rescue block' do
|
461
|
+
it 'runs only the after blocks from the same level as the rescue block, and up' do
|
462
|
+
get '/raises/raises'
|
463
|
+
expect(last_response.body).to eq('"before1|before2|after1"')
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
context 'when rescue block raises an error that is handled a few levels up' do
|
468
|
+
let :app do
|
469
|
+
App.new do
|
470
|
+
after do |_, response|
|
471
|
+
response.body = response.body.join('|')
|
472
|
+
end
|
473
|
+
|
474
|
+
route 'one' do
|
475
|
+
after do |_, response|
|
476
|
+
response.body << 'after1'
|
477
|
+
end
|
478
|
+
|
479
|
+
route 'two' do
|
480
|
+
after do |_, response|
|
481
|
+
response.body << 'after2'
|
482
|
+
end
|
483
|
+
|
484
|
+
rescue_from StandardError do
|
485
|
+
end
|
486
|
+
|
487
|
+
route 'three' do
|
488
|
+
after do |_, response|
|
489
|
+
response.body << 'after3'
|
490
|
+
end
|
491
|
+
|
492
|
+
route 'four' do
|
493
|
+
after do |_, response|
|
494
|
+
response.body << 'after4'
|
495
|
+
end
|
496
|
+
|
497
|
+
rescue_from StandardError do
|
498
|
+
raise 'Snork'
|
499
|
+
end
|
500
|
+
|
501
|
+
route 'raise-in-after' do
|
502
|
+
after do |_, response|
|
503
|
+
raise 'Bork'
|
504
|
+
end
|
505
|
+
|
506
|
+
get do
|
507
|
+
[]
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
route 'raise-in-before' do
|
512
|
+
before do |_, response|
|
513
|
+
response.body = []
|
514
|
+
raise 'Bork'
|
515
|
+
end
|
516
|
+
|
517
|
+
get do
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
route 'raise-in-handler' do
|
522
|
+
get do |_, response|
|
523
|
+
response.body = []
|
524
|
+
raise 'Bork'
|
525
|
+
end
|
526
|
+
end
|
527
|
+
end
|
528
|
+
end
|
529
|
+
end
|
530
|
+
end
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
it 'runs only the after blocks from the same level as the rescue block, and up' do
|
535
|
+
get '/one/two/three/four/raise-in-after'
|
536
|
+
expect(last_response.body).to eq('after2|after1')
|
537
|
+
get '/one/two/three/four/raise-in-before'
|
538
|
+
expect(last_response.body).to eq('after2|after1')
|
539
|
+
get '/one/two/three/four/raise-in-handler'
|
540
|
+
expect(last_response.body).to eq('after2|after1')
|
541
|
+
end
|
542
|
+
end
|
306
543
|
end
|
307
544
|
|
308
545
|
context 'an app that has capturing routes' do
|
@@ -378,6 +615,18 @@ module Regal
|
|
378
615
|
end
|
379
616
|
end
|
380
617
|
|
618
|
+
SimpleApp1 = App.create do
|
619
|
+
get do
|
620
|
+
'simple1'
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
SimpleApp2 = App.create do
|
625
|
+
get do
|
626
|
+
'simple2'
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
381
630
|
let :app do
|
382
631
|
App.new do
|
383
632
|
route 'i' do
|
@@ -388,8 +637,26 @@ module Regal
|
|
388
637
|
end
|
389
638
|
|
390
639
|
route 'oh' do
|
640
|
+
get do
|
641
|
+
'oh'
|
642
|
+
end
|
643
|
+
|
391
644
|
mount HelloApp
|
392
645
|
end
|
646
|
+
|
647
|
+
route 'shadows' do
|
648
|
+
get do
|
649
|
+
'simple0'
|
650
|
+
end
|
651
|
+
|
652
|
+
mount SimpleApp1
|
653
|
+
mount SimpleApp2
|
654
|
+
|
655
|
+
route 'sub' do
|
656
|
+
mount SimpleApp1
|
657
|
+
mount SimpleApp2
|
658
|
+
end
|
659
|
+
end
|
393
660
|
end
|
394
661
|
end
|
395
662
|
|
@@ -416,42 +683,162 @@ module Regal
|
|
416
683
|
expect(last_response.status).to eq(200)
|
417
684
|
expect(last_response.body).to eq('hello')
|
418
685
|
end
|
686
|
+
|
687
|
+
context 'when the mounting and the mounted apps have handlers that match a request' do
|
688
|
+
it 'calls the mounting app\'s handler' do
|
689
|
+
get '/shadows'
|
690
|
+
expect(last_response.status).to eq(200)
|
691
|
+
expect(last_response.body).to eq('simple0')
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
context 'when the mounted apps have handlers that match a request' do
|
696
|
+
it 'calls the last mounted app\'s handler', pending: true do
|
697
|
+
get '/shadows/sub'
|
698
|
+
expect(last_response.status).to eq(200)
|
699
|
+
expect(last_response.body).to eq('simple2')
|
700
|
+
end
|
701
|
+
end
|
702
|
+
end
|
703
|
+
|
704
|
+
context 'an app that groups routes together in scopes' do
|
705
|
+
let :app do
|
706
|
+
App.new do
|
707
|
+
route 'scoped' do
|
708
|
+
before do |_, response|
|
709
|
+
response.headers['CommonBefore'] = 'yes'
|
710
|
+
end
|
711
|
+
|
712
|
+
after do |_, response|
|
713
|
+
response.headers['CommonAfter'] = 'yes'
|
714
|
+
end
|
715
|
+
|
716
|
+
scope do
|
717
|
+
before do |_, response|
|
718
|
+
response.headers['BeforeScope'] = '1'
|
719
|
+
end
|
720
|
+
|
721
|
+
after do |_, response|
|
722
|
+
response.headers['AfterScope'] = '1'
|
723
|
+
end
|
724
|
+
|
725
|
+
get do
|
726
|
+
'scope1-level1'
|
727
|
+
end
|
728
|
+
|
729
|
+
route '1' do
|
730
|
+
get do
|
731
|
+
'scope1-level2'
|
732
|
+
end
|
733
|
+
end
|
734
|
+
end
|
735
|
+
|
736
|
+
scope do
|
737
|
+
before do |_, response|
|
738
|
+
response.headers['BeforeScope'] = '2'
|
739
|
+
end
|
740
|
+
|
741
|
+
after do |_, response|
|
742
|
+
response.headers['AfterScope'] = '2'
|
743
|
+
end
|
744
|
+
|
745
|
+
get do
|
746
|
+
'scope2-level1'
|
747
|
+
end
|
748
|
+
|
749
|
+
scope do
|
750
|
+
before do |_, response|
|
751
|
+
response.headers['BeforeSubScope'] = '2'
|
752
|
+
end
|
753
|
+
|
754
|
+
route '2' do
|
755
|
+
get do
|
756
|
+
'scope2-level2'
|
757
|
+
end
|
758
|
+
end
|
759
|
+
end
|
760
|
+
end
|
761
|
+
end
|
762
|
+
end
|
763
|
+
end
|
764
|
+
|
765
|
+
it 'routes a request' do
|
766
|
+
get '/scoped/1'
|
767
|
+
expect(last_response.status).to eq(200)
|
768
|
+
expect(last_response.body).to eq('scope1-level2')
|
769
|
+
get '/scoped/2'
|
770
|
+
expect(last_response.status).to eq(200)
|
771
|
+
expect(last_response.body).to eq('scope2-level2')
|
772
|
+
end
|
773
|
+
|
774
|
+
it 'picks the handler from the last scope when there are multiple candidates', pending: true do
|
775
|
+
get '/scoped'
|
776
|
+
expect(last_response.status).to eq(200)
|
777
|
+
expect(last_response.body).to eq('scope2-level1')
|
778
|
+
end
|
779
|
+
|
780
|
+
it 'calls the route\'s parent scope\'s before blocks only' do
|
781
|
+
get '/scoped/1'
|
782
|
+
expect(last_response.status).to eq(200)
|
783
|
+
expect(last_response.headers).to include('BeforeScope' => '1')
|
784
|
+
get '/scoped/2'
|
785
|
+
expect(last_response.status).to eq(200)
|
786
|
+
expect(last_response.headers).to include('BeforeScope' => '2', 'BeforeSubScope' => '2')
|
787
|
+
end
|
788
|
+
|
789
|
+
it 'calls the route\'s parent scope\'s after blocks only' do
|
790
|
+
get '/scoped/1'
|
791
|
+
expect(last_response.status).to eq(200)
|
792
|
+
expect(last_response.headers).to include('AfterScope' => '1')
|
793
|
+
get '/scoped/2'
|
794
|
+
expect(last_response.status).to eq(200)
|
795
|
+
expect(last_response.headers).to include('AfterScope' => '2')
|
796
|
+
end
|
797
|
+
|
798
|
+
it 'calls the common before and after blocks' do
|
799
|
+
get '/scoped/1'
|
800
|
+
expect(last_response.status).to eq(200)
|
801
|
+
expect(last_response.headers).to include('CommonBefore' => 'yes', 'CommonAfter' => 'yes')
|
802
|
+
get '/scoped/2'
|
803
|
+
expect(last_response.status).to eq(200)
|
804
|
+
expect(last_response.headers).to include('CommonBefore' => 'yes', 'CommonAfter' => 'yes')
|
805
|
+
end
|
419
806
|
end
|
420
807
|
|
421
808
|
context 'an app that supports all HTTP methods' do
|
422
809
|
let :app do
|
423
810
|
App.new do
|
424
811
|
get do |request|
|
425
|
-
request.
|
812
|
+
request.env['REQUEST_METHOD']
|
426
813
|
end
|
427
814
|
|
428
815
|
head do |request|
|
429
|
-
request.
|
816
|
+
request.env['REQUEST_METHOD']
|
430
817
|
end
|
431
818
|
|
432
819
|
options do |request|
|
433
|
-
request.
|
820
|
+
request.env['REQUEST_METHOD']
|
434
821
|
end
|
435
822
|
|
436
823
|
delete do |request|
|
437
|
-
request.
|
824
|
+
request.env['REQUEST_METHOD']
|
438
825
|
end
|
439
826
|
|
440
827
|
post do |request|
|
441
|
-
request.
|
828
|
+
request.env['REQUEST_METHOD']
|
442
829
|
end
|
443
830
|
|
444
831
|
put do |request|
|
445
|
-
request.
|
832
|
+
request.env['REQUEST_METHOD']
|
446
833
|
end
|
447
834
|
|
448
835
|
patch do |request|
|
449
|
-
request.
|
836
|
+
request.env['REQUEST_METHOD']
|
450
837
|
end
|
451
838
|
|
452
839
|
route 'anything' do
|
453
840
|
any do |request|
|
454
|
-
request.
|
841
|
+
request.env['REQUEST_METHOD']
|
455
842
|
end
|
456
843
|
end
|
457
844
|
end
|
@@ -522,6 +909,10 @@ module Regal
|
|
522
909
|
'top_level_helper'
|
523
910
|
end
|
524
911
|
|
912
|
+
rescue_from StandardError do |_, _, response|
|
913
|
+
response.body = top_level_helper
|
914
|
+
end
|
915
|
+
|
525
916
|
route 'one' do
|
526
917
|
def first_level_helper
|
527
918
|
'first_level_helper'
|
@@ -549,6 +940,12 @@ module Regal
|
|
549
940
|
end
|
550
941
|
end
|
551
942
|
end
|
943
|
+
|
944
|
+
route 'boom' do
|
945
|
+
get do
|
946
|
+
raise 'Bork'
|
947
|
+
end
|
948
|
+
end
|
552
949
|
end
|
553
950
|
end
|
554
951
|
|
@@ -575,164 +972,80 @@ module Regal
|
|
575
972
|
expect(last_response.status).to eq(200)
|
576
973
|
expect(last_response.body).to include('after:top_level_helper,first_level_helper,second_level_helper')
|
577
974
|
end
|
975
|
+
|
976
|
+
it 'can use the helper methods in rescue blocks' do
|
977
|
+
get '/boom'
|
978
|
+
expect(last_response.status).to eq(200)
|
979
|
+
expect(last_response.body).to include('top_level_helper')
|
980
|
+
end
|
578
981
|
end
|
579
982
|
|
580
983
|
context 'an app that receives configuration when created' do
|
581
984
|
let :app do
|
582
|
-
App.new(
|
583
|
-
|
584
|
-
|
985
|
+
App.new(fuzzinator: fuzzinator, blip_count: 3, counter: 0, str: '') do
|
986
|
+
route 'blip' do
|
987
|
+
get do |request|
|
988
|
+
"blip\n" * request.attributes[:blip_count]
|
989
|
+
end
|
585
990
|
end
|
586
991
|
|
587
|
-
|
588
|
-
|
992
|
+
route 'fuzz' do
|
993
|
+
get do |request|
|
994
|
+
request.attributes[:fuzzinator].fuzz(request.parameters['s'])
|
995
|
+
end
|
589
996
|
end
|
590
997
|
|
591
|
-
route '
|
592
|
-
|
593
|
-
|
594
|
-
@thing2 = thing2
|
998
|
+
route 'increment' do
|
999
|
+
before do |request|
|
1000
|
+
request.attributes[:counter] += 1
|
595
1001
|
end
|
596
1002
|
|
597
|
-
get do
|
598
|
-
[
|
1003
|
+
get do |request|
|
1004
|
+
request.attributes[:counter].to_s
|
599
1005
|
end
|
600
1006
|
end
|
601
1007
|
|
602
|
-
route '
|
603
|
-
|
604
|
-
|
605
|
-
@thing2 = thing2
|
606
|
-
end
|
607
|
-
|
608
|
-
setup do |_, thing_two|
|
609
|
-
@thing_two = thing_two
|
1008
|
+
route 'append' do
|
1009
|
+
before do |request|
|
1010
|
+
request.attributes[:str] << '1'
|
610
1011
|
end
|
611
1012
|
|
612
|
-
get do
|
613
|
-
[
|
1013
|
+
get do |request|
|
1014
|
+
request.attributes[:str].to_s
|
614
1015
|
end
|
615
1016
|
end
|
616
1017
|
end
|
617
1018
|
end
|
618
1019
|
|
619
|
-
let :
|
620
|
-
double(:
|
1020
|
+
let :fuzzinator do
|
1021
|
+
double(:fuzzinator)
|
621
1022
|
end
|
622
1023
|
|
623
|
-
|
624
|
-
|
1024
|
+
before do
|
1025
|
+
allow(fuzzinator).to receive(:fuzz) { |s| s.split('').join('z') }
|
625
1026
|
end
|
626
1027
|
|
627
|
-
it '
|
628
|
-
get '/'
|
629
|
-
expect(last_response.status).to eq(200)
|
630
|
-
expect(last_response.body).to eq('this_thing,that_other_thing')
|
631
|
-
end
|
632
|
-
|
633
|
-
it 'calls the setup methods of all routes' do
|
634
|
-
get '/one'
|
1028
|
+
it 'can access the hash given to .new through the request attributes hash' do
|
1029
|
+
get '/blip'
|
635
1030
|
expect(last_response.status).to eq(200)
|
636
|
-
expect(last_response.body).to eq(
|
637
|
-
|
638
|
-
|
639
|
-
it 'calls all setup methods' do
|
640
|
-
get '/two'
|
1031
|
+
expect(last_response.body).to eq("blip\nblip\nblip\n")
|
1032
|
+
get '/fuzz?s=badaboom'
|
641
1033
|
expect(last_response.status).to eq(200)
|
642
|
-
expect(last_response.body).to eq('
|
643
|
-
end
|
644
|
-
end
|
645
|
-
|
646
|
-
context 'an app that uses Rack middleware' do
|
647
|
-
class Reverser
|
648
|
-
def initialize(app)
|
649
|
-
@app = app
|
650
|
-
end
|
651
|
-
|
652
|
-
def call(env)
|
653
|
-
response = @app.call(env)
|
654
|
-
body = response[2][0]
|
655
|
-
body && body.reverse!
|
656
|
-
response
|
657
|
-
end
|
658
|
-
end
|
659
|
-
|
660
|
-
class Uppercaser
|
661
|
-
def initialize(app)
|
662
|
-
@app = app
|
663
|
-
end
|
664
|
-
|
665
|
-
def call(env)
|
666
|
-
response = @app.call(env)
|
667
|
-
body = response[2][0]
|
668
|
-
body && body.upcase!
|
669
|
-
response
|
670
|
-
end
|
671
|
-
end
|
672
|
-
|
673
|
-
class Mutator
|
674
|
-
def initialize(app, &block)
|
675
|
-
@app = app
|
676
|
-
@block = block
|
677
|
-
end
|
678
|
-
|
679
|
-
def call(env)
|
680
|
-
@app.call(@block.call(env))
|
681
|
-
end
|
1034
|
+
expect(last_response.body).to eq('bzazdzazbzozozm')
|
682
1035
|
end
|
683
1036
|
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
'lorem ipsum'
|
690
|
-
end
|
691
|
-
|
692
|
-
route 'more' do
|
693
|
-
use Uppercaser
|
694
|
-
|
695
|
-
get do
|
696
|
-
'dolor sit'
|
697
|
-
end
|
698
|
-
end
|
699
|
-
|
700
|
-
route 'hello' do
|
701
|
-
use Rack::Runtime, 'Regal'
|
702
|
-
use Mutator do |env|
|
703
|
-
env['app.greeting'] = 'Bonjour'
|
704
|
-
env
|
705
|
-
end
|
706
|
-
|
707
|
-
get do |request|
|
708
|
-
request.env['app.greeting'] + ', ' + request.parameters['name']
|
709
|
-
end
|
710
|
-
end
|
711
|
-
end
|
712
|
-
end
|
713
|
-
|
714
|
-
it 'calls the middleware when processing the request' do
|
715
|
-
get '/'
|
716
|
-
expect(last_response.status).to eq(200)
|
717
|
-
expect(last_response.body).to eq('muspi merol')
|
718
|
-
end
|
719
|
-
|
720
|
-
it 'calls the middleware of all routes' do
|
721
|
-
get '/more'
|
722
|
-
expect(last_response.status).to eq(200)
|
723
|
-
expect(last_response.body).to eq('TIS ROLOD')
|
724
|
-
end
|
725
|
-
|
726
|
-
it 'passes arguments when instantiating the middleware' do
|
727
|
-
get '/hello?name=Eve'
|
728
|
-
expect(last_response.status).to eq(200)
|
729
|
-
expect(last_response.headers).to have_key('X-Runtime-Regal')
|
1037
|
+
it 'gives each request its own copy of the attributes' do
|
1038
|
+
get '/increment'
|
1039
|
+
get '/increment'
|
1040
|
+
get '/increment'
|
1041
|
+
expect(last_response.body).to eq('1')
|
730
1042
|
end
|
731
1043
|
|
732
|
-
it '
|
733
|
-
get '/
|
734
|
-
|
735
|
-
|
1044
|
+
it 'does not make a deep copy of the attributes' do
|
1045
|
+
get '/append'
|
1046
|
+
get '/append'
|
1047
|
+
get '/append'
|
1048
|
+
expect(last_response.body).to eq('111')
|
736
1049
|
end
|
737
1050
|
end
|
738
1051
|
|
@@ -761,6 +1074,11 @@ module Regal
|
|
761
1074
|
'I will not be used'
|
762
1075
|
end
|
763
1076
|
end
|
1077
|
+
|
1078
|
+
route 'nil-body' do
|
1079
|
+
get do
|
1080
|
+
end
|
1081
|
+
end
|
764
1082
|
end
|
765
1083
|
end
|
766
1084
|
|
@@ -778,6 +1096,11 @@ module Regal
|
|
778
1096
|
get '/no-body'
|
779
1097
|
expect(last_response.body).to be_empty
|
780
1098
|
end
|
1099
|
+
|
1100
|
+
it 'assumes that a nil body is no body' do
|
1101
|
+
get '/nil-body'
|
1102
|
+
expect(last_response.body).to be_empty
|
1103
|
+
end
|
781
1104
|
end
|
782
1105
|
|
783
1106
|
context 'an app that responds with no-body response codes' do
|
@@ -791,6 +1114,17 @@ module Regal
|
|
791
1114
|
end
|
792
1115
|
end
|
793
1116
|
end
|
1117
|
+
|
1118
|
+
route 'with-after' do
|
1119
|
+
get do |_, response|
|
1120
|
+
response.status = 204
|
1121
|
+
'this will not be returned'
|
1122
|
+
end
|
1123
|
+
|
1124
|
+
after do |_, response|
|
1125
|
+
response.body = 'this will not be returned either'
|
1126
|
+
end
|
1127
|
+
end
|
794
1128
|
end
|
795
1129
|
end
|
796
1130
|
|
@@ -801,6 +1135,12 @@ module Regal
|
|
801
1135
|
expect(last_response.body).to be_empty
|
802
1136
|
end
|
803
1137
|
end
|
1138
|
+
|
1139
|
+
it 'ignores response bodies set by after blocks' do
|
1140
|
+
get '/with-after'
|
1141
|
+
expect(last_response.status).to eq(204)
|
1142
|
+
expect(last_response.body).to be_empty
|
1143
|
+
end
|
804
1144
|
end
|
805
1145
|
|
806
1146
|
context 'an app that raises exceptions' do
|
@@ -808,6 +1148,45 @@ module Regal
|
|
808
1148
|
class AppError < StandardError; end
|
809
1149
|
class SpecificError < AppError; end
|
810
1150
|
|
1151
|
+
MountedRescuingApp = App.create do
|
1152
|
+
rescue_from SpecificError do |_, _, response|
|
1153
|
+
response.body = 'handled SpecificError in the mounted app'
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
route 'raise-specific-error' do
|
1157
|
+
get do
|
1158
|
+
raise SpecificError, 'Blam!'
|
1159
|
+
end
|
1160
|
+
end
|
1161
|
+
|
1162
|
+
route 'raise-app-error' do
|
1163
|
+
get do
|
1164
|
+
raise AppError, 'Kaboom!'
|
1165
|
+
end
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
route 'raise-and-and-handle-app-error' do
|
1169
|
+
rescue_from AppError do |_, _, response|
|
1170
|
+
response.body = 'handled AppError in the mounted app'
|
1171
|
+
end
|
1172
|
+
|
1173
|
+
get do
|
1174
|
+
raise AppError, 'Kaboom!'
|
1175
|
+
end
|
1176
|
+
end
|
1177
|
+
end
|
1178
|
+
|
1179
|
+
MountedNonRescuingApp = App.create do
|
1180
|
+
after do
|
1181
|
+
raise AppError, 'Kaboom!'
|
1182
|
+
end
|
1183
|
+
|
1184
|
+
route 'raise-from-after' do
|
1185
|
+
get do
|
1186
|
+
end
|
1187
|
+
end
|
1188
|
+
end
|
1189
|
+
|
811
1190
|
let :app do
|
812
1191
|
App.new do
|
813
1192
|
route 'unhandled' do
|
@@ -851,15 +1230,25 @@ module Regal
|
|
851
1230
|
end
|
852
1231
|
|
853
1232
|
route 'from-after' do
|
1233
|
+
after do |_, response|
|
1234
|
+
response.headers['NextAfterWasCalled'] = 'yes'
|
1235
|
+
end
|
1236
|
+
|
854
1237
|
after do
|
855
1238
|
raise SpecificError, 'Kazam!'
|
856
1239
|
end
|
857
1240
|
|
858
|
-
|
859
|
-
|
1241
|
+
get do
|
1242
|
+
end
|
1243
|
+
end
|
1244
|
+
|
1245
|
+
route 'from-rescue' do
|
1246
|
+
rescue_from SpecificError do |e|
|
1247
|
+
raise AppError, 'Badaboom!'
|
860
1248
|
end
|
861
1249
|
|
862
1250
|
get do
|
1251
|
+
raise SpecificError, 'Kaboom!'
|
863
1252
|
end
|
864
1253
|
end
|
865
1254
|
|
@@ -871,6 +1260,40 @@ module Regal
|
|
871
1260
|
raise SpecificError, 'Bam!'
|
872
1261
|
end
|
873
1262
|
end
|
1263
|
+
|
1264
|
+
route 'at-the-right-level' do
|
1265
|
+
rescue_from SpecificError do |_, _, response|
|
1266
|
+
response.headers['HandledAtLevel'] = '2'
|
1267
|
+
end
|
1268
|
+
|
1269
|
+
route 'level-3' do
|
1270
|
+
before do
|
1271
|
+
raise SpecificError, 'Badam!'
|
1272
|
+
end
|
1273
|
+
|
1274
|
+
rescue_from SpecificError do |_, _, response|
|
1275
|
+
response.headers['HandledAtLevel'] = '3'
|
1276
|
+
end
|
1277
|
+
|
1278
|
+
route 'level-4' do
|
1279
|
+
rescue_from SpecificError do |_, _, response|
|
1280
|
+
response.headers['HandledAtLevel'] = '4'
|
1281
|
+
end
|
1282
|
+
|
1283
|
+
get do
|
1284
|
+
end
|
1285
|
+
end
|
1286
|
+
end
|
1287
|
+
end
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
route 'with-mounted-app' do
|
1291
|
+
rescue_from AppError do |_, _, response|
|
1292
|
+
response.body = 'handled AppError in the mounting app'
|
1293
|
+
end
|
1294
|
+
|
1295
|
+
mount MountedRescuingApp
|
1296
|
+
mount MountedNonRescuingApp
|
874
1297
|
end
|
875
1298
|
end
|
876
1299
|
end
|
@@ -901,10 +1324,16 @@ module Regal
|
|
901
1324
|
expect(last_response.body).to eq('Bang!')
|
902
1325
|
end
|
903
1326
|
|
1327
|
+
it 'delegates them to matching error handlers at the same level, not below' do
|
1328
|
+
get '/handled/at-the-right-level/level-3/level-4'
|
1329
|
+
expect(last_response.headers).to include('HandledAtLevel' => '3')
|
1330
|
+
end
|
1331
|
+
|
904
1332
|
it 'calls after blocks when errors are handled' do
|
905
1333
|
get '/handled/from-before'
|
906
1334
|
expect(last_response.headers['WasAfterCalled']).to eq('yes')
|
907
1335
|
end
|
1336
|
+
|
908
1337
|
end
|
909
1338
|
|
910
1339
|
context 'from after blocks' do
|
@@ -919,6 +1348,37 @@ module Regal
|
|
919
1348
|
expect(last_response.headers['WasAfterCalled']).to eq('yes')
|
920
1349
|
end
|
921
1350
|
end
|
1351
|
+
|
1352
|
+
context 'from rescue blocks' do
|
1353
|
+
it 'delegates them to the next matching error handler' do
|
1354
|
+
get '/handled/from-rescue'
|
1355
|
+
expect(last_response.body).to eq('Badaboom!')
|
1356
|
+
end
|
1357
|
+
end
|
1358
|
+
|
1359
|
+
context 'from mounted apps' do
|
1360
|
+
it 'delegates them to matching error handlers in the mounting app' do
|
1361
|
+
get '/with-mounted-app/raise-app-error'
|
1362
|
+
expect(last_response.body).to eq('handled AppError in the mounting app')
|
1363
|
+
end
|
1364
|
+
|
1365
|
+
it 'delegates them to matching error handlers declared at the top of the mounted app' do
|
1366
|
+
get '/with-mounted-app/raise-specific-error'
|
1367
|
+
expect(last_response.body).to eq('handled SpecificError in the mounted app')
|
1368
|
+
end
|
1369
|
+
|
1370
|
+
it 'delegates them to matching error handlers declared in routes of the mounted app' do
|
1371
|
+
get '/with-mounted-app/raise-and-and-handle-app-error'
|
1372
|
+
expect(last_response.body).to eq('handled AppError in the mounted app')
|
1373
|
+
end
|
1374
|
+
|
1375
|
+
context 'in after blocks' do
|
1376
|
+
it 'delegates them to a matching error handler' do
|
1377
|
+
get '/with-mounted-app/raise-from-after'
|
1378
|
+
expect(last_response.body).to eq('handled AppError in the mounting app')
|
1379
|
+
end
|
1380
|
+
end
|
1381
|
+
end
|
922
1382
|
end
|
923
1383
|
end
|
924
1384
|
end
|