grape 1.1.0 → 1.2.5

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 (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