grape 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (87) hide show
  1. checksums.yaml +5 -5
  2. data/Appraisals +1 -1
  3. data/CHANGELOG.md +18 -0
  4. data/Dangerfile +1 -0
  5. data/Gemfile +9 -10
  6. data/Gemfile.lock +42 -40
  7. data/LICENSE +1 -1
  8. data/README.md +89 -40
  9. data/Rakefile +1 -46
  10. data/gemfiles/multi_json.gemfile +9 -10
  11. data/gemfiles/multi_xml.gemfile +9 -10
  12. data/gemfiles/rack_1.5.2.gemfile +9 -10
  13. data/gemfiles/rack_edge.gemfile +9 -10
  14. data/gemfiles/rails_3.gemfile +9 -10
  15. data/gemfiles/rails_4.gemfile +9 -10
  16. data/gemfiles/rails_5.gemfile +9 -10
  17. data/gemfiles/rails_edge.gemfile +9 -10
  18. data/grape.gemspec +3 -3
  19. data/lib/grape/api.rb +2 -2
  20. data/lib/grape/dsl/inside_route.rb +30 -10
  21. data/lib/grape/dsl/routing.rb +1 -1
  22. data/lib/grape/dsl/settings.rb +6 -6
  23. data/lib/grape/endpoint.rb +1 -1
  24. data/lib/grape/exceptions/incompatible_option_values.rb +0 -1
  25. data/lib/grape/exceptions/invalid_accept_header.rb +0 -1
  26. data/lib/grape/exceptions/invalid_formatter.rb +0 -1
  27. data/lib/grape/exceptions/invalid_message_body.rb +0 -1
  28. data/lib/grape/exceptions/invalid_version_header.rb +0 -1
  29. data/lib/grape/exceptions/invalid_versioner_option.rb +0 -1
  30. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +0 -1
  31. data/lib/grape/exceptions/method_not_allowed.rb +0 -1
  32. data/lib/grape/exceptions/missing_group_type.rb +0 -1
  33. data/lib/grape/exceptions/missing_mime_type.rb +0 -1
  34. data/lib/grape/exceptions/missing_option.rb +0 -1
  35. data/lib/grape/exceptions/missing_vendor_option.rb +0 -1
  36. data/lib/grape/exceptions/unknown_options.rb +0 -1
  37. data/lib/grape/exceptions/unknown_parameter.rb +0 -1
  38. data/lib/grape/exceptions/unknown_validator.rb +0 -1
  39. data/lib/grape/exceptions/unsupported_group_type.rb +0 -1
  40. data/lib/grape/namespace.rb +1 -1
  41. data/lib/grape/router.rb +2 -0
  42. data/lib/grape/router/pattern.rb +1 -1
  43. data/lib/grape/router/route.rb +14 -14
  44. data/lib/grape/validations/params_scope.rb +3 -4
  45. data/lib/grape/validations/types.rb +14 -1
  46. data/lib/grape/validations/types/build_coercer.rb +8 -0
  47. data/lib/grape/validations/types/custom_type_coercer.rb +1 -1
  48. data/lib/grape/validations/types/custom_type_collection_coercer.rb +71 -0
  49. data/lib/grape/validations/validators/allow_blank.rb +1 -1
  50. data/lib/grape/validations/validators/base.rb +1 -0
  51. data/lib/grape/validations/validators/coerce.rb +6 -0
  52. data/lib/grape/version.rb +1 -1
  53. data/pkg/grape-1.0.1.gem +0 -0
  54. data/spec/grape/api_spec.rb +22 -12
  55. data/spec/grape/dsl/inside_route_spec.rb +1 -1
  56. data/spec/grape/dsl/parameters_spec.rb +5 -5
  57. data/spec/grape/dsl/settings_spec.rb +2 -2
  58. data/spec/grape/endpoint_spec.rb +25 -12
  59. data/spec/grape/entity_spec.rb +1 -1
  60. data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -1
  61. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -1
  62. data/spec/grape/exceptions/missing_option_spec.rb +0 -1
  63. data/spec/grape/exceptions/unknown_options_spec.rb +1 -2
  64. data/spec/grape/exceptions/unknown_validator_spec.rb +0 -1
  65. data/spec/grape/exceptions/validation_errors_spec.rb +1 -1
  66. data/spec/grape/middleware/exception_spec.rb +36 -12
  67. data/spec/grape/middleware/formatter_spec.rb +1 -1
  68. data/spec/grape/middleware/versioner/header_spec.rb +1 -1
  69. data/spec/grape/middleware/versioner/param_spec.rb +1 -1
  70. data/spec/grape/middleware/versioner/path_spec.rb +1 -1
  71. data/spec/grape/path_spec.rb +3 -3
  72. data/spec/grape/util/inheritable_setting_spec.rb +2 -2
  73. data/spec/grape/util/reverse_stackable_values_spec.rb +13 -13
  74. data/spec/grape/util/stackable_values_spec.rb +13 -13
  75. data/spec/grape/util/strict_hash_configuration_spec.rb +1 -1
  76. data/spec/grape/validations/params_scope_spec.rb +19 -7
  77. data/spec/grape/validations/validators/all_or_none_spec.rb +1 -1
  78. data/spec/grape/validations/validators/at_least_one_of_spec.rb +1 -1
  79. data/spec/grape/validations/validators/coerce_spec.rb +94 -13
  80. data/spec/grape/validations/validators/default_spec.rb +40 -0
  81. data/spec/grape/validations/validators/exactly_one_of_spec.rb +1 -1
  82. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +1 -1
  83. data/spec/grape/validations/validators/values_spec.rb +3 -3
  84. data/spec/grape/validations_spec.rb +9 -9
  85. data/spec/shared/versioning_examples.rb +58 -0
  86. data/spec/support/content_type_helpers.rb +1 -1
  87. metadata +26 -24
