grape 1.3.0 → 1.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +119 -1
  3. data/LICENSE +1 -1
  4. data/README.md +123 -29
  5. data/UPGRADING.md +265 -39
  6. data/lib/grape/api/instance.rb +32 -31
  7. data/lib/grape/api.rb +5 -5
  8. data/lib/grape/content_types.rb +34 -0
  9. data/lib/grape/dsl/callbacks.rb +1 -1
  10. data/lib/grape/dsl/helpers.rb +2 -1
  11. data/lib/grape/dsl/inside_route.rb +77 -43
  12. data/lib/grape/dsl/parameters.rb +12 -8
  13. data/lib/grape/dsl/routing.rb +12 -11
  14. data/lib/grape/dsl/validations.rb +18 -1
  15. data/lib/grape/eager_load.rb +1 -1
  16. data/lib/grape/endpoint.rb +8 -6
  17. data/lib/grape/exceptions/base.rb +0 -4
  18. data/lib/grape/exceptions/validation.rb +1 -1
  19. data/lib/grape/exceptions/validation_errors.rb +12 -13
  20. data/lib/grape/http/headers.rb +26 -0
  21. data/lib/grape/middleware/auth/base.rb +3 -3
  22. data/lib/grape/middleware/base.rb +4 -5
  23. data/lib/grape/middleware/error.rb +11 -13
  24. data/lib/grape/middleware/formatter.rb +3 -3
  25. data/lib/grape/middleware/stack.rb +10 -2
  26. data/lib/grape/middleware/versioner/header.rb +4 -4
  27. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
  28. data/lib/grape/middleware/versioner/path.rb +1 -1
  29. data/lib/grape/namespace.rb +12 -2
  30. data/lib/grape/path.rb +13 -3
  31. data/lib/grape/request.rb +13 -8
  32. data/lib/grape/router/attribute_translator.rb +26 -5
  33. data/lib/grape/router/pattern.rb +17 -16
  34. data/lib/grape/router/route.rb +5 -24
  35. data/lib/grape/router.rb +26 -30
  36. data/lib/grape/{serve_file → serve_stream}/file_body.rb +1 -1
  37. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +1 -1
  38. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +8 -8
  39. data/lib/grape/util/base_inheritable.rb +15 -8
  40. data/lib/grape/util/cache.rb +20 -0
  41. data/lib/grape/util/lazy_object.rb +43 -0
  42. data/lib/grape/util/lazy_value.rb +1 -0
  43. data/lib/grape/util/reverse_stackable_values.rb +2 -0
  44. data/lib/grape/util/stackable_values.rb +7 -20
  45. data/lib/grape/validations/attributes_iterator.rb +8 -0
  46. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  47. data/lib/grape/validations/params_scope.rb +10 -8
  48. data/lib/grape/validations/single_attribute_iterator.rb +1 -1
  49. data/lib/grape/validations/types/array_coercer.rb +14 -5
  50. data/lib/grape/validations/types/build_coercer.rb +5 -8
  51. data/lib/grape/validations/types/custom_type_coercer.rb +16 -2
  52. data/lib/grape/validations/types/dry_type_coercer.rb +36 -1
  53. data/lib/grape/validations/types/file.rb +15 -12
  54. data/lib/grape/validations/types/invalid_value.rb +24 -0
  55. data/lib/grape/validations/types/json.rb +40 -36
  56. data/lib/grape/validations/types/primitive_coercer.rb +15 -6
  57. data/lib/grape/validations/types/set_coercer.rb +6 -4
  58. data/lib/grape/validations/types/variant_collection_coercer.rb +1 -1
  59. data/lib/grape/validations/types.rb +7 -9
  60. data/lib/grape/validations/validator_factory.rb +1 -1
  61. data/lib/grape/validations/validators/as.rb +1 -1
  62. data/lib/grape/validations/validators/base.rb +8 -8
  63. data/lib/grape/validations/validators/coerce.rb +11 -15
  64. data/lib/grape/validations/validators/default.rb +3 -5
  65. data/lib/grape/validations/validators/exactly_one_of.rb +4 -2
  66. data/lib/grape/validations/validators/except_values.rb +1 -1
  67. data/lib/grape/validations/validators/multiple_params_base.rb +2 -1
  68. data/lib/grape/validations/validators/regexp.rb +1 -1
  69. data/lib/grape/validations/validators/values.rb +1 -1
  70. data/lib/grape/version.rb +1 -1
  71. data/lib/grape.rb +5 -5
  72. data/spec/grape/api/instance_spec.rb +50 -0
  73. data/spec/grape/api_remount_spec.rb +9 -4
  74. data/spec/grape/api_spec.rb +82 -6
  75. data/spec/grape/dsl/inside_route_spec.rb +182 -33
  76. data/spec/grape/endpoint/declared_spec.rb +601 -0
  77. data/spec/grape/endpoint_spec.rb +0 -521
  78. data/spec/grape/entity_spec.rb +7 -1
  79. data/spec/grape/exceptions/validation_errors_spec.rb +2 -2
  80. data/spec/grape/integration/rack_sendfile_spec.rb +12 -8
  81. data/spec/grape/middleware/auth/strategies_spec.rb +1 -1
  82. data/spec/grape/middleware/error_spec.rb +1 -1
  83. data/spec/grape/middleware/formatter_spec.rb +3 -3
  84. data/spec/grape/middleware/stack_spec.rb +10 -0
  85. data/spec/grape/path_spec.rb +4 -4
  86. data/spec/grape/request_spec.rb +1 -1
  87. data/spec/grape/validations/instance_behaivour_spec.rb +1 -1
  88. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +13 -3
  89. data/spec/grape/validations/params_scope_spec.rb +26 -0
  90. data/spec/grape/validations/single_attribute_iterator_spec.rb +17 -6
  91. data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
  92. data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
  93. data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
  94. data/spec/grape/validations/types_spec.rb +1 -1
  95. data/spec/grape/validations/validators/coerce_spec.rb +366 -86
  96. data/spec/grape/validations/validators/default_spec.rb +170 -0
  97. data/spec/grape/validations/validators/exactly_one_of_spec.rb +12 -12
  98. data/spec/grape/validations/validators/except_values_spec.rb +1 -0
  99. data/spec/grape/validations/validators/values_spec.rb +1 -1
  100. data/spec/grape/validations_spec.rb +298 -30
  101. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  102. data/spec/shared/versioning_examples.rb +20 -20
  103. data/spec/spec_helper.rb +3 -10
  104. data/spec/support/chunks.rb +14 -0
  105. data/spec/support/eager_load.rb +19 -0
  106. data/spec/support/versioned_helpers.rb +4 -6
  107. metadata +27 -10
  108. data/lib/grape/util/content_types.rb +0 -28
