grape 0.16.2 → 0.17.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.

Potentially problematic release.


This version of grape might be problematic. Click here for more details.

Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +4 -0
  3. data/CHANGELOG.md +54 -27
  4. data/Dangerfile +80 -0
  5. data/Gemfile +23 -0
  6. data/Gemfile.lock +61 -27
  7. data/README.md +135 -7
  8. data/Rakefile +34 -30
  9. data/UPGRADING.md +21 -0
  10. data/gemfiles/rack_1.5.2.gemfile +21 -0
  11. data/gemfiles/rails_3.gemfile +22 -1
  12. data/gemfiles/rails_4.gemfile +21 -0
  13. data/gemfiles/rails_5.gemfile +34 -0
  14. data/grape.gemspec +0 -14
  15. data/lib/grape.rb +2 -0
  16. data/lib/grape/api.rb +9 -2
  17. data/lib/grape/dsl/headers.rb +1 -1
  18. data/lib/grape/dsl/inside_route.rb +15 -17
  19. data/lib/grape/dsl/middleware.rb +15 -1
  20. data/lib/grape/dsl/parameters.rb +16 -14
  21. data/lib/grape/dsl/request_response.rb +24 -20
  22. data/lib/grape/dsl/routing.rb +11 -10
  23. data/lib/grape/dsl/settings.rb +16 -0
  24. data/lib/grape/endpoint.rb +77 -60
  25. data/lib/grape/exceptions/validation.rb +5 -2
  26. data/lib/grape/exceptions/validation_array_errors.rb +11 -0
  27. data/lib/grape/formatter/xml.rb +1 -1
  28. data/lib/grape/middleware/error.rb +34 -25
  29. data/lib/grape/middleware/formatter.rb +9 -9
  30. data/lib/grape/middleware/stack.rb +110 -0
  31. data/lib/grape/middleware/versioner.rb +1 -1
  32. data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
  33. data/lib/grape/middleware/versioner/header.rb +3 -3
  34. data/lib/grape/path.rb +10 -2
  35. data/lib/grape/request.rb +1 -1
  36. data/lib/grape/router.rb +10 -19
  37. data/lib/grape/router/pattern.rb +2 -2
  38. data/lib/grape/router/route.rb +3 -3
  39. data/lib/grape/util/content_types.rb +1 -1
  40. data/lib/grape/util/inheritable_setting.rb +7 -2
  41. data/lib/grape/util/reverse_stackable_values.rb +45 -0
  42. data/lib/grape/util/stackable_values.rb +10 -11
  43. data/lib/grape/validations/attributes_iterator.rb +32 -7
  44. data/lib/grape/validations/params_scope.rb +33 -21
  45. data/lib/grape/validations/types.rb +4 -4
  46. data/lib/grape/validations/types/build_coercer.rb +9 -1
  47. data/lib/grape/validations/validators/all_or_none.rb +2 -2
  48. data/lib/grape/validations/validators/allow_blank.rb +10 -11
  49. data/lib/grape/validations/validators/at_least_one_of.rb +1 -1
  50. data/lib/grape/validations/validators/base.rb +16 -6
  51. data/lib/grape/validations/validators/coerce.rb +3 -6
  52. data/lib/grape/validations/validators/default.rb +26 -1
  53. data/lib/grape/validations/validators/exactly_one_of.rb +1 -1
  54. data/lib/grape/validations/validators/mutual_exclusion.rb +1 -1
  55. data/lib/grape/validations/validators/presence.rb +1 -1
  56. data/lib/grape/validations/validators/regexp.rb +1 -1
  57. data/lib/grape/validations/validators/values.rb +1 -1
  58. data/lib/grape/version.rb +1 -1
  59. data/spec/grape/api/custom_validations_spec.rb +3 -3
  60. data/spec/grape/api/parameters_modification_spec.rb +41 -0
  61. data/spec/grape/api_spec.rb +335 -108
  62. data/spec/grape/dsl/logger_spec.rb +1 -1
  63. data/spec/grape/dsl/middleware_spec.rb +25 -5
  64. data/spec/grape/dsl/request_response_spec.rb +20 -6
  65. data/spec/grape/dsl/validations_spec.rb +1 -1
  66. data/spec/grape/endpoint_spec.rb +166 -23
  67. data/spec/grape/entity_spec.rb +0 -2
  68. data/spec/grape/exceptions/body_parse_errors_spec.rb +37 -0
  69. data/spec/grape/exceptions/validation_errors_spec.rb +5 -5
  70. data/spec/grape/exceptions/validation_spec.rb +10 -0
  71. data/spec/grape/integration/global_namespace_function_spec.rb +1 -1
  72. data/spec/grape/integration/rack_spec.rb +1 -1
  73. data/spec/grape/middleware/base_spec.rb +1 -1
  74. data/spec/grape/middleware/exception_spec.rb +2 -2
  75. data/spec/grape/middleware/formatter_spec.rb +4 -4
  76. data/spec/grape/middleware/stack_spec.rb +123 -0
  77. data/spec/grape/middleware/versioner/header_spec.rb +6 -6
  78. data/spec/grape/request_spec.rb +22 -22
  79. data/spec/grape/util/inheritable_setting_spec.rb +23 -0
  80. data/spec/grape/util/reverse_stackable_values_spec.rb +131 -0
  81. data/spec/grape/validations/params_scope_spec.rb +88 -1
  82. data/spec/grape/validations/validators/allow_blank_spec.rb +5 -0
  83. data/spec/grape/validations/validators/coerce_spec.rb +5 -5
  84. data/spec/grape/validations/validators/default_spec.rb +44 -0
  85. data/spec/grape/validations/validators/values_spec.rb +1 -1
  86. data/spec/grape/validations_spec.rb +36 -17
  87. data/spec/spec_helper.rb +1 -8
  88. data/spec/support/versioned_helpers.rb +3 -3
  89. metadata +13 -188
  90. data/gemfiles/rails_3.gemfile.lock +0 -225
  91. data/pkg/grape-0.16.1.gem +0 -0
  92. data/pkg/patch.diff +0 -24
  93. data/tmp/Gemfile.lock +0 -63