@@ -9,7 +9,7 @@ module Grape
9
9
  it 'returns all key' do
10
10
  subject[:some_thing] = :foo_bar
11
11
  subject[:some_thing_else] = :foo_bar
12
- expect(subject.keys).to eq [:some_thing, :some_thing_else].sort
12
+ expect(subject.keys).to eq %i[some_thing some_thing_else].sort
13
13
  end
14
14
 
15
15
  it 'returns merged keys with parent' do
@@ -19,7 +19,7 @@ module Grape
19
19
  subject[:some_thing] = :foo_bar
20
20
  subject[:some_thing_more] = :foo_bar
21
21
 
22
- expect(subject.keys).to eq [:some_thing, :some_thing_else, :some_thing_more].sort
22
+ expect(subject.keys).to eq %i[some_thing some_thing_else some_thing_more].sort
23
23
  end
24
24
  end
25
25
 
@@ -52,7 +52,7 @@ module Grape
52
52
  it 'combines parent and actual values' do
53
53
  parent[:some_thing] = :foo
54
54
  subject[:some_thing] = :foo_bar
55
- expect(subject[:some_thing]).to eq [:foo, :foo_bar]
55
+ expect(subject[:some_thing]).to eq %i[foo foo_bar]
56
56
  end
57
57
 
58
58
  it 'parent values are not changed' do
@@ -71,27 +71,27 @@ module Grape
71
71
  it 'pushes further values' do
72
72
  subject[:some_thing] = :foo
73
73
  subject[:some_thing] = :bar
74
- expect(subject[:some_thing]).to eq [:foo, :bar]
74
+ expect(subject[:some_thing]).to eq %i[foo bar]
75
75
  end
76
76
 
77
77
  it 'can handle array values' do
78
78
  subject[:some_thing] = :foo
79
- subject[:some_thing] = [:bar, :more]
80
- expect(subject[:some_thing]).to eq [:foo, [:bar, :more]]
79
+ subject[:some_thing] = %i[bar more]
80
+ expect(subject[:some_thing]).to eq [:foo, %i[bar more]]
81
81
 