@@ -280,527 +280,6 @@ describe Grape::Endpoint do
280
280
  end
281
281
  end
282
282
 
283
- describe '#declared' do
284
- before do
285
- subject.format :json
286
- subject.params do
287
- requires :first
288
- optional :second
289
- optional :third, default: 'third-default'
290
- optional :nested, type: Hash do
291
- optional :fourth
292
- optional :fifth
293
- optional :nested_two, type: Hash do
294
- optional :sixth
295
- optional :nested_three, type: Hash do
296
- optional :seventh
297
- end
298
- end
299
- end
300
- optional :nested_arr, type: Array do
301
- optional :eighth
302
- end
303
- end
304
- end
305
-
306
- context 'when params are not built with default class' do
307
- it 'returns an object that corresponds with the params class - hash with indifferent access' do
308
- subject.params do
309
- build_with Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder
310
- end
311
- subject.get '/declared' do
312
- d = declared(params, include_missing: true)
313
- { declared_class: d.class.to_s }
314
- end
315
-
316
- get '/declared?first=present'
317
- expect(JSON.parse(last_response.body)['declared_class']).to eq('ActiveSupport::HashWithIndifferentAccess')
318
- end
319
-
320
- it 'returns an object that corresponds with the params class - hashie mash' do
321
- subject.params do
322
- build_with Grape::Extensions::Hashie::Mash::ParamBuilder
323
- end
324
- subject.get '/declared' do
325
- d = declared(params, include_missing: true)
326
- { declared_class: d.class.to_s }
327
- end
328
-
329
- get '/declared?first=present'
330
- expect(JSON.parse(last_response.body)['declared_class']).to eq('Hashie::Mash')
331
- end
332
-
333
- it 'returns an object that corresponds with the params class - hash' do
334
- subject.params do
335
- build_with Grape::Extensions::Hash::ParamBuilder
336
- end
337
- subject.get '/declared' do
338
- d = declared(params, include_missing: true)
339
- { declared_class: d.class.to_s }
340
- end
341
-
342
- get '/declared?first=present'
343
- expect(JSON.parse(last_response.body)['declared_class']).to eq('Hash')
344
- end
345
- end
346
-
347
- it 'should show nil for nested params if include_missing is true' do
348
- subject.get '/declared' do
349
- declared(params, include_missing: true)
350
- end
351
-
352
- get '/declared?first=present'
353
- expect(last_response.status).to eq(200)
354
- expect(JSON.parse(last_response.body)['nested']['fourth']).to be_nil
355
- end
356
-
357
- it 'does not work in a before filter' do
358
- subject.before do
359
- declared(params)
360
- end
361
- subject.get('/declared') { declared(params) }
362
-
363
- expect { get('/declared') }.to raise_error(
364
- Grape::DSL::InsideRoute::MethodNotYetAvailable
365
- )
366
- end
367
-
368
- it 'has as many keys as there are declared params' do
369
- subject.get '/declared' do
370
- declared(params)
371
- end
372
- get '/declared?first=present'
373
- expect(last_response.status).to eq(200)
374
- expect(JSON.parse(last_response.body).keys.size).to eq(5)
375
- end
376
-
377
- it 'has a optional param with default value all the time' do
378
- subject.get '/declared' do
379
- declared(params)
380
- end
381
- get '/declared?first=one'
382
- expect(last_response.status).to eq(200)
383
- expect(JSON.parse(last_response.body)['third']).to eql('third-default')
384
- end
385
-
386
- it 'builds nested params' do
387
- subject.get '/declared' do
388
- declared(params)
389
- end
390
-
391
- get '/declared?first=present&nested[fourth]=1'
392
- expect(last_response.status).to eq(200)
393
- expect(JSON.parse(last_response.body)['nested'].keys.size).to eq 3
394
- end
395
-
396
- it 'builds nested params when given array' do
397
- subject.get '/dummy' do
398
- end
399
- subject.params do
400
- requires :first
401
- optional :second
402
- optional :third, default: 'third-default'
403
- optional :nested, type: Array do
404
- optional :fourth
405
- end
406
- end
407
- subject.get '/declared' do
408
- declared(params)
409
- end
410
-
411
- get '/declared?first=present&nested[][fourth]=1&nested[][fourth]=2'
412
- expect(last_response.status).to eq(200)
413
- expect(JSON.parse(last_response.body)['nested'].size).to eq 2
414
- end
415
-
416
- context 'sets nested objects when the param is missing' do
417
- it 'to be a hash when include_missing is true' do
418
- subject.get '/declared' do
419
- declared(params, include_missing: true)
420
- end
421
-
422
- get '/declared?first=present'
423
- expect(last_response.status).to eq(200)
424
- expect(JSON.parse(last_response.body)['nested']).to be_a(Hash)
425
- end
426
-
427
- it 'to be an array when include_missing is true' do
428
- subject.get '/declared' do
429
- declared(params, include_missing: true)
430
- end
431
-
432
- get '/declared?first=present'
433
- expect(last_response.status).to eq(200)
434
- expect(JSON.parse(last_response.body)['nested_arr']).to be_a(Array)
435
- end
436
-
437
- it 'to be nil when include_missing is false' do
438
- subject.get '/declared' do
439
- declared(params, include_missing: false)
440
- end
441
-
442
- get '/declared?first=present'
443
- expect(last_response.status).to eq(200)
444
- expect(JSON.parse(last_response.body)['nested']).to be_nil
445
- end
446
- end
447
-
448
- it 'filters out any additional params that are given' do
449
- subject.get '/declared' do
450
- declared(params)
451
- end
452
- get '/declared?first=one&other=two'
453
- expect(last_response.status).to eq(200)
454
- expect(JSON.parse(last_response.body).key?(:other)).to eq false
455
- end
456
-
457
- it 'stringifies if that option is passed' do
458
- subject.get '/declared' do
459
- declared(params, stringify: true)
460
- end
461
-
462
- get '/declared?first=one&other=two'
463
- expect(last_response.status).to eq(200)
464
- expect(JSON.parse(last_response.body)['first']).to eq 'one'
465
- end
466
-
467
- it 'does not include missing attributes if that option is passed' do
468
- subject.get '/declared' do
469
- error! 'expected nil', 400 if declared(params, include_missing: false).key?(:second)
470
- ''
471
- end
472
-
473
- get '/declared?first=one&other=two'
474
- expect(last_response.status).to eq(200)
475
- end
476
-
477
- it 'does not include renamed missing attributes if that option is passed' do
478
- subject.params do
479
- optional :renamed_original, as: :renamed
480
- end
481
- subject.get '/declared' do
482
- error! 'expected nil', 400 if declared(params, include_missing: false).key?(:renamed)
483
- ''
484
- end
485
-
486
- get '/declared?first=one&other=two'
487
- expect(last_response.status).to eq(200)
488
- end
489
-
490
- it 'includes attributes with value that evaluates to false' do
491
- subject.params do
492
- requires :first
493
- optional :boolean
494
- end
495
-
496
- subject.post '/declared' do
497
- error!('expected false', 400) if declared(params, include_missing: false)[:boolean] != false
498
- ''
499
- end
500
-
501
- post '/declared', ::Grape::Json.dump(first: 'one', boolean: false), 'CONTENT_TYPE' => 'application/json'
502
- expect(last_response.status).to eq(201)
503
- end
504
-
505
- it 'includes attributes with value that evaluates to nil' do
506
- subject.params do
507
- requires :first
508
- optional :second
509
- end
510
-
511
- subject.post '/declared' do
512
- error!('expected nil', 400) unless declared(params, include_missing: false)[:second].nil?
513
- ''
514
- end
515
-
516
- post '/declared', ::Grape::Json.dump(first: 'one', second: nil), 'CONTENT_TYPE' => 'application/json'
517
- expect(last_response.status).to eq(201)
518
- end
519
-
520
- it 'includes missing attributes with defaults when there are nested hashes' do
521
- subject.get '/dummy' do
522
- end
523
-
524
- subject.params do
525
- requires :first
526
- optional :second
527
- optional :third, default: nil
528
- optional :nested, type: Hash do
529
- optional :fourth, default: nil
530
- optional :fifth, default: nil
531
- requires :nested_nested, type: Hash do
532
- optional :sixth, default: 'sixth-default'
533
- optional :seven, default: nil
534
- end
535
- end
536
- end
537
-
538
- subject.get '/declared' do
539
- declared(params, include_missing: false)
540
- end
541
-
542
- get '/declared?first=present&nested[fourth]=&nested[nested_nested][sixth]=sixth'
543
- json = JSON.parse(last_response.body)
544
- expect(last_response.status).to eq(200)
545
- expect(json['first']).to eq 'present'
546
- expect(json['nested'].keys).to eq %w[fourth fifth nested_nested]
547
- expect(json['nested']['fourth']).to eq ''
548
- expect(json['nested']['nested_nested'].keys).to eq %w[sixth seven]
549
- expect(json['nested']['nested_nested']['sixth']).to eq 'sixth'
550
- end
551
-
552
- it 'does not include missing attributes when there are nested hashes' do
553
- subject.get '/dummy' do
554
- end
555
-
556
- subject.params do
557
- requires :first
558
- optional :second
559
- optional :third
560
- optional :nested, type: Hash do
561
- optional :fourth
562
- optional :fifth
563
- end
564
- end
565
-
566
- subject.get '/declared' do
567
- declared(params, include_missing: false)
568
- end
569
-
570
- get '/declared?first=present&nested[fourth]=4'
571
- json = JSON.parse(last_response.body)
572
- expect(last_response.status).to eq(200)
573
- expect(json['first']).to eq 'present'
574
- expect(json['nested'].keys).to eq %w[fourth]
575
- expect(json['nested']['fourth']).to eq '4'
576
- end
577
- end
578
-
579
- describe '#declared; call from child namespace' do
580
- before do
581
- subject.format :json
582
- subject.namespace :parent do
583
- params do
584
- requires :parent_name, type: String
585
- end
586
-
587
- namespace ':parent_name' do
588
- params do
589
- requires :child_name, type: String
590
- requires :child_age, type: Integer
591
- end
592
-
593
- namespace ':child_name' do
594
- params do
595
- requires :grandchild_name, type: String
596
- end
597
-
598
- get ':grandchild_name' do
599
- {
600
- 'params' => params,
601
- 'without_parent_namespaces' => declared(params, include_parent_namespaces: false),
602
- 'with_parent_namespaces' => declared(params, include_parent_namespaces: true)
603
- }
604
- end
605
- end
606
- end
607
- end
608
-
609
- get '/parent/foo/bar/baz', child_age: 5, extra: 'hello'
610
- end
611
-
612
- let(:parsed_response) { JSON.parse(last_response.body, symbolize_names: true) }
613
-
614
- it { expect(last_response.status).to eq 200 }
615
-
616
- context 'with include_parent_namespaces: false' do
617
- it 'returns declared parameters only from current namespace' do
618
- expect(parsed_response[:without_parent_namespaces]).to eq(
619
- grandchild_name: 'baz'
620
- )
621
- end
622
- end
623
-
624
- context 'with include_parent_namespaces: true' do
625
- it 'returns declared parameters from every parent namespace' do
626
- expect(parsed_response[:with_parent_namespaces]).to eq(
627
- parent_name: 'foo',
628
- child_name: 'bar',
629
- grandchild_name: 'baz',
630
- child_age: 5
631
- )
632
- end
633
- end
634
-
635
- context 'without declaration' do
636
- it 'returns all requested parameters' do
637
- expect(parsed_response[:params]).to eq(
638
- parent_name: 'foo',
639
- child_name: 'bar',
640
- grandchild_name: 'baz',
641
- child_age: 5,
642
- extra: 'hello'
643
- )
644
- end
645
- end
646
- end
647
-
648
- describe '#declared; from a nested mounted endpoint' do
649
- before do
650
- doubly_mounted = Class.new(Grape::API)
651
- doubly_mounted.namespace :more do
652
- params do
653
- requires :y, type: Integer
654
- end
655
- route_param :y do
656
- get do
657
- {
658
- params: params,
659
- declared_params: declared(params)
660
- }
661
- end
662
- end
663
- end
664
-
665
- mounted = Class.new(Grape::API)
666
- mounted.namespace :another do
667
- params do
668
- requires :mount_space, type: Integer
669
- end
670
- route_param :mount_space do
671
- mount doubly_mounted
672
- end
673
- end
674
-
675
- subject.format :json
676
- subject.namespace :something do
677
- params do
678
- requires :id, type: Integer
679
- end
680
- resource ':id' do
681
- mount mounted
682
- end
683
- end
684
- end
685
-
686
- it 'can access parent attributes' do
687
- get '/something/123/another/456/more/789'
688
- expect(last_response.status).to eq 200
689
- json = JSON.parse(last_response.body, symbolize_names: true)
690
-
691
- # test all three levels of params
692
- expect(json[:declared_params][:y]).to eq 789
693
- expect(json[:declared_params][:mount_space]).to eq 456
694
- expect(json[:declared_params][:id]).to eq 123
695
- end
696
- end
697
-
698
- describe '#declared; mixed nesting' do
699
- before do
700
- subject.format :json
701
- subject.resource :users do
702
- route_param :id, type: Integer, desc: 'ID desc' do
703
- # Adding this causes route_setting(:declared_params) to be nil for the
704
- # get block in namespace 'foo' below
705
- get do
706
- end
707
-
708
- namespace 'foo' do
709
- get do
710
- {
711
- params: params,
712
- declared_params: declared(params),
713
- declared_params_no_parent: declared(params, include_parent_namespaces: false)
714
- }
715
- end
716
- end
717
- end
718
- end
719
- end
720
-
721
- it 'can access parent route_param' do
722
- get '/users/123/foo', bar: 'bar'
723
- expect(last_response.status).to eq 200
724
- json = JSON.parse(last_response.body, symbolize_names: true)
725
-
726
- expect(json[:declared_params][:id]).to eq 123
727
- expect(json[:declared_params_no_parent][:id]).to eq nil
728
- end
729
- end
730
-
731
- describe '#declared; with multiple route_param' do
732
- before do
733
- mounted = Class.new(Grape::API)
734
- mounted.namespace :albums do
735
- get do
736
- declared(params)
737
- end
738
- end
739
-
740
- subject.format :json
741
- subject.namespace :artists do
742
- route_param :id, type: Integer do
743
- get do
744
- declared(params)
745
- end
746
-
747
- params do
748
- requires :filter, type: String
749
- end
750
- get :some_route do
751
- declared(params)
752
- end
753
- end
754
-
755
- route_param :artist_id, type: Integer do
756
- namespace :compositions do
757
- get do
758
- declared(params)
759
- end
760
- end
761
- end
762
-
763
- route_param :compositor_id, type: Integer do
764
- mount mounted
765
- end
766
- end
767
- end
768
-
769
- it 'return only :id without :artist_id' do
770
- get '/artists/1'
771
- json = JSON.parse(last_response.body, symbolize_names: true)
772
-
773
- expect(json.key?(:id)).to be_truthy
774
- expect(json.key?(:artist_id)).not_to be_truthy
775
- end
776
-
777
- it 'return only :artist_id without :id' do
778
- get '/artists/1/compositions'
779
- json = JSON.parse(last_response.body, symbolize_names: true)
780
-
781
- expect(json.key?(:artist_id)).to be_truthy
782
- expect(json.key?(:id)).not_to be_truthy
783
- end
784
-
785
- it 'return :filter and :id parameters in declared for second enpoint inside route_param' do
786
- get '/artists/1/some_route', filter: 'some_filter'
787
- json = JSON.parse(last_response.body, symbolize_names: true)
788
-
789
- expect(json.key?(:filter)).to be_truthy
790
- expect(json.key?(:id)).to be_truthy
791
- expect(json.key?(:artist_id)).not_to be_truthy
792
- end
793
-
794
- it 'return :compositor_id for mounter in route_param' do
795
- get '/artists/1/albums'
796
- json = JSON.parse(last_response.body, symbolize_names: true)
797
-
798
- expect(json.key?(:compositor_id)).to be_truthy
799
- expect(json.key?(:id)).not_to be_truthy
800
- expect(json.key?(:artist_id)).not_to be_truthy
801
- end
802
- end
803
-
804
283
  describe '#params' do
