Skip to content

Commit 7e95ac8

Browse files
author
Gwénaël Rault
authored
Add nested array coercion spec (#2098)
1 parent 192a2a2 commit 7e95ac8

File tree

5 files changed

+46
-5
lines changed

5 files changed

+46
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* [#2091](https://github.com/ruby-grape/grape/pull/2091): Fix ruby 2.7 keyword deprecations - [@dim](https://github.com/dim).
1414
* [#2097](https://github.com/ruby-grape/grape/pull/2097): Skip to set default value unless `meets_dependency?` - [@wanabe](https://github.com/wanabe).
1515
* [#2096](https://github.com/ruby-grape/grape/pull/2096): Fix redundant dependency check - [@braktar](https://github.com/braktar).
16+
* [#2096](https://github.com/ruby-grape/grape/pull/2098): Fix nested coercion - [@braktar](https://github.com/braktar).
1617

1718
### 1.4.0 (2020/07/10)
1819

benchmark/large_model.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def self.vrp_request_vehicle(this)
7979
this.optional(:cost_time_multiplier, type: Float)
8080

8181
this.optional :router_dimension, type: String, values: %w[time distance]
82-
this.optional(:skills, type: Array[Array[String]])
82+
this.optional(:skills, type: Array[Array[String]], coerce_with: ->(val) { val.is_a?(String) ? [val.split(/,/).map(&:strip)] : val })
8383

8484
this.optional(:unavailable_work_day_indices, type: Array[Integer])
8585

@@ -224,7 +224,10 @@ def self.vrp_request_schedule(this)
224224
end
225225
end
226226
post '/' do
227-
'hello'
227+
{
228+
skills_v1: params[:vrp][:vehicles].first[:skills],
229+
skills_v2: params[:vrp][:vehicles].last[:skills]
230+
}
228231
end
229232
end
230233
puts Grape::VERSION
@@ -238,7 +241,8 @@ def self.vrp_request_schedule(this)
238241

239242
start = Time.now
240243
result = RubyProf.profile do
241-
API.call env
244+
response = API.call env
245+
puts response.last
242246
end
243247
puts Time.now - start
244248
printer = RubyProf::FlatPrinter.new(result)

benchmark/resource/vrp_example.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

lib/grape/validations/types/custom_type_coercer.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,25 @@ def infer_type_check(type)
103103
# passed, or if the type also implements a parse() method.
104104
type
105105
elsif type.is_a?(Enumerable)
106-
->(value) { value.respond_to?(:all?) && value.all? { |item| item.is_a? type[0] } }
106+
lambda do |value|
107+
value.is_a?(Enumerable) && value.all? do |val|
108+
recursive_type_check(type.first, val)
109+
end
110+
end
107111
else
108112
# By default, do a simple type check
109113
->(value) { value.is_a? type }
110114
end
111115
end
112116

117+
def recursive_type_check(type, value)
118+
if type.is_a?(Enumerable) && value.is_a?(Enumerable)
119+
value.all? { |val| recursive_type_check(type.first, val) }
120+
else
121+
!type.is_a?(Enumerable) && value.is_a?(type)
122+
end
123+
end
124+
113125
# Enforce symbolized keys for complex types
114126
# by wrapping the coercion method such that
115127
# any Hash objects in the immediate heirarchy

spec/grape/validations/validators/coerce_spec.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,30 @@ def self.parsed?(value)
620620
expect(JSON.parse(last_response.body)).to eq(%w[a b c d])
621621
end
622622

623+
it 'parses parameters with Array[Array[String]] type and coerce_with' do
624+
subject.params do
625+
requires :values, type: Array[Array[String]], coerce_with: ->(val) { val.is_a?(String) ? [val.split(/,/).map(&:strip)] : val }
626+
end
627+
subject.post '/coerce_nested_strings' do
628+
params[:values]
629+
end
630+
631+
post '/coerce_nested_strings', ::Grape::Json.dump(values: 'a,b,c,d'), 'CONTENT_TYPE' => 'application/json'
632+
expect(last_response.status).to eq(201)
633+
expect(JSON.parse(last_response.body)).to eq([%w[a b c d]])
634+
635+
post '/coerce_nested_strings', ::Grape::Json.dump(values: [%w[a c], %w[b]]), 'CONTENT_TYPE' => 'application/json'
636+
expect(last_response.status).to eq(201)
637+
expect(JSON.parse(last_response.body)).to eq([%w[a c], %w[b]])
638+
639+
post '/coerce_nested_strings', ::Grape::Json.dump(values: [[]]), 'CONTENT_TYPE' => 'application/json'
640+
expect(last_response.status).to eq(201)
641+
expect(JSON.parse(last_response.body)).to eq([[]])
642+
643+
post '/coerce_nested_strings', ::Grape::Json.dump(values: [['a', { bar: 0 }], ['b']]), 'CONTENT_TYPE' => 'application/json'
644+
expect(last_response.status).to eq(400)
645+
end
646+
623647
it 'parses parameters with Array[Integer] type' do
624648
subject.params do
625649
requires :values, type: Array[Integer], coerce_with: ->(val) { val.split(/\s+/).map(&:to_i) }

0 commit comments

Comments
 (0)