grape 1.1.0 → 1.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +128 -43
  3. data/LICENSE +1 -1
  4. data/README.md +394 -47
  5. data/UPGRADING.md +111 -0
  6. data/grape.gemspec +3 -1
  7. data/lib/grape.rb +98 -66
  8. data/lib/grape/api.rb +136 -175
  9. data/lib/grape/api/instance.rb +280 -0
  10. data/lib/grape/config.rb +32 -0
  11. data/lib/grape/dsl/callbacks.rb +20 -0
  12. data/lib/grape/dsl/desc.rb +39 -7
  13. data/lib/grape/dsl/inside_route.rb +12 -6
  14. data/lib/grape/dsl/middleware.rb +7 -0
  15. data/lib/grape/dsl/parameters.rb +9 -4
  16. data/lib/grape/dsl/routing.rb +5 -1
  17. data/lib/grape/dsl/validations.rb +4 -3
  18. data/lib/grape/eager_load.rb +18 -0
  19. data/lib/grape/endpoint.rb +42 -26
  20. data/lib/grape/error_formatter.rb +1 -1
  21. data/lib/grape/exceptions/base.rb +9 -1
  22. data/lib/grape/exceptions/invalid_response.rb +9 -0
  23. data/lib/grape/exceptions/validation_errors.rb +4 -2
  24. data/lib/grape/formatter.rb +1 -1
  25. data/lib/grape/locale/en.yml +2 -0
  26. data/lib/grape/middleware/auth/base.rb +2 -4
  27. data/lib/grape/middleware/base.rb +2 -0
  28. data/lib/grape/middleware/error.rb +9 -4
  29. data/lib/grape/middleware/helpers.rb +10 -0
  30. data/lib/grape/middleware/stack.rb +1 -1
  31. data/lib/grape/middleware/versioner/header.rb +4 -4
  32. data/lib/grape/parser.rb +1 -1
  33. data/lib/grape/request.rb +1 -1
  34. data/lib/grape/router/attribute_translator.rb +2 -0
  35. data/lib/grape/router/route.rb +2 -2
  36. data/lib/grape/util/base_inheritable.rb +34 -0
  37. data/lib/grape/util/endpoint_configuration.rb +6 -0
  38. data/lib/grape/util/inheritable_values.rb +5 -25
  39. data/lib/grape/util/lazy_block.rb +25 -0
  40. data/lib/grape/util/lazy_value.rb +95 -0
  41. data/lib/grape/util/reverse_stackable_values.rb +7 -36
  42. data/lib/grape/util/stackable_values.rb +19 -22
  43. data/lib/grape/validations/attributes_iterator.rb +5 -3
  44. data/lib/grape/validations/multiple_attributes_iterator.rb +11 -0
  45. data/lib/grape/validations/params_scope.rb +20 -14
  46. data/lib/grape/validations/single_attribute_iterator.rb +13 -0
  47. data/lib/grape/validations/types/custom_type_coercer.rb +1 -1
  48. data/lib/grape/validations/types/file.rb +1 -1
  49. data/lib/grape/validations/validator_factory.rb +6 -11
  50. data/lib/grape/validations/validators/all_or_none.rb +6 -13
  51. data/lib/grape/validations/validators/as.rb +2 -3
  52. data/lib/grape/validations/validators/at_least_one_of.rb +5 -13
  53. data/lib/grape/validations/validators/base.rb +11 -10
  54. data/lib/grape/validations/validators/coerce.rb +4 -0
  55. data/lib/grape/validations/validators/default.rb +1 -1
  56. data/lib/grape/validations/validators/exactly_one_of.rb +6 -23
  57. data/lib/grape/validations/validators/multiple_params_base.rb +14 -10
  58. data/lib/grape/validations/validators/mutual_exclusion.rb +6 -18
  59. data/lib/grape/validations/validators/same_as.rb +23 -0
  60. data/lib/grape/version.rb +1 -1
  61. data/spec/grape/api/defines_boolean_in_params_spec.rb +37 -0
  62. data/spec/grape/api/routes_with_requirements_spec.rb +59 -0
  63. data/spec/grape/api_remount_spec.rb +466 -0
  64. data/spec/grape/api_spec.rb +379 -1
  65. data/spec/grape/config_spec.rb +17 -0
  66. data/spec/grape/dsl/desc_spec.rb +40 -16
  67. data/spec/grape/dsl/middleware_spec.rb +8 -0
  68. data/spec/grape/dsl/routing_spec.rb +10 -0
  69. data/spec/grape/endpoint_spec.rb +40 -4
  70. data/spec/grape/exceptions/base_spec.rb +65 -0
  71. data/spec/grape/exceptions/invalid_response_spec.rb +11 -0
  72. data/spec/grape/exceptions/validation_errors_spec.rb +6 -4
  73. data/spec/grape/integration/rack_spec.rb +22 -6
  74. data/spec/grape/middleware/auth/dsl_spec.rb +3 -3
  75. data/spec/grape/middleware/base_spec.rb +8 -0
  76. data/spec/grape/middleware/exception_spec.rb +1 -1
  77. data/spec/grape/middleware/formatter_spec.rb +15 -5
  78. data/spec/grape/middleware/versioner/header_spec.rb +6 -0
  79. data/spec/grape/named_api_spec.rb +19 -0
  80. data/spec/grape/request_spec.rb +24 -0
  81. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +29 -0
  82. data/spec/grape/validations/params_scope_spec.rb +184 -8
  83. data/spec/grape/validations/single_attribute_iterator_spec.rb +33 -0
  84. data/spec/grape/validations/validators/all_or_none_spec.rb +138 -30
  85. data/spec/grape/validations/validators/at_least_one_of_spec.rb +173 -29
  86. data/spec/grape/validations/validators/coerce_spec.rb +10 -2
  87. data/spec/grape/validations/validators/exactly_one_of_spec.rb +202 -38
  88. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +184 -27
  89. data/spec/grape/validations/validators/same_as_spec.rb +63 -0
  90. data/spec/grape/validations_spec.rb +33 -21
  91. data/spec/spec_helper.rb +4 -1
  92. metadata +35 -23
  93. data/Appraisals +0 -32
  94. data/Dangerfile +0 -2
  95. data/Gemfile +0 -33
  96. data/Gemfile.lock +0 -231
  97. data/Guardfile +0 -10
  98. data/RELEASING.md +0 -111
  99. data/Rakefile +0 -25
  100. data/benchmark/simple.rb +0 -27
  101. data/benchmark/simple_with_type_coercer.rb +0 -22
  102. data/gemfiles/multi_json.gemfile +0 -35
  103. data/gemfiles/multi_xml.gemfile +0 -35
  104. data/gemfiles/rack_1.5.2.gemfile +0 -35
  105. data/gemfiles/rack_edge.gemfile +0 -35
  106. data/gemfiles/rails_3.gemfile +0 -36
  107. data/gemfiles/rails_4.gemfile +0 -35
  108. data/gemfiles/rails_5.gemfile +0 -35
  109. data/gemfiles/rails_edge.gemfile +0 -35
  110. data/pkg/grape-0.17.0.gem +0 -0
  111. data/pkg/grape-0.19.0.gem +0 -0