805
284
  it 'is available to the caller' do
806
285
  subject.get('/hey') do
@@ -116,7 +116,7 @@ describe Grape::Entity do
116
116
  expect(last_response.body).to eq('Auto-detect!')
117
117
  end
118
118
 
119
- it 'does not run autodetection for Entity when explicitely provided' do
119
+ it 'does not run autodetection for Entity when explicitly provided' do
120
120
  entity = Class.new(Grape::Entity)
121
121
  some_array = []
122
122
 
@@ -181,6 +181,7 @@ describe Grape::Entity do
181
181
  subject.get '/example' do
182
182
  c = Class.new do
183
183
  attr_reader :id
184
+
184
185
  def initialize(id)
185
186
  @id = id
186
187
  end
@@ -202,6 +203,7 @@ describe Grape::Entity do
202
203
  subject.get '/examples' do
203
204
  c = Class.new do
204
205
  attr_reader :id
206
+
205
207
  def initialize(id)
206
208
  @id = id
207
209
  end
@@ -226,6 +228,7 @@ describe Grape::Entity do
226
228
  subject.get '/example' do
227
229
  c = Class.new do
228
230
  attr_reader :name
231
+
229
232
  def initialize(args)
230
233
  @name = args[:name] || 'no name set'
231
234
  end
