grape 1.3.0 → 1.5.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.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +90 -0
  3. data/LICENSE +1 -1
  4. data/README.md +104 -21
  5. data/UPGRADING.md +243 -39
  6. data/lib/grape.rb +4 -5
  7. data/lib/grape/api.rb +4 -4
  8. data/lib/grape/api/instance.rb +32 -31
  9. data/lib/grape/content_types.rb +34 -0
  10. data/lib/grape/dsl/helpers.rb +2 -1
  11. data/lib/grape/dsl/inside_route.rb +76 -42
  12. data/lib/grape/dsl/parameters.rb +4 -4
  13. data/lib/grape/dsl/routing.rb +8 -8
  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_errors.rb +11 -12
  19. data/lib/grape/http/headers.rb +26 -0
  20. data/lib/grape/middleware/base.rb +3 -4
  21. data/lib/grape/middleware/error.rb +10 -12
  22. data/lib/grape/middleware/formatter.rb +3 -3
  23. data/lib/grape/middleware/stack.rb +19 -5
  24. data/lib/grape/middleware/versioner/header.rb +4 -4
  25. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
  26. data/lib/grape/middleware/versioner/path.rb +1 -1
  27. data/lib/grape/namespace.rb +12 -2
  28. data/lib/grape/path.rb +13 -3
  29. data/lib/grape/request.rb +13 -8
  30. data/lib/grape/router.rb +26 -30
  31. data/lib/grape/router/attribute_translator.rb +25 -4
  32. data/lib/grape/router/pattern.rb +17 -16
  33. data/lib/grape/router/route.rb +5 -24
  34. data/lib/grape/{serve_file → serve_stream}/file_body.rb +1 -1
  35. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +1 -1
  36. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +8 -8
  37. data/lib/grape/util/base_inheritable.rb +15 -8
  38. data/lib/grape/util/cache.rb +20 -0
  39. data/lib/grape/util/lazy_object.rb +43 -0
  40. data/lib/grape/util/lazy_value.rb +1 -0
  41. data/lib/grape/util/reverse_stackable_values.rb +2 -0
  42. data/lib/grape/util/stackable_values.rb +7 -20
  43. data/lib/grape/validations/params_scope.rb +6 -5
  44. data/lib/grape/validations/types.rb +6 -5
  45. data/lib/grape/validations/types/array_coercer.rb +14 -5
  46. data/lib/grape/validations/types/build_coercer.rb +5 -8
  47. data/lib/grape/validations/types/custom_type_coercer.rb +14 -2
  48. data/lib/grape/validations/types/dry_type_coercer.rb +36 -1
  49. data/lib/grape/validations/types/file.rb +15 -12
  50. data/lib/grape/validations/types/json.rb +40 -36
  51. data/lib/grape/validations/types/primitive_coercer.rb +15 -6
  52. data/lib/grape/validations/types/set_coercer.rb +6 -4
  53. data/lib/grape/validations/types/variant_collection_coercer.rb +1 -1
  54. data/lib/grape/validations/validators/as.rb +1 -1
  55. data/lib/grape/validations/validators/base.rb +2 -4
  56. data/lib/grape/validations/validators/coerce.rb +4 -11
  57. data/lib/grape/validations/validators/default.rb +3 -5
  58. data/lib/grape/validations/validators/exactly_one_of.rb +4 -2
  59. data/lib/grape/validations/validators/except_values.rb +1 -1
  60. data/lib/grape/validations/validators/regexp.rb +1 -1
  61. data/lib/grape/validations/validators/values.rb +1 -1
  62. data/lib/grape/version.rb +1 -1
  63. data/spec/grape/api/instance_spec.rb +50 -0
  64. data/spec/grape/api_spec.rb +82 -6
  65. data/spec/grape/dsl/inside_route_spec.rb +182 -33
  66. data/spec/grape/endpoint/declared_spec.rb +590 -0
  67. data/spec/grape/endpoint_spec.rb +0 -521
  68. data/spec/grape/entity_spec.rb +6 -0
  69. data/spec/grape/exceptions/validation_errors_spec.rb +2 -2
  70. data/spec/grape/integration/rack_sendfile_spec.rb +12 -8
  71. data/spec/grape/middleware/auth/strategies_spec.rb +1 -1
  72. data/spec/grape/middleware/error_spec.rb +1 -1
  73. data/spec/grape/middleware/formatter_spec.rb +3 -3
  74. data/spec/grape/middleware/stack_spec.rb +12 -1
  75. data/spec/grape/path_spec.rb +4 -4
  76. data/spec/grape/validations/instance_behaivour_spec.rb +1 -1
  77. data/spec/grape/validations/params_scope_spec.rb +26 -0
  78. data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
  79. data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
  80. data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
  81. data/spec/grape/validations/types_spec.rb +1 -1
  82. data/spec/grape/validations/validators/coerce_spec.rb +329 -77
  83. data/spec/grape/validations/validators/default_spec.rb +170 -0
  84. data/spec/grape/validations/validators/exactly_one_of_spec.rb +12 -12
  85. data/spec/grape/validations/validators/except_values_spec.rb +1 -0
  86. data/spec/grape/validations/validators/values_spec.rb +1 -1
  87. data/spec/grape/validations_spec.rb +30 -30
  88. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  89. data/spec/spec_helper.rb +3 -10
  90. data/spec/support/chunks.rb +14 -0
  91. data/spec/support/eager_load.rb +19 -0
  92. data/spec/support/versioned_helpers.rb +3 -5
  93. metadata +121 -105
  94. data/lib/grape/util/content_types.rb +0 -28