@@ -2,61 +2,218 @@ require 'spec_helper'
2
2
 
3
3
  describe Grape::Validations::MutualExclusionValidator do
4
4
  describe '#validate!' do
5
- let(:scope) do
6
- Struct.new(:opts) do
7
- def params(arg)
8
- arg
5
+ subject(:validate) { post path, params }
6
+
7
+ module ValidationsSpec
8
+ module MutualExclusionValidatorSpec
9
+ class API < Grape::API
10
+ rescue_from Grape::Exceptions::ValidationErrors do |e|
11
+ error!(e.errors.transform_keys! { |key| key.join(',') }, 400)
12
+ end
13
+
14
+ params do
15
+ optional :beer
16
+ optional :wine
17
+ optional :grapefruit
18
+ mutually_exclusive :beer, :wine, :grapefruit
19
+ end
20
+ post do
21
+ end
22
+
23
+ params do
24
+ optional :beer
25
+ optional :wine
26
+ optional :grapefruit
27
+ optional :other
28
+ mutually_exclusive :beer, :wine, :grapefruit
29
+ end
30
+ post 'mixed-params' do
31
+ end
32
+
33
+ params do
34
+ optional :beer
35
+ optional :wine
36
+ optional :grapefruit
37
+ mutually_exclusive :beer, :wine, :grapefruit, message: 'you should not mix beer and wine'
38
+ end
39
+ post '/custom-message' do
40
+ end
41
+
42
+ params do
43
+ requires :item, type: Hash do
44
+ optional :beer
45
+ optional :wine
46
+ optional :grapefruit
47
+ mutually_exclusive :beer, :wine, :grapefruit
48
+ end
49
+ end
50
+ post '/nested-hash' do
51
+ end
52
+
53
+ params do
54
+ optional :item, type: Hash do
55
+ optional :beer
56
+ optional :wine
57
+ optional :grapefruit
58
+ mutually_exclusive :beer, :wine, :grapefruit
59
+ end
60
+ end
61
+ post '/nested-optional-hash' do
62
+ end
63
+
64
+ params do
65
+ requires :items, type: Array do
66
+ optional :beer
67
+ optional :wine
68
+ optional :grapefruit
69
+ mutually_exclusive :beer, :wine, :grapefruit
70
+ end
71
+ end
72
+ post '/nested-array' do
73
+ end
74
+
75
+ params do
76
+ requires :items, type: Array do
77
+ requires :nested_items, type: Array do
78
+ optional :beer, :wine, :grapefruit, type: Boolean
79
+ mutually_exclusive :beer, :wine, :grapefruit
80
+ end
81
+ end
82
+ end
83
+ post '/deeply-nested-array' do
84
+ end
9
85
  end