@@ -255,6 +258,7 @@ XML
255
258
  subject.get '/example' do
256
259
  c = Class.new do
257
260
  attr_reader :name
261
+
258
262
  def initialize(args)
259
263
  @name = args[:name] || 'no name set'
260
264
  end
@@ -284,6 +288,7 @@ XML
284
288
  subject.get '/example' do
285
289
  c = Class.new do
286
290
  attr_reader :name
291
+
287
292
  def initialize(args)
288
293
  @name = args[:name] || 'no name set'
289
294
  end
@@ -302,6 +307,7 @@ XML
302
307
  it 'present with multiple entities using optional symbol' do
303
308
  user = Class.new do
304
309
  attr_reader :name
310
+
305
311
  def initialize(args)
306
312
  @name = args[:name] || 'no name set'
307
313
  end
@@ -81,8 +81,8 @@ describe Grape::Exceptions::ValidationErrors do
81
81
  expect(last_response.status).to eq(400)
82
82
  expect(JSON.parse(last_response.body)).to eq(
83
83
  [
84
- 'params' => %w[beer wine juice],
85
- 'messages' => ['are missing, exactly one parameter must be provided']
84
+ 'params' => %w[beer wine],
85
+ 'messages' => ['are mutually exclusive']
86
86
  ]
87
87
  )