@@ -298,4 +298,174 @@ describe Grape::Validations::DefaultValidator do
298
298
  end
299
299
  end
300
300
  end
301
+
302
+ context 'optional with nil as value' do
303
+ subject do
304
+ Class.new(Grape::API) do
305
+ default_format :json
306
+ end
307
+ end
308
+
309
+ def app
310
+ subject
311
+ end
312
+
313
+ context 'primitive types' do
314
+ [
315
+ [Integer, 0],
316
+ [Integer, 42],
317
+ [Float, 0.0],
318
+ [Float, 4.2],
319
+ [BigDecimal, 0.0],
320
+ [BigDecimal, 4.2],
321
+ [Numeric, 0],
322
+ [Numeric, 42],
323
+ [Date, Date.today],
324
+ [DateTime, DateTime.now],
325
+ [Time, Time.now],
326
+ [Time, Time.at(0)],
327
+ [Grape::API::Boolean, false],
328
+ [String, ''],
329
+ [String, 'non-empty-string'],
330
+ [Symbol, :symbol],
331
+ [TrueClass, true],
332
+ [FalseClass, false]
333
+ ].each do |type, default|
334
+ it 'respects the default value' do
335
+ subject.params do
336
+ optional :param, type: type, default: default
337
+ end
338
+ subject.get '/default_value' do
339
+ params[:param]
340
+ end
341
+
342
+ get '/default_value', param: nil
343
+ expect(last_response.status).to eq(200)
344
+ expect(last_response.body).to eq(default.to_json)
345
+ end
346
+ end
347
+ end
348
+
349
+ context 'structures types' do
350
+ [
351
+ [Hash, {}],
352
+ [Hash, { test: 'non-empty' }],
353
+ [Array, []],
354
+ [Array, ['non-empty']],
355
+ [Array[Integer], []],
356
+ [Set, []],
357
+ [Set, [1]]
358
+ ].each do |type, default|
359
+ it 'respects the default value' do
360
+ subject.params do
361
+ optional :param, type: type, default: default
362
+ end
363
+ subject.get '/default_value' do
364
+ params[:param]
365
+ end
366
+
367
+ get '/default_value', param: nil
368
+ expect(last_response.status).to eq(200)
369
+ expect(last_response.body).to eq(default.to_json)
370
+ end
371
+ end
372
+ end
373
+
374
+ context 'special types' do
375
+ [
376
+ [JSON, ''],
377
+ [JSON, { test: 'non-empty-string' }.to_json],
378
+ [Array[JSON], []],
379
+ [Array[JSON], [{ test: 'non-empty-string' }.to_json]],
380
+ [::File, ''],
381
+ [::File, { test: 'non-empty-string' }.to_json],
382
+ [Rack::Multipart::UploadedFile, ''],
383
+ [Rack::Multipart::UploadedFile, { test: 'non-empty-string' }.to_json]
384
+ ].each do |type, default|
385
+ it 'respects the default value' do
386
+ subject.params do
387
+ optional :param, type: type, default: default
388
+ end
389
+ subject.get '/default_value' do
390
+ params[:param]
391
+ end
392
+
393
+ get '/default_value', param: nil
394
+ expect(last_response.status).to eq(200)
395
+ expect(last_response.body).to eq(default.to_json)
396
+ end
397
+ end
398
+ end
399
+
400
+ context 'variant-member-type collections' do
401
+ [
402
+ [Array[Integer, String], [0, '']],
403
+ [Array[Integer, String], [42, 'non-empty-string']],
404
+ [[Integer, String, Array[Integer, String]], [0, '', [0, '']]],
405
+ [[Integer, String, Array[Integer, String]], [42, 'non-empty-string', [42, 'non-empty-string']]]
406
+ ].each do |type, default|
407
+ it 'respects the default value' do
408
+ subject.params do
409
+ optional :param, type: type, default: default
410
+ end
411
+ subject.get '/default_value' do
412
+ params[:param]
413
+ end
414
+
415
+ get '/default_value', param: nil
416
+ expect(last_response.status).to eq(200)
417
+ expect(last_response.body).to eq(default.to_json)
418
+ end
419
+ end
420
+ end
421
+ end
422
+
423
+ context 'array with default values and given conditions' do
424
+ subject do
425
+ Class.new(Grape::API) do
426
+ default_format :json
427
+ end
428
+ end
429
+
430
+ def app
431
+ subject
432
+ end
433
+
434
+ it 'applies the default values only if the conditions are met' do
435
+ subject.params do
436
+ requires :ary, type: Array do
437
+ requires :has_value, type: Grape::API::Boolean
438
+ given has_value: ->(has_value) { has_value } do
439
+ optional :type, type: String, values: %w[str int], default: 'str'
440
+ given type: ->(type) { type == 'str' } do
441
+ optional :str, type: String, default: 'a'
442
+ end
443
+ given type: ->(type) { type == 'int' } do
444
+ optional :int, type: Integer, default: 1
445
+ end
446
+ end
447
+ end
448
+ end
449
+ subject.post('/nested_given_and_default') { declared(self.params) }
450
+
451
+ params = {
452
+ ary: [
453
+ { has_value: false },
454
+ { has_value: true, type: 'int', int: 123 },
455
+ { has_value: true, type: 'str', str: 'b' }
456
+ ]
457
+ }
458
+ expected = {
459
+ 'ary' => [
460
+ { 'has_value' => false, 'type' => nil, 'int' => nil, 'str' => nil },
461
+ { 'has_value' => true, 'type' => 'int', 'int' => 123, 'str' => nil },
462
+ { 'has_value' => true, 'type' => 'str', 'int' => nil, 'str' => 'b' }
463
+ ]
464
+ }
465
+
466
+ post '/nested_given_and_default', params
467
+ expect(last_response.status).to eq(201)
468
+ expect(JSON.parse(last_response.body)).to eq(expected)
469
+ end
470
+ end
301
471
  end
