Skip to content

Commit 83960b0

Browse files
authored
Coerce empty string to nil for all Primitive types except String (#2067)
* Reorder PrimitiveCoercerSpec contexts to be alphabetical * Coerce empty string to nil for all Primitive types except String This matches the behavior in v1.2.5 with the exception of Symbol. In v1.2.5 an empty string was coerced to an empty symbol (:''), now it is coerced to nil. * Add integration specs for empty string param coercion These specs mirror the specs for nil params. Exceptions are made for non-primitive types based on their current behavior.
1 parent b3ab0a8 commit 83960b0

File tree

4 files changed

+148
-6
lines changed

4 files changed

+148
-6
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+
* [#2067](https://github.com/ruby-grape/grape/pull/2067): Coerce empty string to nil for all primitive types except String - [@petekinnecom](https://github.com/petekinnecom).
1213
* [#2064](https://github.com/ruby-grape/grape/pull/2064): Fix Ruby 2.7 deprecation warning in `Grape::Middleware::Base#initialize` - [@skarger](https://github.com/skarger).
1314

1415
### 1.3.3 (2020/05/23)

lib/grape/validations/types/primitive_coercer.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ def initialize(type, strict = false)
3737
def call(val)
3838
return InvalidValue.new if reject?(val)
3939
return nil if val.nil? || treat_as_nil?(val)
40-
return '' if val == ''
4140

4241
super
4342
end
@@ -60,7 +59,7 @@ def reject?(val)
6059
# absence of a value and coerces it into nil. See a discussion there
6160
# https://github.com/ruby-grape/grape/pull/2045
6261
def treat_as_nil?(val)
63-
val == '' && type == Grape::API::Boolean
62+
val == '' && type != String
6463
end
6564
end
6665
end

spec/grape/validations/types/primitive_coercer_spec.rb

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@
88
subject { described_class.new(type, strict) }
99

1010
describe '#call' do
11+
context 'BigDecimal' do
12+
let(:type) { BigDecimal }
13+
14+
it 'coerces to BigDecimal' do
15+
expect(subject.call(5)).to eq(BigDecimal(5))
16+
end
17+
18+
it 'coerces an empty string to nil' do
19+
expect(subject.call('')).to be_nil
20+
end
21+
end
22+
1123
context 'Boolean' do
1224
let(:type) { Grape::API::Boolean }
1325

@@ -32,19 +44,63 @@
3244
end
3345
end
3446

47+
context 'DateTime' do
48+
let(:type) { DateTime }
49+
50+
it 'coerces an empty string to nil' do
51+
expect(subject.call('')).to be_nil
52+
end
53+
end
54+
55+
context 'Float' do
56+
let(:type) { Float }
57+
58+
it 'coerces an empty string to nil' do
59+
expect(subject.call('')).to be_nil
60+
end
61+
end
62+
63+
context 'Integer' do
64+
let(:type) { Integer }
65+
66+
it 'coerces an empty string to nil' do
67+
expect(subject.call('')).to be_nil
68+
end
69+
end
70+
71+
context 'Numeric' do
72+
let(:type) { Numeric }
73+
74+
it 'coerces an empty string to nil' do
75+
expect(subject.call('')).to be_nil
76+
end
77+
end
78+
79+
context 'Time' do
80+
let(:type) { Time }
81+
82+
it 'coerces an empty string to nil' do
83+
expect(subject.call('')).to be_nil
84+
end
85+
end
86+
3587
context 'String' do
3688
let(:type) { String }
3789

3890
it 'coerces to String' do
3991
expect(subject.call(10)).to eq('10')
4092
end
93+
94+
it 'does not coerce an empty string to nil' do
95+
expect(subject.call('')).to eq('')
96+
end
4197
end
4298

43-
context 'BigDecimal' do
44-
let(:type) { BigDecimal }
99+
context 'Symbol' do
100+
let(:type) { Symbol }
45101

46-
it 'coerces to BigDecimal' do
47-
expect(subject.call(5)).to eq(BigDecimal(5))
102+
it 'coerces an empty string to nil' do
103+
expect(subject.call('')).to be_nil
48104
end
49105
end
50106

spec/grape/validations/validators/coerce_spec.rb

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,92 @@ def self.parsed?(value)
497497
end
498498
end
499499
end
500+
501+
context 'empty string' do
502+
context 'primitive types' do
503+
(Grape::Validations::Types::PRIMITIVES - [String]).each do |type|
504+
it "is coerced to nil for type #{type}" do
505+
subject.params do
506+
requires :param, type: type
507+
end
508+
subject.get '/empty_string' do
509+
params[:param].class
510+
end
511+
512+
get '/empty_string', param: ''
513+
expect(last_response.status).to eq(200)
514+
expect(last_response.body).to eq('NilClass')
515+
end
516+
end
517+
518+
it 'is not coerced to nil for type String' do
519+
subject.params do
520+
requires :param, type: String
521+
end
522+
subject.get '/empty_string' do
523+
params[:param].class
524+
end
525+
526+
get '/empty_string', param: ''
527+
expect(last_response.status).to eq(200)
528+
expect(last_response.body).to eq('String')
529+
end
530+
end
531+
532+
context 'structures types' do
533+
(Grape::Validations::Types::STRUCTURES - [Hash]).each do |type|
534+
it "is coerced to nil for type #{type}" do
535+
subject.params do
536+
requires :param, type: type
537+
end
538+
subject.get '/empty_string' do
539+
params[:param].class
540+
end
541+
542+
get '/empty_string', param: ''
543+
expect(last_response.status).to eq(200)
544+
expect(last_response.body).to eq('NilClass')
545+
end
546+
end
547+
end
548+
549+
context 'special types' do
550+
(Grape::Validations::Types::SPECIAL.keys - [File, Rack::Multipart::UploadedFile]).each do |type|
551+
it "is coerced to nil for type #{type}" do
552+
subject.params do
553+
requires :param, type: type
554+
end
555+
subject.get '/empty_string' do
556+
params[:param].class
557+
end
558+
559+
get '/empty_string', param: ''
560+
expect(last_response.status).to eq(200)
561+
expect(last_response.body).to eq('NilClass')
562+
end
563+
end
564+
565+
context 'variant-member-type collections' do
566+
[
567+
Array[Integer, String],
568+
[Integer, String, Array[Integer, String]]
569+
].each do |type|
570+
it "is coerced to nil for type #{type}" do
571+
subject.params do
572+
requires :param, type: type
573+
end
574+
subject.get '/empty_string' do
575+
params[:param].class
576+
end
577+
578+
get '/empty_string', param: ''
579+
expect(last_response.status).to eq(200)
580+
expect(last_response.body).to eq('NilClass')
581+
end
582+
end
583+
end
584+
end
585+
end
500586
end
501587

502588
context 'using coerce_with' do

0 commit comments

Comments
 (0)