88
88
  end
@@ -4,12 +4,16 @@ require 'spec_helper'
4
4
 
5
5
  describe Rack::Sendfile do
6
6
  subject do
7
- send_file = file_streamer
7
+ content_object = file_object
8
8
  app = Class.new(Grape::API) do
9
9
  use Rack::Sendfile
10
10
  format :json
11
11
  get do
12
- file send_file
12
+ if content_object.is_a?(String)
13
+ sendfile content_object
14
+ else
15
+ stream content_object
16
+ end
13
17
  end
14
18
  end
15
19
 
@@ -22,9 +26,9 @@ describe Rack::Sendfile do
22
26
  app.call(env)
23
27
  end
24
28
 
25
- context do
26
- let(:file_streamer) do
27
- double(:file_streamer, to_path: '/accel/mapping/some/path')
29
+ context 'when calling sendfile' do
30
+ let(:file_object) do
31
+ '/accel/mapping/some/path'
28
32
  end
29
33
 
30
34
  it 'contains Sendfile headers' do
@@ -33,9 +37,9 @@ describe Rack::Sendfile do
33
37
  end
34
38
  end
35
39
 
36
- context do
37
- let(:file_streamer) do
38
- double(:file_streamer)
40
+ context 'when streaming non file content' do
41
+ let(:file_object) do
42
+ double(:file_object, each: nil)
39
43
  end