@@ -18,7 +18,7 @@ module Grape
18
18
  end
19
19
 
20
20
  it 'returns a logger' do
21
- expect(subject.logger logger).to eq logger
21
+ expect(subject.logger(logger)).to eq logger
22
22
  end
23
23
  end
24
24
  end
@@ -7,23 +7,43 @@ module Grape
7
7
  include Grape::DSL::Middleware
8
8
  end
9
9
  end
10
+
10
11
  describe Middleware do
11
12
  subject { Class.new(MiddlewareSpec::Dummy) }
12
13
  let(:proc) { ->() {} }
14
+ let(:foo_middleware) { Class.new }
15
+ let(:bar_middleware) { Class.new }
13
16
 
14
17
  describe '.use' do
15
- it 'adds a middleware' do
16
- expect(subject).to receive(:namespace_stackable).with(:middleware, [:my_middleware, :arg1, proc])
18
+ it 'adds a middleware with the right operation' do
19
+ expect(subject).to receive(:namespace_stackable).with(:middleware, [:use, foo_middleware, :arg1, proc])
20
+
21
+ subject.use foo_middleware, :arg1, &proc
22
+ end
23
+ end
24
+
25
+ describe '.insert_before' do
26
+ it 'adds a middleware with the right operation' do
27
+ expect(subject).to receive(:namespace_stackable).with(:middleware, [:insert_before, foo_middleware, :arg1, proc])
28
+
29
+ subject.insert_before foo_middleware, :arg1, &proc
30
+ end
31
+ end
32
+
33
+ describe '.insert_after' do
34
+ it 'adds a middleware with the right operation' do
35
+ expect(subject).to receive(:namespace_stackable).with(:middleware, [:insert_after, foo_middleware, :arg1, proc])
17
36
 