10
86
  end
11
87
  end
12
- let(:mutually_exclusive_params) { %i[beer wine grapefruit] }
13
- let(:validator) { described_class.new(mutually_exclusive_params, {}, false, scope.new) }
88
+
89
+ def app
90
+ ValidationsSpec::MutualExclusionValidatorSpec::API
91
+ end
14
92
 
15
93
  context 'when all mutually exclusive params are present' do
94
+ let(:path) { '/' }
16
95
  let(:params) { { beer: true, wine: true, grapefruit: true } }
17
96
 
18
- it 'raises a validation exception' do
19
- expect do
20
- validator.validate! params
21
- end.to raise_error(Grape::Exceptions::Validation)
97
+ it 'returns a validation error' do
98
+ validate
99
+ expect(last_response.status).to eq 400
100
+ expect(JSON.parse(last_response.body)).to eq(
101
+ 'beer,wine,grapefruit' => ['are mutually exclusive']
102
+ )
22
103
  end
23
104
 
24
105
  context 'mixed with other params' do
25
- let(:mixed_params) { params.merge!(other: true, andanother: true) }
106
+ let(:path) { '/mixed-params' }
107
+ let(:params) { { beer: true, wine: true, grapefruit: true, other: true } }
26
108
 
27
- it 'still raises a validation exception' do
28
- expect do
29
- validator.validate! mixed_params
30
- end.to raise_error(Grape::Exceptions::Validation)
109
+ it 'returns a validation error' do
110
+ validate
111
+ expect(last_response.status).to eq 400
112
+ expect(JSON.parse(last_response.body)).to eq(
113
+ 'beer,wine,grapefruit' => ['are mutually exclusive']
114
+ )
31
115
  end
32
116
  end
33
117
  end
34
118
 
35
119
  context 'when a subset of mutually exclusive params are present' do
120
+ let(:path) { '/' }
36
121
  let(:params) { { beer: true, grapefruit: true } }
37
122
 
38
- it 'raises a validation exception' do
39
- expect do
40
- validator.validate! params
41
- end.to raise_error(Grape::Exceptions::Validation)
123
+ it 'returns a validation error' do
124
+ validate
125
+ expect(last_response.status).to eq 400
126
+ expect(JSON.parse(last_response.body)).to eq(
127
+ 'beer,grapefruit' => ['are mutually exclusive']
128
+ )
42
129
  end
43
130
  end
44
131
 
45
- context 'when params keys come as strings' do
46
- let(:params) { { 'beer' => true, 'grapefruit' => true } }
132
+ context 'when custom message is specified' do
133
+ let(:path) { '/custom-message' }
134
+ let(:params) { { beer: true, wine: true } }
47
135
 
48
- it 'raises a validation exception' do
49
- expect do
50
- validator.validate! params
51
- end.to raise_error(Grape::Exceptions::Validation)
136
+ it 'returns a validation error' do
137
+ validate
138
+ expect(last_response.status).to eq 400
139
+ expect(JSON.parse(last_response.body)).to eq(
140
+ 'beer,wine' => ['you should not mix beer and wine']
141
+ )
52
142
  end
53
143
  end
54
144
 
55
145
  context 'when no mutually exclusive params are present' do
146
+ let(:path) { '/' }
56
147
  let(:params) { { beer: true, somethingelse: true } }
57
148
 