40
44
 
41
45
  it 'not contains Sendfile headers' do
@@ -36,7 +36,7 @@ describe Grape::Middleware::Auth::Strategies do
36
36
  RSpec::Matchers.define :be_challenge do
37
37
  match do |actual_response|
38
38
  actual_response.status == 401 &&
39
- actual_response['WWW-Authenticate'] =~ /^Digest / &&
39
+ actual_response['WWW-Authenticate'].start_with?('Digest ') &&
40
40
  actual_response.body.empty?
41
41
  end
42
42
  end
@@ -30,7 +30,7 @@ describe Grape::Middleware::Error do
30
30
  opts = options
31
31
  Rack::Builder.app do
32
32
  use Spec::Support::EndpointFaker
33
- use Grape::Middleware::Error, opts
33
+ use Grape::Middleware::Error, **opts
34
34
  run ErrorSpec::ErrApp
35
35
  end
36
36
  end
@@ -380,7 +380,7 @@ describe Grape::Middleware::Formatter do
380
380
 
381
381
  context 'send file' do
382
382
  let(:file) { double(File) }
383
- let(:file_body) { Grape::ServeFile::FileResponse.new(file) }
383
+ let(:file_body) { Grape::ServeStream::StreamResponse.new(file) }
384
384
  let(:app) { ->(_env) { [200, {}, file_body] } }