82
- parent[:some_thing_else] = [:foo, :bar]
83
- subject[:some_thing_else] = [:some, :bar, :foo]
82
+ parent[:some_thing_else] = %i[foo bar]
83
+ subject[:some_thing_else] = %i[some bar foo]
84
84
 
85
- expect(subject[:some_thing_else]).to eq [[:foo, :bar], [:some, :bar, :foo]]
85
+ expect(subject[:some_thing_else]).to eq [%i[foo bar], %i[some bar foo]]
86
86
  end
87
87
  end
88
88
 
89
89
  describe '#to_hash' do
90
90
  it 'returns a Hash representation' do
91
91
  parent[:some_thing] = :foo
92
- subject[:some_thing] = [:bar, :more]
92
+ subject[:some_thing] = %i[bar more]
93
93
  subject[:some_thing_more] = :foo_bar
94
- expect(subject.to_hash).to eq(some_thing: [:foo, [:bar, :more]], some_thing_more: [:foo_bar])
94
+ expect(subject.to_hash).to eq(some_thing: [:foo, %i[bar more]], some_thing_more: [:foo_bar])
95
95
  end
96
96
  end
97
97
 
@@ -103,11 +103,11 @@ module Grape
103
103
  grandchild = StackableValues.new child
104
104
 
105
105
  parent[:some_thing] = :foo
106
- child[:some_thing] = [:bar, :more]
106
+ child[:some_thing] = %i[bar more]
107
107
  grandchild[:some_thing] = :grand_foo_bar
108
108
  grandchild[:some_thing_more] = :foo_bar
109
109
 
110
- expect(grandchild.clone.to_hash).to eq(some_thing: [:foo, [:bar, :more], :grand_foo_bar], some_thing_more: [:foo_bar])
110
+ expect(grandchild.clone.to_hash).to eq(some_thing: [:foo, %i[bar more], :grand_foo_bar], some_thing_more: [:foo_bar])
111
111
  end
112
112
 
113
113
  context 'complex (i.e. not primitive) data types (ex. middleware, please see bug #930)' do
@@ -4,7 +4,7 @@ module Grape
4
4
  describe 'StrictHashConfiguration' do
5
5
  subject do
6
6
  Class.new do
7
- include Grape::Util::StrictHashConfiguration.module(:config1, :config2, config3: [:config4], config5: [config6: [:config7, :config8]])
7
+ include Grape::Util::StrictHashConfiguration.module(:config1, :config2, config3: [:config4], config5: [config6: %i[config7 config8]])
8
8
  end
9
9
  end
10
10
 
@@ -74,7 +74,7 @@ describe Grape::Validations::ParamsScope do
74
74
  end
75
75
 
76
76
  context 'setting description' do
77
- [:desc, :description].each do |description_type|
77
+ %i[desc description].each do |description_type|
78
78
  it "allows setting #{description_type}" do
79
79
  subject.params do
80
80
  requires :int, type: Integer, description_type => 'My very nice integer'
@@ -160,18 +160,18 @@ describe Grape::Validations::ParamsScope do
160
160
  context 'array without coerce type explicitly given' do
161
161
  it 'sets the type based on first element' do
162
162
  subject.params do
163
- requires :periods, type: Array, values: -> { %w(day month) }
163
+ requires :periods, type: Array, values: -> { %w[day month] }
164
164
  end
165
165
  subject.get('/required') { 'required works' }
166
166
 
167
- get '/required', periods: %w(day month)
167
+ get '/required', periods: %w[day month]
168
168
  expect(last_response.status).to eq(200)
169
169
  expect(last_response.body).to eq('required works')
170
170
  end
171
171
 
172
172
  it 'fails to call API without Array type' do
173
173
  subject.params do
174
- requires :periods, type: Array, values: -> { %w(day month) }
174
+ requires :periods, type: Array, values: -> { %w[day month] }
175
175
  end