@@ -100,7 +100,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
100
100
  validate
101
101
  expect(last_response.status).to eq 400
102
102
  expect(JSON.parse(last_response.body)).to eq(
103
- 'beer,wine,grapefruit' => ['are missing, exactly one parameter must be provided']
103
+ 'beer,wine,grapefruit' => ['are mutually exclusive']
104
104
  )
105
105
  end
106
106
 
@@ -112,7 +112,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
112
112
  validate
113
113
  expect(last_response.status).to eq 400
114
114
  expect(JSON.parse(last_response.body)).to eq(
115
- 'beer,wine,grapefruit' => ['are missing, exactly one parameter must be provided']
115
+ 'beer,wine,grapefruit' => ['are mutually exclusive']
116
116
  )
117
117
  end
118
118
  end
@@ -126,7 +126,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
126
126
  validate
127
127
  expect(last_response.status).to eq 400
128
128
  expect(JSON.parse(last_response.body)).to eq(
129
- 'beer,wine,grapefruit' => ['are missing, exactly one parameter must be provided']
129
+ 'beer,grapefruit' => ['are mutually exclusive']
130
130
  )
131
131
  end
132
132
  end
@@ -139,7 +139,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
139
139
  validate
140
140
  expect(last_response.status).to eq 400
141
141
  expect(JSON.parse(last_response.body)).to eq(
142
- 'beer,wine,grapefruit' => ['you should choose one']
142
+ 'beer,wine' => ['you should choose one']
143
143
  )
144
144
  end
145
145
  end
@@ -175,7 +175,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
175
175
  validate
176
176
  expect(last_response.status).to eq 400
177
177
  expect(JSON.parse(last_response.body)).to eq(
178
- 'item[beer],item[wine],item[grapefruit]' => ['are missing, exactly one parameter must be provided']
178
+ 'item[beer],item[wine]' => ['are mutually exclusive']
179
179
  )