385
385
 
386
386
  it 'returns a file response' do
@@ -402,10 +402,10 @@ describe Grape::Middleware::Formatter do
402
402
  let(:app) { ->(_env) { [200, {}, ['']] } }
403
403
  before do
404
404
  Grape::Formatter.register :invalid, InvalidFormatter
405
- Grape::ContentTypes::CONTENT_TYPES[:invalid] = 'application/x-invalid'
405
+ Grape::ContentTypes.register :invalid, 'application/x-invalid'
406
406
  end
407
407
  after do
408
- Grape::ContentTypes::CONTENT_TYPES.delete(:invalid)
408
+ Grape::ContentTypes.default_elements.delete(:invalid)
409
409
  Grape::Formatter.default_elements.delete(:invalid)
410
410
  end
411
411
 
@@ -8,6 +8,7 @@ describe Grape::Middleware::Stack do
8
8
  class BarMiddleware; end
9
9
  class BlockMiddleware
10
10
  attr_reader :block
11
+
11
12
  def initialize(&block)
12
13
  @block = block
13
14
  end
@@ -111,6 +112,15 @@ describe Grape::Middleware::Stack do
111
112
  expect(subject[1]).to eq(StackSpec::BlockMiddleware)
112
113
  expect(subject[2]).to eq(StackSpec::BarMiddleware)
113
114
  end
115
+
116
+ context 'middleware spec with proc declaration exists' do
117
+ let(:middleware_spec_with_proc) { [:use, StackSpec::FooMiddleware, proc] }
118
+
119
+ it 'properly forwards spec arguments' do
120
+ expect(subject).to receive(:use).with(StackSpec::FooMiddleware)
121
+ subject.merge_with([middleware_spec_with_proc])
122
+ end
123
+ end
114
124
  end
115
125
 
116
126
  describe '#build' do