Skip to content

Commit 44b2431

Browse files
committed
Merge pull request #1218 from towanda/array-context
Provides Array index context in errors.
2 parents b190011 + cec42f2 commit 44b2431

File tree

5 files changed

+26
-15
lines changed

5 files changed

+26
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
* Your contribution here.
77

8+
* [#1218](https://github.com/ruby-grape/grape/pull/1218): Provide array index context in errors - [@towanda](https://github.com/towanda).
89
* [#1196](https://github.com/ruby-grape/grape/pull/1196): Allow multiple `before_each` blocks - [@huynhquancam](https://github.com/huynhquancam).
910
* [#1190](https://github.com/ruby-grape/grape/putt/1190): Bypass formatting for statuses with no entity-body - [@tylerdooling](https://github.com/tylerdooling).
1011
* [#1188](https://github.com/ruby-grape/grape/putt/1188): Allow parameters with more than one type - [@dslh](https://github.com/dslh).

lib/grape/validations/attributes_iterator.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@ module Validations
33
class AttributesIterator
44
include Enumerable
55

6+
attr_reader :scope
7+
68
def initialize(validator, scope, params)
9+
@scope = scope
710
@attrs = validator.attrs
811
@params = Array.wrap(scope.params(params))
912
end
1013

1114
def each
1215
@params.each do |resource_params|
13-
@attrs.each do |attr_name|
16+
@attrs.each_with_index do |attr_name, index|
17+
if resource_params.is_a?(Hash) && resource_params[attr_name].is_a?(Array)
18+
scope.index = index
19+
end
1420
yield resource_params, attr_name
1521
end
1622
end

lib/grape/validations/params_scope.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module Grape
22
module Validations
33
class ParamsScope
4-
attr_accessor :element, :parent
4+
attr_accessor :element, :parent, :index
55

66
include Grape::DSL::Parameters
77

@@ -46,7 +46,7 @@ def full_name(name)
4646
case
4747
when nested?
4848
# Find our containing element's name, and append ours.
49-
"#{@parent.full_name(@element)}[#{name}]"
49+
"#{@parent.full_name(@element)}#{parent_index}[#{name}]"
5050
when lateral?
5151
# Find the name of the element as if it was at the
5252
# same nesting level as our parent.
@@ -57,6 +57,10 @@ def full_name(name)
5757
end
5858
end
5959

60+
def parent_index
61+
"[#{@parent.index}]" if @parent.present? && @parent.index.present?
62+
end
63+
6064
# @return [Boolean] whether or not this scope is the root-level scope
6165
def root?
6266
!@parent

spec/grape/validations/validators/coerce_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ class User
293293

294294
get '/ints', ints: '{"i":1,"j":"2"}'
295295
expect(last_response.status).to eq(400)
296-
expect(last_response.body).to eq('ints[i] is missing, ints[i] is invalid, ints[j] is missing')
296+
expect(last_response.body).to eq('ints[0][i] is missing, ints[0][i] is invalid, ints[0][j] is missing')
297297

298298
get '/ints', ints: '[{"i":"1","j":"2"}]'
299299
expect(last_response.status).to eq(200)

spec/grape/validations_spec.rb

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ def define_requires_none
337337

338338
get '/required', items: [{ key: 'hash in array' }]
339339
expect(last_response.status).to eq(400)
340-
expect(last_response.body).to eq('items is invalid, items[key] does not have a valid value')
340+
expect(last_response.body).to eq('items is invalid, items[0][key] does not have a valid value')
341341
end
342342

343343
it 'works when all params match' do
@@ -492,7 +492,7 @@ def validate_param!(attr_name, params)
492492
# NOTE: with body parameters in json or XML or similar this
493493
# should actually fail with: children[parents][name] is missing.
494494
expect(last_response.status).to eq(400)
495-
expect(last_response.body).to eq('children[parents] is missing')
495+
expect(last_response.body).to eq('children[0][parents] is missing')
496496
end
497497

498498
it 'safely handles empty arrays and blank parameters' do
@@ -503,7 +503,7 @@ def validate_param!(attr_name, params)
503503
expect(last_response.body).to eq('children is missing')
504504
get '/within_array', children: [name: 'Jay']
505505
expect(last_response.status).to eq(400)
506-
expect(last_response.body).to eq('children[parents] is missing')
506+
expect(last_response.body).to eq('children[0][parents] is missing')
507507
end
508508

509509
it 'errors when param is not an Array' do
@@ -518,7 +518,7 @@ def validate_param!(attr_name, params)
518518

519519
get '/within_array', children: [name: 'Jay', parents: { name: 'Fred' }]
520520
expect(last_response.status).to eq(400)
521-
expect(last_response.body).to eq('children[parents] is invalid')
521+
expect(last_response.body).to eq('children[0][parents] is invalid')
522522
end
523523
end
524524

@@ -644,15 +644,15 @@ def validate_param!(attr_name, params)
644644
{ name: 'Job', parents: [{ name: 'Joy' }] }
645645
]
646646
expect(last_response.status).to eq(400)
647-
expect(last_response.body).to eq('children[parents][name] is missing')
647+
expect(last_response.body).to eq('children[0][parents][0][name] is missing')
648648
end
649649

650650
it 'safely handles empty arrays and blank parameters' do
651651
put_with_json '/within_array', children: []
652652
expect(last_response.status).to eq(200)
653653
put_with_json '/within_array', children: [name: 'Jay']
654654
expect(last_response.status).to eq(400)
655-
expect(last_response.body).to eq('children[parents] is missing')
655+
expect(last_response.body).to eq('children[0][parents] is missing')
656656
end
657657
end
658658

@@ -683,7 +683,7 @@ def validate_param!(attr_name, params)
683683
it 'errors when group is present, but required param is not' do
684684
get '/optional_group', items: [{ not_key: 'foo' }]
685685
expect(last_response.status).to eq(400)
686-
expect(last_response.body).to eq('items[key] is missing')
686+
expect(last_response.body).to eq('items[0][key] is missing')
687687
end
688688

689689
it "errors when param is present but isn't an Array" do
@@ -727,7 +727,7 @@ def validate_param!(attr_name, params)
727727
it 'does internal validations if the outer group is present' do
728728
get '/nested_optional_group', items: [{ key: 'foo' }]
729729
expect(last_response.status).to eq(400)
730-
expect(last_response.body).to eq('items[required_subitems] is missing')
730+
expect(last_response.body).to eq('items[0][required_subitems] is missing')
731731

732732
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
733733
expect(last_response.status).to eq(200)
@@ -737,7 +737,7 @@ def validate_param!(attr_name, params)
737737
it 'handles deep nesting' do
738738
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ not_value: 'baz' }] }]
739739
expect(last_response.status).to eq(400)
740-
expect(last_response.body).to eq('items[optional_subitems][value] is missing')
740+
expect(last_response.body).to eq('items[0][optional_subitems][0][value] is missing')
741741

742742
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ value: 'baz' }] }]
743743
expect(last_response.status).to eq(200)
@@ -747,15 +747,15 @@ def validate_param!(attr_name, params)
747747
it 'handles validation within arrays' do
748748
get '/nested_optional_group', items: [{ key: 'foo' }]
749749
expect(last_response.status).to eq(400)
750-
expect(last_response.body).to eq('items[required_subitems] is missing')
750+
expect(last_response.body).to eq('items[0][required_subitems] is missing')
751751

752752
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
753753
expect(last_response.status).to eq(200)
754754
expect(last_response.body).to eq('nested optional group works')
755755

756756
get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ not_value: 'baz' }] }]
757757
expect(last_response.status).to eq(400)
758-
expect(last_response.body).to eq('items[optional_subitems][value] is missing')
758+
expect(last_response.body).to eq('items[0][optional_subitems][0][value] is missing')
759759
end
760760

761761
it 'adds to declared parameters' do

0 commit comments

Comments
 (0)