58
- it 'params' do
59
- expect(validator.validate!(params)).to eql params
149
+ it 'does not return a validation error' do
150
+ validate
151
+ expect(last_response.status).to eq 201
152
+ end
153
+ end
154
+
155
+ context 'when mutually exclusive params are nested inside required hash' do
156
+ let(:path) { '/nested-hash' }
157
+ let(:params) { { item: { beer: true, wine: true } } }
158
+
159
+ it 'returns a validation error with full names of the params' do
160
+ validate
161
+ expect(last_response.status).to eq 400
162
+ expect(JSON.parse(last_response.body)).to eq(
163
+ 'item[beer],item[wine]' => ['are mutually exclusive']
164
+ )
165
+ end
166
+ end
167
+
168
+ context 'when mutually exclusive params are nested inside optional hash' do
169
+ let(:path) { '/nested-optional-hash' }
170
+
171
+ context 'when params are passed' do
172
+ let(:params) { { item: { beer: true, wine: true } } }
173
+
174
+ it 'returns a validation error with full names of the params' do
175
+ validate
176
+ expect(last_response.status).to eq 400
177
+ expect(JSON.parse(last_response.body)).to eq(
178
+ 'item[beer],item[wine]' => ['are mutually exclusive']
179
+ )
180
+ end
181
+ end
182
+
183
+ context 'when params are empty' do
184
+ let(:params) { {} }
185
+
186
+ it 'does not return a validation error' do
187
+ validate
188
+ expect(last_response.status).to eq 201
189
+ end
190
+ end
191
+ end
192
+
193
+ context 'when mutually exclusive params are nested inside array' do
194
+ let(:path) { '/nested-array' }
195
+ let(:params) { { items: [{ beer: true, wine: true }, { wine: true, grapefruit: true }] } }
196
+
197
+ it 'returns a validation error with full names of the params' do
198
+ validate
199
+ expect(last_response.status).to eq 400
200
+ expect(JSON.parse(last_response.body)).to eq(
201
+ 'items[0][beer],items[0][wine]' => ['are mutually exclusive'],
202
+ 'items[1][wine],items[1][grapefruit]' => ['are mutually exclusive']
203
+ )
204
+ end
205
+ end
206
+
207
+ context 'when mutually exclusive params are deeply nested' do
208
+ let(:path) { '/deeply-nested-array' }
209
+ let(:params) { { items: [{ nested_items: [{ beer: true, wine: true }] }] } }
210
+
211
+ it 'returns a validation error with full names of the params' do
212
+ validate
213
+ expect(last_response.status).to eq 400
214
+ expect(JSON.parse(last_response.body)).to eq(
215
+ 'items[0][nested_items][0][beer],items[0][nested_items][0][wine]' => ['are mutually exclusive']
216
+ )
60
217
  end
61
218
  end
62
219
  end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe Grape::Validations::SameAsValidator do
4
+ module ValidationsSpec
5
+ module SameAsValidatorSpec
6
+ class API < Grape::API
7
+ params do
8
+ requires :password
9
+ requires :password_confirmation, same_as: :password
10
+ end
11
+ post do
12
+ end
13
+
14
+ params do
15
+ requires :password
16
+ requires :password_confirmation, same_as: { value: :password, message: 'not match' }
17
+ end
18
+ post '/custom-message' do
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def app
25
+ ValidationsSpec::SameAsValidatorSpec::API
26
+ end
27
+
28
+ describe '/' do
29
+ context 'is the same' do
30
+ it do
31
+ post '/', password: '987654', password_confirmation: '987654'
32
+ expect(last_response.status).to eq(201)
33
+ expect(last_response.body).to eq('')
34
+ end
35
+ end
36
+
37
+ context 'is not the same' do
38
+ it do
39
+ post '/', password: '123456', password_confirmation: 'whatever'
40
+ expect(last_response.status).to eq(400)
41
+ expect(last_response.body).to eq('password_confirmation is not the same as password')
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '/custom-message' do
47
+ context 'is the same' do
48
+ it do
49
+ post '/custom-message', password: '987654', password_confirmation: '987654'
50
+ expect(last_response.status).to eq(201)
51
+ expect(last_response.body).to eq('')
52
+ end
53
+ end
54
+
55
+ context 'is not the same' do
56
+ it do
57
+ post '/custom-message', password: '123456', password_confirmation: 'whatever'
58
+ expect(last_response.status).to eq(400)
59
+ expect(last_response.body).to eq('password_confirmation not match')
60
+ end
61
+ end
62
+ end
63
+ end
@@ -540,7 +540,10 @@ describe Grape::Validations do
540
540
  ]
541
541
 