18
- subject.use :my_middleware, :arg1, &proc
37
+ subject.insert_after foo_middleware, :arg1, &proc
19
38
  end
20
39
  end
21
40
 
22
41
  describe '.middleware' do
23
42
  it 'returns the middleware stack' do
24
- subject.use :my_middleware, :arg1, &proc
43
+ subject.use foo_middleware, :arg1, &proc
44
+ subject.insert_before bar_middleware, :arg1, :arg2
25
45
 
26
- expect(subject.middleware).to eq [[:my_middleware, :arg1, proc]]
46
+ expect(subject.middleware).to eq [[:use, foo_middleware, :arg1, proc], [:insert_before, bar_middleware, :arg1, :arg2]]
27
47
  end
28
48
  end
29
49
  end
@@ -143,37 +143,51 @@ module Grape
143
143
  end
144
144
  end
145
145
 
146
+ describe ':grape_exceptions' do
147
+ it 'sets rescue all to true' do
148
+ expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true)
149
+ expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true)
150
+ subject.rescue_from :grape_exceptions
151
+ end
152
+
153
+ it 'sets rescue_grape_exceptions to true' do
154
+ expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true)
155
+ expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true)
156
+ subject.rescue_from :grape_exceptions
157
+ end
158
+ end
159
+
146
160
  describe 'list of exceptions is passed' do
147
161
  it 'sets hash of exceptions as rescue handlers' do
148
- expect(subject).to receive(:namespace_stackable).with(:rescue_handlers, StandardError => nil)
162
+ expect(subject).to receive(:namespace_reverse_stackable).with(:rescue_handlers, StandardError => nil)
149
163
  expect(subject).to receive(:namespace_stackable).with(:rescue_options, {})
150
164
  subject.rescue_from StandardError
151
165
  end
152
166
 
153
167
  it 'rescues only base handlers if rescue_subclasses: false option is passed' do
154
- expect(subject).to receive(:namespace_stackable).with(:base_only_rescue_handlers, StandardError => nil)
168
+ expect(subject).to receive(:namespace_reverse_stackable).with(:base_only_rescue_handlers, StandardError => nil)
155
169
  expect(subject).to receive(:namespace_stackable).with(:rescue_options, rescue_subclasses: false)
156
170
  subject.rescue_from StandardError, rescue_subclasses: false
157
171
  end
158
172
 
159
173
  it 'sets given proc as rescue handler for each key in hash' do
160
174
  rescue_handler_proc = proc {}
161
- expect(subject).to receive(:namespace_stackable).with(:rescue_handlers, StandardError => rescue_handler_proc)
175
+ expect(subject).to receive(:namespace_reverse_stackable).with(:rescue_handlers, StandardError => rescue_handler_proc)
162
176
  expect(subject).to receive(:namespace_stackable).with(:rescue_options, {})
163
177
  subject.rescue_from StandardError, rescue_handler_proc
164
178
  end
165
179
 
166
180
  it 'sets given block as rescue handler for each key in hash' do
167
181
  rescue_handler_proc = proc {}
168
- expect(subject).to receive(:namespace_stackable).with(:rescue_handlers, StandardError => rescue_handler_proc)
182
+ expect(subject).to receive(:namespace_reverse_stackable).with(:rescue_handlers, StandardError => rescue_handler_proc)
169
183
  expect(subject).to receive(:namespace_stackable).with(:rescue_options, {})
170
184
  subject.rescue_from StandardError, &rescue_handler_proc
171
185
  end
172
186
 
173
187
  it 'sets a rescue handler declared through :with option for each key in hash' do
174
188
  with_block = -> { 'hello' }
175
- expect(subject).to receive(:namespace_stackable).with(:rescue_handlers, StandardError => an_instance_of(Proc))
176
- expect(subject).to receive(:namespace_stackable).with(:rescue_options, with: with_block)
189
+ expect(subject).to receive(:namespace_reverse_stackable).with(:rescue_handlers, StandardError => an_instance_of(Proc))
190
+ expect(subject).to receive(:namespace_stackable).with(:rescue_options, {})
177
191
  subject.rescue_from StandardError, with: with_block