176
176
  subject.get('/required') { 'required works' }
177
177
 
@@ -339,6 +339,18 @@ describe Grape::Validations::ParamsScope do
339
339
  get '/optional_type_array'
340
340
  end
341
341
 
342
+ it 'handles missing optional Array type' do
343
+ subject.params do
344
+ optional :a, type: Array do
345
+ requires :b
346
+ end
347
+ end
348
+ subject.get('/test') { declared(params).to_json }
349
+ get '/test'
350
+ expect(last_response.status).to eq(200)
351
+ expect(last_response.body).to eq('{"a":[]}')
352
+ end
353
+
342
354
  it 'errors with an unsupported type' do
343
355
  expect do
344
356
  subject.params do
@@ -456,7 +468,7 @@ describe Grape::Validations::ParamsScope do
456
468
 
457
469
  it 'does not validate nested requires when given is false' do
458
470
  subject.params do
459
- requires :a, type: String, allow_blank: false, values: %w(x y z)
471
+ requires :a, type: String, allow_blank: false, values: %w[x y z]
460
472
  given a: ->(val) { val == 'x' } do
461
473
  requires :inner1, type: Hash, allow_blank: false do
462
474
  requires :foo, type: Integer, allow_blank: false
@@ -574,8 +586,8 @@ describe Grape::Validations::ParamsScope do
574
586
 
575
587
  context 'when validations are dependent on a parameter with specific value' do
576
588
  # build test cases from all combinations of declarations and options
577
- a_decls = %i(optional requires)
578
- a_options = [{}, { values: %w(x y z) }]
589
+ a_decls = %i[optional requires]
590
+ a_options = [{}, { values: %w[x y z] }]
579
591
  b_options = [{}, { type: String }, { allow_blank: false }, { type: String, allow_blank: false }]
580
592
  combinations = a_decls.product(a_options, b_options)
581
593
  combinations.each_with_index do |combination, i|
@@ -11,7 +11,7 @@ describe Grape::Validations::AllOrNoneOfValidator do
11
11
  def required?; end
12
12
  end
13
13
  end
14
- let(:all_or_none_params) { [:beer, :wine, :grapefruit] }
14
+ let(:all_or_none_params) { %i[beer wine grapefruit] }
15
15
  let(:validator) { described_class.new(all_or_none_params, {}, false, scope.new) }
16
16
 
17
17
  context 'when all restricted params are present' do
@@ -11,7 +11,7 @@ describe Grape::Validations::AtLeastOneOfValidator do
11
11
  def required?; end
12
12
  end
13
13
  end
14
- let(:at_least_one_of_params) { [:beer, :wine, :grapefruit] }
14
+ let(:at_least_one_of_params) { %i[beer wine grapefruit] }
15
15
  let(:validator) { described_class.new(at_least_one_of_params, {}, false, scope.new) }
16
16
 
17
17
  context 'when all restricted params are present' do
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  require 'spec_helper'
3
2
 
4
3
  describe Grape::Validations::CoerceValidator do
@@ -136,11 +135,11 @@ describe Grape::Validations::CoerceValidator do
136
135
  'array int works'
137
136
  end
138
137
 
139
- get 'array', ids: %w(1 2 az)
138
+ get 'array', ids: %w[1 2 az]
140
139
  expect(last_response.status).to eq(400)
141
140
  expect(last_response.body).to eq('ids is invalid')
142
141
 
143
- get 'array', ids: %w(1 2 890)
142
+ get 'array', ids: %w[1 2 890]
144
143
  expect(last_response.status).to eq(200)
145
144
  expect(last_response.body).to eq('array int works')
146
145
  end
@@ -187,7 +186,7 @@ describe Grape::Validations::CoerceValidator do
187
186
  params[:arry][0].class
188
187
  end
189
188
 
190
- get '/array', arry: %w(1 2 3)
189
+ get '/array', arry: %w[1 2 3]
191
190
  expect(last_response.status).to eq(200)