180
180
  end
181
181
  end
@@ -190,7 +190,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
190
190
  validate
191
191
  expect(last_response.status).to eq 400
192
192
  expect(JSON.parse(last_response.body)).to eq(
193
- 'item[beer],item[wine],item[grapefruit]' => ['are missing, exactly one parameter must be provided']
193
+ 'item[beer],item[wine]' => ['are mutually exclusive']
194
194
  )
195
195
  end
196
196
  end
@@ -213,11 +213,11 @@ describe Grape::Validations::ExactlyOneOfValidator do
213
213
  validate
214
214
  expect(last_response.status).to eq 400
215
215
  expect(JSON.parse(last_response.body)).to eq(
216
- 'items[0][beer],items[0][wine],items[0][grapefruit]' => [
217
- 'are missing, exactly one parameter must be provided'
216
+ 'items[0][beer],items[0][wine]' => [
217
+ 'are mutually exclusive'
218
218
  ],
219
- 'items[1][beer],items[1][wine],items[1][grapefruit]' => [
220
- 'are missing, exactly one parameter must be provided'
219
+ 'items[1][wine],items[1][grapefruit]' => [
220
+ 'are mutually exclusive'
221
221
  ]
222
222
  )
223
223
  end
@@ -231,8 +231,8 @@ describe Grape::Validations::ExactlyOneOfValidator do
231
231
  validate
232
232
  expect(last_response.status).to eq 400
233
233
  expect(JSON.parse(last_response.body)).to eq(
234
- 'items[0][nested_items][0][beer],items[0][nested_items][0][wine],items[0][nested_items][0][grapefruit]' => [
235
- 'are missing, exactly one parameter must be provided'
234
+ 'items[0][nested_items][0][beer],items[0][nested_items][0][wine]' => [
235
+ 'are mutually exclusive'
236
236
  ]
237
237
  )
238
238
  end
@@ -8,6 +8,7 @@ describe Grape::Validations::ExceptValuesValidator do
8
8
  DEFAULT_EXCEPTS = ['invalid-type1', 'invalid-type2', 'invalid-type3'].freeze
9
9
  class << self
10
10
  attr_accessor :excepts
11
+
11
12
  def excepts
12
13
  @excepts ||= []
13
14
  [DEFAULT_EXCEPTS + @excepts].flatten.uniq
@@ -319,7 +319,7 @@ describe Grape::Validations::ValuesValidator do
319
319
  expect(last_response.status).to eq 200
320
320
  end
321
321
 
322
- it 'allows for an optional param with a list of values' do
322
+ it 'accepts for an optional param with a list of values' do
323
323
  put('/optional_with_array_of_string_values', optional: nil)
324
324
  expect(last_response.status).to eq 200
325
325
  end
@@ -9,16 +9,23 @@ describe Grape::Validations do
9
9
  subject
10
10
  end
11
11
 
12
+ def declared_params
13
+ subject.namespace_stackable(:declared_params).flatten
14
+ end
15
+
12
16
  describe 'params' do
13
17
  context 'optional' do
14
- it 'validates when params is present' do
18
+ before do
15
19
  subject.params do
16
20
  optional :a_number, regexp: /^[0-9]+$/
21
+ optional :attachment, type: File
17
22
  end
18
23
  subject.get '/optional' do
19
24
  'optional works!'
20
25
  end
26
+ end
21
27
 
28
+ it 'validates when params is present' do
22
29
  get '/optional', a_number: 'string'
23
30
  expect(last_response.status).to eq(400)
24
31
  expect(last_response.body).to eq('a_number is invalid')
@@ -29,14 +36,7 @@ describe Grape::Validations do
29
36
  end
30
37
 
31
38
  it "doesn't validate when param not present" do
32
- subject.params do
33
- optional :a_number, regexp: /^[0-9]+$/
34
- end
35
- subject.get '/optional' do
36
- 'optional works!'
37
- end
38
-
39
- get '/optional'
39
+ get '/optional', a_number: nil, attachment: nil
40
40
  expect(last_response.status).to eq(200)
41
41
  expect(last_response.body).to eq('optional works!')
42
42
  end
@@ -45,7 +45,7 @@ describe Grape::Validations do
45
45
  subject.params do
46
46
  optional :some_param
47
47
  end
48
- expect(subject.route_setting(:declared_params)).to eq([:some_param])
48
+ expect(declared_params).to eq([:some_param])
49
49
  end
50
50
  end
51
51
 
@@ -65,7 +65,7 @@ describe Grape::Validations do
65
65
 
66
66
  it 'adds entity documentation to declared params' do
67
67
  define_optional_using
68
- expect(subject.route_setting(:declared_params)).to eq(%i[field_a field_b])
68
+ expect(declared_params).to eq(%i[field_a field_b])
69
69
  end
70
70
 
71
71
  it 'works when field_a and field_b are not present' do
@@ -112,7 +112,7 @@ describe Grape::Validations do
112
112
  subject.params do
113
113
  requires :some_param
114
114
  end
115
- expect(subject.route_setting(:declared_params)).to eq([:some_param])
115
+ expect(declared_params).to eq([:some_param])
116
116
  end
117
117
 
118
118
  it 'works when required field is present but nil' do
@@ -197,7 +197,7 @@ describe Grape::Validations do
197
197
 
198
198
  it 'adds entity documentation to declared params' do
199
199
  define_requires_all
200
- expect(subject.route_setting(:declared_params)).to eq(%i[required_field optional_field])
200
+ expect(declared_params).to eq(%i[required_field optional_field])
201
201
  end
202
202
 
203
203
  it 'errors when required_field is not present' do
@@ -232,7 +232,7 @@ describe Grape::Validations do
232
232
 
233
233
  it 'adds entity documentation to declared params' do
234
234
  define_requires_none
235
- expect(subject.route_setting(:declared_params)).to eq(%i[required_field optional_field])
235
+ expect(declared_params).to eq(%i[required_field optional_field])
236
236
  end
237
237
 
238
238
  it 'errors when required_field is not present' do
@@ -262,7 +262,7 @@ describe Grape::Validations do
262
262
 
263
263
  it 'adds only the entity documentation to declared params, nothing more' do
264
264
  define_requires_all
265
- expect(subject.route_setting(:declared_params)).to eq(%i[required_field optional_field])
265
+ expect(declared_params).to eq(%i[required_field optional_field])
266
266
  end
267
267
  end
268
268
 
@@ -328,7 +328,7 @@ describe Grape::Validations do
328
328
  requires :key
329
329
  end
330
330
  end
331
- expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
331
+ expect(declared_params).to eq([items: [:key]])
332
332
  end
333
333
  end
334
334
 
@@ -400,7 +400,7 @@ describe Grape::Validations do
400
400
  requires :key
401
401
  end
402
402
  end
403
- expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
403
+ expect(declared_params).to eq([items: [:key]])
404
404
  end
405
405
  end
406
406
 
@@ -463,7 +463,7 @@ describe Grape::Validations do
463
463
  requires :key
464
464
  end
465
465
  end
466
- expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
466
+ expect(declared_params).to eq([items: [:key]])
467
467
  end
468
468
  end
469
469
 
@@ -578,7 +578,7 @@ describe Grape::Validations do
578
578
  # NOTE: with body parameters in json or XML or similar this
579
579
  # should actually fail with: children[parents][name] is missing.
580
580
  expect(last_response.status).to eq(400)
581
- expect(last_response.body).to eq('children[1][parents] is missing')
581
+ expect(last_response.body).to eq('children[1][parents] is missing, children[0][parents][1][name] is missing, children[0][parents][1][name] is empty')
582
582
  end
583
583
 
584
584
  it 'errors when a parameter is not present in array within array' do
@@ -619,7 +619,7 @@ describe Grape::Validations do
619
619
 
620
620
  get '/within_array', children: [name: 'Jay']
621
621
  expect(last_response.status).to eq(400)
622
- expect(last_response.body).to eq('children[0][parents] is missing')
622
+ expect(last_response.body).to eq('children[0][parents] is missing, children[0][parents][0][name] is missing, children[0][parents][0][name] is empty')
623
623
  end
624
624
 
625
625
  it 'errors when param is not an Array' do
@@ -767,7 +767,7 @@ describe Grape::Validations do
767
767
  expect(last_response.status).to eq(200)
768
768
  put_with_json '/within_array', children: [name: 'Jay']
769
769
  expect(last_response.status).to eq(400)
770
- expect(last_response.body).to eq('children[0][parents] is missing')
770
+ expect(last_response.body).to eq('children[0][parents] is missing, children[0][parents][0][name] is missing')
771
771
  end
772
772
  end
773
773
 
@@ -817,7 +817,7 @@ describe Grape::Validations do
817
817
  requires :key
818
818
  end
819
819
  end
820
- expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
820
+ expect(declared_params).to eq([items: [:key]])
821
821
  end
822
822
  end
823
823
 
@@ -842,7 +842,7 @@ describe Grape::Validations do
842
842
  it 'does internal validations if the outer group is present' do
843
843
  get '/nested_optional_group', items: [{ key: 'foo' }]
844
844
  expect(last_response.status).to eq(400)
845
- expect(last_response.body).to eq('items[0][required_subitems] is missing')
845
+ expect(last_response.body).to eq('items[0][required_subitems] is missing, items[0][required_subitems][0][value] is missing')
846
846
 
847
847
  get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
848
848
  expect(last_response.status).to eq(200)
@@ -862,7 +862,7 @@ describe Grape::Validations do
862
862
  it 'handles validation within arrays' do
863
863
  get '/nested_optional_group', items: [{ key: 'foo' }]
864
864
  expect(last_response.status).to eq(400)
865
- expect(last_response.body).to eq('items[0][required_subitems] is missing')
865
+ expect(last_response.body).to eq('items[0][required_subitems] is missing, items[0][required_subitems][0][value] is missing')
866
866
 
867
867
  get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
868
868
  expect(last_response.status).to eq(200)
@@ -881,7 +881,7 @@ describe Grape::Validations do
881
881
  requires(:required_subitems, type: Array) { requires :value }
882
882
  end
883
883
  end
884
- expect(subject.route_setting(:declared_params)).to eq([items: [:key, { optional_subitems: [:value] }, { required_subitems: [:value] }]])
884
+ expect(declared_params).to eq([items: [:key, { optional_subitems: [:value] }, { required_subitems: [:value] }]])
885
885
  end
886
886
  end
887
887
 
@@ -1126,14 +1126,14 @@ describe Grape::Validations do
1126
1126
  subject.params do
1127
1127
  use :pagination
1128
1128
  end
1129
- expect(subject.route_setting(:declared_params)).to eq %i[page per_page]
1129
+ expect(declared_params).to eq %i[page per_page]
1130
1130
  end
1131
1131
 
1132
1132
  it 'by #use with multiple params' do
1133
1133
  subject.params do
1134
1134
  use :pagination, :period
1135
1135
  end
1136
- expect(subject.route_setting(:declared_params)).to eq %i[page per_page start_date end_date]
1136
+ expect(declared_params).to eq %i[page per_page start_date end_date]
1137
1137
  end
1138
1138
  end
1139
1139
 
@@ -1422,7 +1422,7 @@ describe Grape::Validations do
1422
1422
  it 'errors when two or more are present' do
1423
1423
  get '/custom_message/exactly_one_of', beer: 'string', wine: 'anotherstring'
1424
1424
  expect(last_response.status).to eq(400)
1425
- expect(last_response.body).to eq 'beer, wine, juice are missing, exactly one parameter is required'
1425
+ expect(last_response.body).to eq 'beer, wine are missing, exactly one parameter is required'
1426
1426
  end
1427
1427
  end
1428
1428
 
@@ -1441,7 +1441,7 @@ describe Grape::Validations do
1441
1441
  it 'errors when two or more are present' do
1442
1442
  get '/exactly_one_of', beer: 'string', wine: 'anotherstring'
1443
1443
  expect(last_response.status).to eq(400)
1444
- expect(last_response.body).to eq 'beer, wine, juice are missing, exactly one parameter must be provided'
1444
+ expect(last_response.body).to eq 'beer, wine are mutually exclusive'
1445
1445
  end
1446
1446
  end
1447
1447
 
@@ -1481,7 +1481,7 @@ describe Grape::Validations do
1481
1481
  it 'errors when two or more are present' do
1482
1482
  get '/exactly_one_of_nested', nested: { beer_nested: 'string' }, nested2: [{ beer_nested2: 'string', wine_nested2: 'anotherstring' }]
1483
1483
  expect(last_response.status).to eq(400)
1484
- expect(last_response.body).to eq 'nested2[0][beer_nested2], nested2[0][wine_nested2], nested2[0][juice_nested2] are missing, exactly one parameter must be provided'
1484
+ expect(last_response.body).to eq 'nested2[0][beer_nested2], nested2[0][wine_nested2] are mutually exclusive'
1485
1485
  end
1486
1486
  end
1487
1487
  end