542
542
  expect(last_response.status).to eq(400)
543
- expect(last_response.body).to eq('children[0][parents] is missing, children[1][parents] is missing')
543
+ expect(last_response.body).to eq(
544
+ 'children[0][parents][0][name] is missing, ' \
545
+ 'children[1][parents][0][name] is missing'
546
+ )
544
547
  end
545
548
 
546
549
  it 'safely handles empty arrays and blank parameters' do
@@ -548,7 +551,14 @@ describe Grape::Validations do
548
551
  # should actually return 200, since an empty array is valid.
549
552
  get '/within_array', children: []
550
553
  expect(last_response.status).to eq(400)
551
- expect(last_response.body).to eq('children is missing')
554
+ expect(last_response.body).to eq(
555
+ 'children[0][name] is missing, ' \
556
+ 'children[0][parents] is missing, ' \
557
+ 'children[0][parents] is invalid, ' \
558
+ 'children[0][parents][0][name] is missing, ' \
559
+ 'children[0][parents][0][name] is empty'
560
+ )
561
+
552
562
  get '/within_array', children: [name: 'Jay']
553
563
  expect(last_response.status).to eq(400)
554
564
  expect(last_response.body).to eq('children[0][parents] is missing')
@@ -1013,7 +1023,7 @@ describe Grape::Validations do
1013
1023
  expect(last_response.body).to eq('custom is not custom with options!')
1014
1024
  end
1015
1025
  end
1016
- end # end custom validation
1026
+ end
1017
1027
 
1018
1028
  context 'named' do
1019
1029
  context 'can be defined' do
@@ -1223,7 +1233,9 @@ describe Grape::Validations do
1223
1233
  end
1224
1234
  get '/custom_message/mutually_exclusive', beer: 'true', wine: 'true', nested: { scotch: 'true', aquavit: 'true' }, nested2: [{ scotch2: 'true' }, { scotch2: 'true', aquavit2: 'true' }]
1225
1235
  expect(last_response.status).to eq(400)
1226
- expect(last_response.body).to eq 'beer, wine are mutually exclusive pass only one, scotch, aquavit are mutually exclusive pass only one, scotch2, aquavit2 are mutually exclusive pass only one'
1236
+ expect(last_response.body).to eq(
1237
+ 'beer, wine are mutually exclusive pass only one, nested[scotch], nested[aquavit] are mutually exclusive pass only one, nested2[1][scotch2], nested2[1][aquavit2] are mutually exclusive pass only one'
1238
+ )
1227
1239
  end
1228
1240
  end
1229
1241
 
@@ -1249,7 +1261,7 @@ describe Grape::Validations do
1249
1261
 
1250
1262
  get '/mutually_exclusive', beer: 'true', wine: 'true', nested: { scotch: 'true', aquavit: 'true' }, nested2: [{ scotch2: 'true' }, { scotch2: 'true', aquavit2: 'true' }]
1251
1263
  expect(last_response.status).to eq(400)
1252
- expect(last_response.body).to eq 'beer, wine are mutually exclusive, scotch, aquavit are mutually exclusive, scotch2, aquavit2 are mutually exclusive'
1264
+ expect(last_response.body).to eq 'beer, wine are mutually exclusive, nested[scotch], nested[aquavit] are mutually exclusive, nested2[1][scotch2], nested2[1][aquavit2] are mutually exclusive'
1253
1265
  end
1254
1266
  end
1255
1267
 
@@ -1318,7 +1330,7 @@ describe Grape::Validations do
1318
1330
  optional :beer
1319
1331
  optional :wine
1320
1332
  optional :juice
1321
- exactly_one_of :beer, :wine, :juice, message: { exactly_one: 'are missing, exactly one parameter is required', mutual_exclusion: 'are mutually exclusive, exactly one parameter is required' }
1333
+ exactly_one_of :beer, :wine, :juice, message: 'are missing, exactly one parameter is required'
1322
1334
  end
1323
1335
  get '/exactly_one_of' do
1324
1336
  'exactly_one_of works!'
@@ -1352,7 +1364,7 @@ describe Grape::Validations do
1352
1364
  it 'errors when two or more are present' do
1353
1365
  get '/custom_message/exactly_one_of', beer: 'string', wine: 'anotherstring'
1354
1366
  expect(last_response.status).to eq(400)
1355
- expect(last_response.body).to eq 'beer, wine are mutually exclusive, exactly one parameter is required'
1367
+ expect(last_response.body).to eq 'beer, wine, juice are missing, exactly one parameter is required'
1356
1368
  end