192
191
  expect(last_response.body).to eq(integer_class_name)
193
192
  end
@@ -225,6 +224,55 @@ describe Grape::Validations::CoerceValidator do
225
224
  expect(last_response.status).to eq(200)
226
225
  expect(last_response.body).to eq('1')
227
226
  end
227
+
228
+ it 'Array of type implementing parse' do
229
+ subject.params do
230
+ requires :uri, type: Array[URI]
231
+ end
232
+ subject.get '/uri_array' do
233
+ params[:uri][0].class
234
+ end
235
+ get 'uri_array', uri: ['http://www.google.com']
236
+ expect(last_response.status).to eq(200)
237
+ expect(last_response.body).to eq('URI::HTTP')
238
+ end
239
+
240
+ it 'Set of type implementing parse' do
241
+ subject.params do
242
+ requires :uri, type: Set[URI]
243
+ end
244
+ subject.get '/uri_array' do
245
+ "#{params[:uri].class},#{params[:uri].first.class},#{params[:uri].size}"
246
+ end
247
+ get 'uri_array', uri: Array.new(2) { 'http://www.example.com' }
248
+ expect(last_response.status).to eq(200)
249
+ expect(last_response.body).to eq('Set,URI::HTTP,1')
250
+ end
251
+
252
+ it 'Array of class implementing parse and parsed?' do
253
+ class SecureURIOnly
254
+ def self.parse(value)
255
+ URI.parse(value)
256
+ end
257
+
258
+ def self.parsed?(value)
259
+ value.is_a? URI::HTTPS
260
+ end
261
+ end
262
+
263
+ subject.params do
264
+ requires :uri, type: Array[SecureURIOnly]
265
+ end
266
+ subject.get '/secure_uris' do
267
+ params[:uri].first.class
268
+ end
269
+ get 'secure_uris', uri: ['https://www.example.com']
270
+ expect(last_response.status).to eq(200)
271
+ expect(last_response.body).to eq('URI::HTTPS')
272
+ get 'secure_uris', uri: ['https://www.example.com', 'http://www.example.com']
273
+ expect(last_response.status).to eq(400)
274
+ expect(last_response.body).to eq('uri is invalid')
275
+ end
228
276
  end
229
277
 
230
278
  context 'Set' do
@@ -395,11 +443,11 @@ describe Grape::Validations::CoerceValidator do
395
443
 
396
444
  get '/ints', values: '1 2 3 4'
397
445
  expect(last_response.status).to eq(200)
398
- expect(JSON.parse(last_response.body)).to eq(%w(1 2 3 4))
446
+ expect(JSON.parse(last_response.body)).to eq(%w[1 2 3 4])
399
447
 
400
448
  get '/ints', values: 'a b c d'
401
449
  expect(last_response.status).to eq(200)
402
- expect(JSON.parse(last_response.body)).to eq(%w(0 0 0 0))
450
+ expect(JSON.parse(last_response.body)).to eq(%w[0 0 0 0])
403
451
  end
404
452
 
405
453
  it 'parses parameters with Array[Integer] type' do
@@ -419,6 +467,23 @@ describe Grape::Validations::CoerceValidator do
419
467
  expect(JSON.parse(last_response.body)).to eq([0, 0, 0, 0])
420
468
  end
421
469
 
470
+ it 'parses parameters even if type is valid' do
471
+ subject.params do
472
+ requires :values, type: Array, coerce_with: ->(array) { array.map { |val| val.to_i + 1 } }
473
+ end
474
+ subject.get '/ints' do
475
+ params[:values]
476
+ end
477
+
478
+ get '/ints', values: [1, 2, 3, 4]
479
+ expect(last_response.status).to eq(200)
480
+ expect(JSON.parse(last_response.body)).to eq([2, 3, 4, 5])
481
+
482
+ get '/ints', values: %w[a b c d]
483
+ expect(last_response.status).to eq(200)
484
+ expect(JSON.parse(last_response.body)).to eq([1, 1, 1, 1])
485
+ end
486
+
422
487
  it 'uses parse where available' do