178
192
  end
179
193
  end
@@ -51,7 +51,7 @@ module Grape
51
51
  end
52
52
 
53
53
  it 'evaluates block' do
54
- expect { subject.params { fail 'foo' } }.to raise_error RuntimeError, 'foo'
54
+ expect { subject.params { raise 'foo' } }.to raise_error RuntimeError, 'foo'
55
55
  end
56
56
  end
57
57
 
@@ -466,49 +466,192 @@ describe Grape::Endpoint do
466
466
  describe '#declared; call from child namespace' do
467
467
  before do
468
468
  subject.format :json
469
- subject.namespace :something do
469
+ subject.namespace :parent do
470
470
  params do
471
- requires :id, type: Integer
471
+ requires :parent_name, type: String
472
472
  end
473
- resource ':id' do
473
+
474
+ namespace ':parent_name' do
474
475
  params do
475
- requires :foo
476
- optional :bar
476
+ requires :child_name, type: String
477
+ requires :child_age, type: Integer
477
478
  end
479
+
480
+ namespace ':child_name' do
481
+ params do
482
+ requires :grandchild_name, type: String
483
+ end
484
+
485
+ get ':grandchild_name' do
486
+ {
487
+ 'params' => params,
488
+ 'without_parent_namespaces' => declared(params, include_parent_namespaces: false),
489
+ 'with_parent_namespaces' => declared(params, include_parent_namespaces: true)
490
+ }
491
+ end
492
+ end
493
+ end
494
+ end
495
+
496
+ get '/parent/foo/bar/baz', child_age: 5, extra: 'hello'
497
+ end
498
+
499
+ let(:parsed_response) { JSON.parse(last_response.body, symbolize_names: true) }
500
+
501
+ it { expect(last_response.status).to eq 200 }
502
+
503
+ context 'with include_parent_namespaces: false' do
504
+ it 'returns declared parameters only from current namespace' do
505
+ expect(parsed_response[:without_parent_namespaces]).to eq(
506
+ grandchild_name: 'baz'
507
+ )
508
+ end
509
+ end
510
+
511
+ context 'with include_parent_namespaces: true' do
512
+ it 'returns declared parameters from every parent namespace' do
513
+ expect(parsed_response[:with_parent_namespaces]).to eq(
514
+ parent_name: 'foo',
515
+ child_name: 'bar',
516
+ grandchild_name: 'baz',
517
+ child_age: 5
518
+ )
519
+ end
520
+ end
521
+
522
+ context 'without declaration' do
523
+ it 'returns all requested parameters' do
524
+ expect(parsed_response[:params]).to eq(
525
+ parent_name: 'foo',
526
+ child_name: 'bar',
527
+ grandchild_name: 'baz',
528
+ child_age: 5,
529
+ extra: 'hello'
530
+ )
531
+ end
532
+ end
533
+ end
534
+
535
+ describe '#declared; from a nested mounted endpoint' do
536
+ before do
537
+ doubly_mounted = Class.new(Grape::API)
538
+ doubly_mounted.namespace :more do
539
+ params do
540
+ requires :y, type: Integer
541
+ end
542
+ route_param :y do
478
543
  get do
479
544
  {
480
545
  params: params,
481
546
  declared_params: declared(params)
482
547
  }
483
548
  end
