Skip to content

Commit 31d9ff0

Browse files
committed
Avoid coercing parameter with multiple types to an empty Array
If an optional parameter has multiple types (e.g. [File, String]) and a nil parameter is passed, Grape would previously coerce the value to an empty Array. This can break certain APIs that treat nil values differently from an empty Array. To fix this, we refactor the nil handling into a `coerce_nil` method and remove the special case check for handling an Array of types. Closes #2018
1 parent 8deebb5 commit 31d9ff0

File tree

3 files changed

+39
-5
lines changed

3 files changed

+39
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#### Fixes
1010

1111
* Your contribution here.
12+
* [#2019](https://github.com/ruby-grape/grape/pull/2018): Avoid coercing parameter with multiple types to an empty Array - [@stanhu](https://github.com/stanhu).
1213

1314
### 1.3.1 (2020/03/11)
1415

lib/grape/validations/validators/coerce.rb

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,7 @@ def valid_type?(val)
6767
def coerce_value(val)
6868
# define default values for structures, the dry-types lib which is used
6969
# for coercion doesn't accept nil as a value, so it would fail
70-
if val.nil?
71-
return [] if type == Array || type.is_a?(Array)
72-
return Set.new if type == Set
73-
return {} if type == Hash
74-
end
70+
return coerce_nil(val) if val.nil?
7571

7672
converter.call(val)
7773

@@ -80,6 +76,13 @@ def coerce_value(val)
8076
Types::InvalidValue.new
8177
end
8278

79+
def coerce_nil(val)
80+
return [] if type == Array
81+
return Set.new if type == Set
82+
return {} if type == Hash
83+
return val
84+
end
85+
8386
# Type to which the parameter will be coerced.
8487
#
8588
# @return [Class]

spec/grape/validations/validators/coerce_spec.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,23 @@ def self.parsed?(value)
180180
expect(last_response.body).to eq(integer_class_name)
181181
end
182182

183+
it 'String' do
184+
subject.params do
185+
requires :string, coerce: String
186+
end
187+
subject.get '/string' do
188+
params[:string].class
189+
end
190+
191+
get '/string', string: 45
192+
expect(last_response.status).to eq(200)
193+
expect(last_response.body).to eq('String')
194+
195+
get '/string', string: nil
196+
expect(last_response.status).to eq(200)
197+
expect(last_response.body).to eq('NilClass')
198+
end
199+
183200
it 'is a custom type' do
184201
subject.params do
185202
requires :uri, coerce: SecureURIOnly
@@ -647,6 +664,19 @@ def self.parsed?(value)
647664
expect(last_response.body).to eq('String')
648665
end
649666

667+
it 'respects nil values' do
668+
subject.params do
669+
optional :a, types: [File, String]
670+
end
671+
subject.get '/' do
672+
params[:a].class.to_s
673+
end
674+
675+
get '/', a: nil
676+
expect(last_response.status).to eq(200)
677+
expect(last_response.body).to eq('NilClass')
678+
end
679+
650680
it 'fails when no coercion is possible' do
651681
subject.params do
652682
requires :a, types: [Boolean, Integer]

0 commit comments

Comments
 (0)