423
488
  subject.params do
424
489
  requires :ints, type: Array, coerce_with: JSON do
@@ -477,7 +542,7 @@ describe Grape::Validations::CoerceValidator do
477
542
  end
478
543
 
479
544
  context 'first-class JSON' do
480
- it 'parses objects and arrays' do
545
+ it 'parses objects, hashes, and arrays' do
481
546
  subject.params do
482
547
  requires :splines, type: JSON do
483
548
  requires :x, type: Integer, values: [1, 2, 3]
@@ -499,17 +564,33 @@ describe Grape::Validations::CoerceValidator do
499
564
  expect(last_response.status).to eq(200)
500
565
  expect(last_response.body).to eq('woof')
501
566
 
567
+ get '/', splines: { x: 1, ints: [1, 2, 3], obj: { y: 'woof' } }
568
+ expect(last_response.status).to eq(200)
569
+ expect(last_response.body).to eq('woof')
570
+
502
571
  get '/', splines: '[{"x":2,"ints":[]},{"x":3,"ints":[4],"obj":{"y":"quack"}}]'
503
572
  expect(last_response.status).to eq(200)
504
573
  expect(last_response.body).to eq('arrays work')
505
574
 
575
+ get '/', splines: [{ x: 2, ints: [] }, { x: 3, ints: [4], obj: { y: 'quack' } }]
576
+ expect(last_response.status).to eq(200)
577
+ expect(last_response.body).to eq('arrays work')
578
+
506
579
  get '/', splines: '{"x":4,"ints":[2]}'
507
580
  expect(last_response.status).to eq(400)
508
581
  expect(last_response.body).to eq('splines[x] does not have a valid value')
509
582
 
583
+ get '/', splines: { x: 4, ints: [2] }
584
+ expect(last_response.status).to eq(400)
585
+ expect(last_response.body).to eq('splines[x] does not have a valid value')
586
+
510
587
  get '/', splines: '[{"x":1,"ints":[]},{"x":4,"ints":[]}]'
511
588
  expect(last_response.status).to eq(400)
512
589
  expect(last_response.body).to eq('splines[x] does not have a valid value')
590
+
591
+ get '/', splines: [{ x: 1, ints: [] }, { x: 4, ints: [] }]
592
+ expect(last_response.status).to eq(400)
593
+ expect(last_response.body).to eq('splines[x] does not have a valid value')
513
594
  end
514
595
 
515
596
  it 'works when declared optional' do
@@ -660,7 +741,7 @@ describe Grape::Validations::CoerceValidator do
660
741
  expect(last_response.status).to eq(200)
661
742
  expect(last_response.body).to eq('"one way"')
662
743
 
663
- get '/', a: %w(the other)
744
+ get '/', a: %w[the other]
664
745
  expect(last_response.status).to eq(200)
665
746
  expect(last_response.body).to eq('["the", "other"]')
666
747
 
@@ -678,7 +759,7 @@ describe Grape::Validations::CoerceValidator do
678
759
  expect(last_response.status).to eq(200)
679
760
  expect(last_response.body).to eq('[1, 2, 3]')
680
761
 
681
- get '/', b: %w(1 2 3)
762
+ get '/', b: %w[1 2 3]
682
763
  expect(last_response.status).to eq(200)
683
764
  expect(last_response.body).to eq('[1, 2, 3]')
684
765
 
@@ -700,7 +781,7 @@ describe Grape::Validations::CoerceValidator do
700
781
  expect(last_response.status).to eq(200)
701
782
  expect(last_response.body).to eq('"one"')
702
783
 
703
- get '/', d: %w(1 two)
784
+ get '/', d: %w[1 two]
704
785
  expect(last_response.status).to eq(200)
705
786
  expect(last_response.body).to eq('#<Set: {1, "two"}>')