549
+ end
550
+ end
551
+
552
+ mounted = Class.new(Grape::API)
553
+ mounted.namespace :another do
554
+ params do
555
+ requires :mount_space, type: Integer
556
+ end
557
+ route_param :mount_space do
558
+ mount doubly_mounted
559
+ end
560
+ end
561
+
562
+ subject.format :json
563
+ subject.namespace :something do
564
+ params do
565
+ requires :id, type: Integer
566
+ end
567
+ resource ':id' do
568
+ mount mounted
569
+ end
570
+ end
571
+ end
572
+
573
+ it 'can access parent attributes' do
574
+ get '/something/123/another/456/more/789'
575
+ expect(last_response.status).to eq 200
576
+ json = JSON.parse(last_response.body, symbolize_names: true)
577
+
578
+ # test all three levels of params
579
+ expect(json[:declared_params][:y]).to eq 789
580
+ expect(json[:declared_params][:mount_space]).to eq 456
581
+ expect(json[:declared_params][:id]).to eq 123
582
+ end
583
+ end
584
+
585
+ describe '#declared; with multiple route_param' do
586
+ before do
587
+ mounted = Class.new(Grape::API)
588
+ mounted.namespace :albums do
589
+ get do
590
+ declared(params)
591
+ end
592
+ end
593
+
594
+ subject.format :json
595
+ subject.namespace :artists do
596
+ route_param :id, type: Integer do
597
+ get do
598
+ declared(params)
599
+ end
600
+
484
601
  params do
485
- requires :happy
486
- optional :days
602
+ requires :filter, type: String
487
603
  end
488
- get '/test' do
489
- {
490
- params: params,
491
- declared_params: declared(params, include_parent_namespaces: false)
492
- }
604
+ get :some_route do
605
+ declared(params)
606
+ end
607
+ end
608
+
609
+ route_param :artist_id, type: Integer do
610
+ namespace :compositions do
611
+ get do
612
+ declared(params)
613
+ end
493
614
  end
494
615
  end
616
+
617
+ route_param :compositor_id, type: Integer do
618
+ mount mounted
619
+ end
495
620
  end
496
621
  end
497
622
 
498
- it 'should include params defined in the parent namespace' do
499
- get '/something/123', foo: 'test', extra: 'hello'
500
- expect(last_response.status).to eq 200
623
+ it 'return only :id without :artist_id' do
624
+ get '/artists/1'
625
+ json = JSON.parse(last_response.body, symbolize_names: true)
626
+
627
+ expect(json.key?(:id)).to be_truthy
628
+ expect(json.key?(:artist_id)).not_to be_truthy
629
+ end
630
+
631
+ it 'return only :artist_id without :id' do
632
+ get '/artists/1/compositions'
501
633
  json = JSON.parse(last_response.body, symbolize_names: true)
502
- expect(json[:params][:id]).to eq 123
503
- expect(json[:declared_params].keys).to match_array [:foo, :bar, :id]
634
+
635
+ expect(json.key?(:artist_id)).to be_truthy
636
+ expect(json.key?(:id)).not_to be_truthy
504
637
  end
505
638
 
506
- it 'does not include params defined in the parent namespace with include_parent_namespaces: false' do
507
- get '/something/123/test', happy: 'test', extra: 'hello'
508
- expect(last_response.status).to eq 200
639
+ it 'return :filter and :id parameters in declared for second enpoint inside route_param' do
640
+ get '/artists/1/some_route', filter: 'some_filter'
509
641
  json = JSON.parse(last_response.body, symbolize_names: true)
510
- expect(json[:params][:id]).to eq 123
511
- expect(json[:declared_params].keys).to match_array [:happy, :days]
642
+
643
+ expect(json.key?(:filter)).to be_truthy
644
+ expect(json.key?(:id)).to be_truthy
645
+ expect(json.key?(:artist_id)).not_to be_truthy
646
+ end
647
+
648
+ it 'return :compositor_id for mounter in route_param' do
649
+ get '/artists/1/albums'
650
+ json = JSON.parse(last_response.body, symbolize_names: true)
651
+
652
+ expect(json.key?(:compositor_id)).to be_truthy
653
+ expect(json.key?(:id)).not_to be_truthy
654
+ expect(json.key?(:artist_id)).not_to be_truthy
512
655
  end
513
656
  end
514
657
 
@@ -577,7 +720,7 @@ describe Grape::Endpoint do
577
720
  params[:person_email]
578
721
  end
579
722
 
580
- namespace :inner, requirements: { number: /[0-9]/, person_email: /someone@(.*).com/ }do
723
+ namespace :inner, requirements: { number: /[0-9]/, person_email: /someone@(.*).com/ } do
581
724
  get '/:person_email/test/:number' do