1357
1369
  end
1358
1370
 
@@ -1371,7 +1383,7 @@ describe Grape::Validations do
1371
1383
  it 'errors when two or more are present' do
1372
1384
  get '/exactly_one_of', beer: 'string', wine: 'anotherstring'
1373
1385
  expect(last_response.status).to eq(400)
1374
- expect(last_response.body).to eq 'beer, wine are mutually exclusive'
1386
+ expect(last_response.body).to eq 'beer, wine, juice are missing, exactly one parameter must be provided'
1375
1387
  end
1376
1388
  end
1377
1389
 
@@ -1399,7 +1411,7 @@ describe Grape::Validations do
1399
1411
  it 'errors when none are present' do
1400
1412
  get '/exactly_one_of_nested'
1401
1413
  expect(last_response.status).to eq(400)
1402
- expect(last_response.body).to eq 'nested is missing, beer_nested, wine_nested, juice_nested are missing, exactly one parameter must be provided'
1414
+ expect(last_response.body).to eq 'nested is missing, nested[beer_nested], nested[wine_nested], nested[juice_nested] are missing, exactly one parameter must be provided'
1403
1415
  end
1404
1416
 
1405
1417
  it 'succeeds when one is present' do
@@ -1411,7 +1423,7 @@ describe Grape::Validations do
1411
1423
  it 'errors when two or more are present' do
1412
1424
  get '/exactly_one_of_nested', nested: { beer_nested: 'string' }, nested2: [{ beer_nested2: 'string', wine_nested2: 'anotherstring' }]
1413
1425
  expect(last_response.status).to eq(400)
1414
- expect(last_response.body).to eq 'beer_nested2, wine_nested2 are mutually exclusive'
1426
+ 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'
1415
1427
  end
1416
1428
  end
1417
1429
  end
@@ -1485,16 +1497,16 @@ describe Grape::Validations do
1485
1497
  before :each do
1486
1498
  subject.params do
1487
1499
  requires :nested, type: Hash do
1488
- optional :beer_nested
1489
- optional :wine_nested
1490
- optional :juice_nested
1491
- at_least_one_of :beer_nested, :wine_nested, :juice_nested
1500
+ optional :beer
1501
+ optional :wine
1502
+ optional :juice
1503
+ at_least_one_of :beer, :wine, :juice
1492
1504
  end
1493
1505
  optional :nested2, type: Array do
1494
- optional :beer_nested2
1495
- optional :wine_nested2
1496
- optional :juice_nested2
1497
- at_least_one_of :beer_nested2, :wine_nested2, :juice_nested2
1506
+ optional :beer
1507
+ optional :wine
1508
+ optional :juice
1509
+ at_least_one_of :beer, :wine, :juice
1498
1510
  end
1499
1511
  end
1500
1512
  subject.get '/at_least_one_of_nested' do
@@ -1505,17 +1517,17 @@ describe Grape::Validations do
1505
1517
  it 'errors when none are present' do
1506
1518
  get '/at_least_one_of_nested'
1507
1519
  expect(last_response.status).to eq(400)
1508
- expect(last_response.body).to eq 'nested is missing, beer_nested, wine_nested, juice_nested are missing, at least one parameter must be provided'
1520
+ expect(last_response.body).to eq 'nested is missing, nested[beer], nested[wine], nested[juice] are missing, at least one parameter must be provided'
1509
1521
  end
1510
1522
 
1511
1523
  it 'does not error when one is present' do
1512
- get '/at_least_one_of_nested', nested: { beer_nested: 'string' }, nested2: [{ beer_nested2: 'string' }]
1524
+ get '/at_least_one_of_nested', nested: { beer: 'string' }, nested2: [{ beer: 'string' }]
1513
1525
  expect(last_response.status).to eq(200)
1514
1526
  expect(last_response.body).to eq 'at_least_one_of works!'
1515
1527
  end
1516
1528
 
1517
1529
  it 'does not error when two are present' do
1518
- get '/at_least_one_of_nested', nested: { beer_nested: 'string', wine_nested: 'string' }, nested2: [{ beer_nested2: 'string', wine_nested2: 'string' }]
1530
+ get '/at_least_one_of_nested', nested: { beer: 'string', wine: 'string' }, nested2: [{ beer: 'string', wine: 'string' }]
1519
1531
  expect(last_response.status).to eq(200)
1520
1532
  expect(last_response.body).to eq 'at_least_one_of works!'
1521
1533
  end