706
787
  end
@@ -731,7 +812,7 @@ describe Grape::Validations::CoerceValidator do
731
812
  expect(last_response.status).to eq(200)
732
813
  expect(last_response.body).to eq('"one way"')
733
814
 
734
- get '/', a: %w(the other)
815
+ get '/', a: %w[the other]
735
816
  expect(last_response.status).to eq(200)
736
817
  expect(last_response.body).to eq('#<Hashie::Array ["the", "other"]>')
737
818
 
@@ -749,7 +830,7 @@ describe Grape::Validations::CoerceValidator do
749
830
  expect(last_response.status).to eq(200)
750
831
  expect(last_response.body).to eq('#<Hashie::Array [1, 2, 3]>')
751
832
 
752
- get '/', b: %w(1 2 3)
833
+ get '/', b: %w[1 2 3]
753
834
  expect(last_response.status).to eq(200)
754
835
  expect(last_response.body).to eq('#<Hashie::Array [1, 2, 3]>')
755
836
 
@@ -771,7 +852,7 @@ describe Grape::Validations::CoerceValidator do
771
852
  expect(last_response.status).to eq(200)
772
853
  expect(last_response.body).to eq('"one"')
773
854
 
774
- get '/', d: %w(1 two)
855
+ get '/', d: %w[1 two]
775
856
  expect(last_response.status).to eq(200)
776
857
  expect(last_response.body).to eq('#<Set: {1, "two"}>')
777
858
  end
@@ -75,6 +75,21 @@ describe Grape::Validations::DefaultValidator do
75
75
  get '/nested_optional_array' do
76
76
  { root: params[:root] }
77
77
  end
78
+
79
+ params do
80
+ requires :root, type: Hash do
81
+ optional :some_things, type: Array do
82
+ requires :foo
83
+ optional :options, type: Array do
84
+ optional :name, type: String
85
+ optional :value, type: String
86
+ end
87
+ end
88
+ end
89
+ end
90
+ get '/another_nested_optional_array' do
91
+ { root: params[:root] }
92
+ end
78
93
  end
79
94
  end
80
95
  end
@@ -99,6 +114,31 @@ describe Grape::Validations::DefaultValidator do
99
114
  expect(last_response.body).to eq({ root: params }.to_json)
100
115
  end
101
116
 
117
+ it 'does not allows faulty optional arrays' do
118
+ params = { some_things:
119
+ [
120
+ { foo: 'one', options: [{ name: 'wat', value: 'nope' }] },
121
+ { foo: 'two', options: [{ name: 'wat' }] },
122
+ { foo: 'three' }
123
+ ] }
124
+ error = { error: 'root[some_things][1][options][0][value] is missing' }
125
+ get '/nested_optional_array', root: params
126
+ expect(last_response.status).to eq(400)
127
+ expect(last_response.body).to eq(error.to_json)
128
+ end
129
+
130
+ it 'allows optional arrays with optional params' do
131
+ params = { some_things:
132
+ [
133
+ { foo: 'one', options: [{ value: 'nope' }] },
134
+ { foo: 'two', options: [{ name: 'wat' }] },
135
+ { foo: 'three' }
136
+ ] }
137
+ get '/another_nested_optional_array', root: params
138
+ expect(last_response.status).to eq(200)
139
+ expect(last_response.body).to eq({ root: params }.to_json)
140
+ end
141
+
102
142
  it 'set default value for optional param' do
103
143
  get('/')
104
144
  expect(last_response.status).to eq(200)
@@ -11,7 +11,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
11
11
  def required?; end
12
12
  end
13
13
  end
14
- let(:exactly_one_of_params) { [:beer, :wine, :grapefruit] }
14
+ let(:exactly_one_of_params) { %i[beer wine grapefruit] }
15
15
  let(:validator) { described_class.new(exactly_one_of_params, {}, false, scope.new) }
16
16
 
17
17
  context 'when all restricted params are present' do