582
725
  params[:person_email] << params[:number]
583
726
  end
@@ -266,8 +266,6 @@ XML
266
266
  end
267
267
 
268
268
  it 'presents with jsonp utilising Rack::JSONP' do
269
- require 'rack/contrib'
270
-
271
269
  # Include JSONP middleware
272
270
  subject.use Rack::JSONP
273
271
 
@@ -52,6 +52,43 @@ describe Grape::Exceptions::ValidationErrors do
52
52
  end
53
53
  end
54
54
 
55
+ context 'api with rescue_from :grape_exceptions handler' do
56
+ subject { Class.new(Grape::API) }
57
+ before do
58
+ subject.rescue_from :all do |_e|
59
+ rack_response 'message was processed', 400
60
+ end
61
+ subject.rescue_from :grape_exceptions
62
+
63
+ subject.params do
64
+ requires :beer
65
+ end
66
+ subject.post '/beer' do
67
+ 'beer received'
68
+ end
69
+ end
70
+
71
+ def app
72
+ subject
73
+ end
74
+
75
+ context 'with content_type json' do
76
+ it 'returns body parsing error message' do
77
+ post '/beer', 'test', 'CONTENT_TYPE' => 'application/json'
78
+ expect(last_response.status).to eq 400
79
+ expect(last_response.body).to include 'message body does not match declared format'
80
+ end
81
+ end
82
+
83
+ context 'with content_type xml' do
84
+ it 'returns body parsing error message' do
85
+ post '/beer', 'test', 'CONTENT_TYPE' => 'application/xml'
86
+ expect(last_response.status).to eq 400
87
+ expect(last_response.body).to include 'message body does not match declared format'
88
+ end
89
+ end
90
+ end
91
+
55
92
  context 'api without a rescue handler' do
56
93
  subject { Class.new(Grape::API) }
57
94
  before do
@@ -6,11 +6,11 @@ describe Grape::Exceptions::ValidationErrors do
6
6
  let(:validation_error) { OpenStruct.new(params: [validation_message]) }
7
7
 
8
8
  context 'initialize' do
9
- let(:headers) {
9
+ let(:headers) do
10
10
  {
11
11
  'A-Header-Key' => 'A-Header-Value'
12
12
  }
13
- }
13
+ end
14
14
 
15
15
  subject do
16
16
  described_class.new(errors: [validation_error], headers: headers)
@@ -69,9 +69,9 @@ describe Grape::Exceptions::ValidationErrors do
69
69
  get '/exactly_one_of', beer: 'string', wine: 'anotherstring'
70
70
  expect(last_response.status).to eq(400)
71
71
  expect(JSON.parse(last_response.body)).to eq([
72
- 'params' => %w(beer wine),
73
- 'messages' => ['are mutually exclusive']
74
- ])
72
+ 'params' => %w(beer wine),
73
+ 'messages' => ['are mutually exclusive']
74
+ ])
75
75
  end
76
76
  end
77
77
  end
@@ -4,4 +4,14 @@ describe Grape::Exceptions::Validation do
4
4
  it 'fails when params are missing' do
5
5
  expect { Grape::Exceptions::Validation.new(message: 'presence') }.to raise_error(RuntimeError, 'Params are missing:')
6
6
  end
7
+ context 'when message is a symbol' do
8
+ it 'stores message_key' do
9
+ expect(Grape::Exceptions::Validation.new(params: ['id'], message: :presence).message_key).to eq(:presence)
10
+ end
11
+ end
12
+ context 'when message is a String' do
13
+ it 'does not store the message_key' do
14
+ expect(Grape::Exceptions::Validation.new(params: ['id'], message: 'presence').message_key).to eq(nil)
15
+ end
16
+ end
7
17
  end
@@ -3,7 +3,7 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  def namespace
6
- fail
6
+ raise
7
7
  end
8
8
 
9
9
  describe Grape::API do