Skip to content

Commit

Permalink
Merge pull request #2043 from kadotami/fix_declared_for_nested_array_…
Browse files Browse the repository at this point in the history
…and_hash

Modify declared for nested array and hash
  • Loading branch information
dblock committed Apr 27, 2020
2 parents e0dfb9c + 3551c67 commit 785d28a
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* [#2038](https://github.com/ruby-grape/grape/pull/2038): Travis - update ruby versions - [@ericproulx](https://github.com/ericproulx).

#### Fixes
* [#2043](https://github.com/ruby-grape/grape/pull/2043): Modify declared for nested array and hash - [@kadotami](https://github.com/kadotami).

* Your contribution here.

Expand Down
51 changes: 37 additions & 14 deletions lib/grape/dsl/inside_route.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,36 +28,38 @@ def self.post_filter_methods(type)
# Methods which should not be available in filters until the before filter
# has completed
module PostBeforeFilter
def declared(passed_params, options = {}, declared_params = nil)
def declared(passed_params, options = {}, declared_params = nil, params_nested_path = [])
options = options.reverse_merge(include_missing: true, include_parent_namespaces: true)
declared_params ||= optioned_declared_params(**options)

if passed_params.is_a?(Array)
declared_array(passed_params, options, declared_params)
declared_array(passed_params, options, declared_params, params_nested_path)
else
declared_hash(passed_params, options, declared_params)
declared_hash(passed_params, options, declared_params, params_nested_path)
end
end

private

def declared_array(passed_params, options, declared_params)
def declared_array(passed_params, options, declared_params, params_nested_path)
passed_params.map do |passed_param|
declared(passed_param || {}, options, declared_params)
declared(passed_param || {}, options, declared_params, params_nested_path)
end
end

def declared_hash(passed_params, options, declared_params)
def declared_hash(passed_params, options, declared_params, params_nested_path)
declared_params.each_with_object(passed_params.class.new) do |declared_param, memo|
if declared_param.is_a?(Hash)
declared_param.each_pair do |declared_parent_param, declared_children_params|
params_nested_path_dup = params_nested_path.dup
params_nested_path_dup << declared_parent_param.to_s
next unless options[:include_missing] || passed_params.key?(declared_parent_param)

passed_children_params = passed_params[declared_parent_param] || passed_params.class.new
memo_key = optioned_param_key(declared_parent_param, options)

memo[memo_key] = handle_passed_param(declared_parent_param, passed_children_params) do
declared(passed_children_params, options, declared_children_params)
memo[memo_key] = handle_passed_param(passed_children_params, params_nested_path_dup) do
declared(passed_children_params, options, declared_children_params, params_nested_path_dup)
end
end
else
Expand All @@ -77,19 +79,34 @@ def declared_hash(passed_params, options, declared_params)
end
end

def handle_passed_param(declared_param, passed_children_params, &_block)
should_be_empty_array?(declared_param, passed_children_params) ? [] : yield
def handle_passed_param(passed_children_params, params_nested_path, &_block)
if should_be_empty_hash?(passed_children_params, params_nested_path)
{}
elsif should_be_empty_array?(passed_children_params, params_nested_path)
[]
else
yield
end
end

def should_be_empty_array?(declared_param, passed_children_params)
declared_param_is_array?(declared_param) && passed_children_params.empty?
def should_be_empty_array?(passed_children_params, params_nested_path)
passed_children_params.empty? && declared_param_is_array?(params_nested_path)
end

def declared_param_is_array?(declared_param)
key = declared_param.to_s
def declared_param_is_array?(params_nested_path)
key = route_options_params_key(params_nested_path)
route_options_params[key] && route_options_params[key][:type] == 'Array'
end

def should_be_empty_hash?(passed_children_params, params_nested_path)
passed_children_params.empty? && declared_param_is_hash?(params_nested_path)
end

def declared_param_is_hash?(params_nested_path)
key = route_options_params_key(params_nested_path)
route_options_params[key] && route_options_params[key][:type] == 'Hash'
end

def route_options_params
options[:route_options][:params] || {}
end
Expand All @@ -98,6 +115,12 @@ def optioned_param_key(declared_param, options)
options[:stringify] ? declared_param.to_s : declared_param.to_sym
end

def route_options_params_key(params_nested_path)
key = params_nested_path[0]
key += '[' + params_nested_path[1..-1].join('][') + ']' if params_nested_path.size > 1
key
end

def optioned_declared_params(**options)
declared_params = if options[:include_parent_namespaces]
# Declared params including parent namespaces
Expand Down
23 changes: 18 additions & 5 deletions spec/grape/endpoint_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,12 @@ def app
optional :seventh
end
end
optional :nested_arr, type: Array do
optional :eighth
end
end
optional :nested_arr, type: Array do
optional :eighth
optional :arr, type: Array do
optional :nineth
end
end
end
Expand Down Expand Up @@ -390,7 +393,7 @@ def app

get '/declared?first=present&nested[fourth]=1'
expect(last_response.status).to eq(200)
expect(JSON.parse(last_response.body)['nested'].keys.size).to eq 3
expect(JSON.parse(last_response.body)['nested'].keys.size).to eq 4
end

it 'builds nested params when given array' do
Expand Down Expand Up @@ -421,7 +424,7 @@ def app

get '/declared?first=present'
expect(last_response.status).to eq(200)
expect(JSON.parse(last_response.body)['nested']).to be_a(Hash)
expect(JSON.parse(last_response.body)['nested']).to eq({})
end

it 'to be an array when include_missing is true' do
Expand All @@ -431,7 +434,17 @@ def app

get '/declared?first=present'
expect(last_response.status).to eq(200)
expect(JSON.parse(last_response.body)['nested_arr']).to be_a(Array)
expect(JSON.parse(last_response.body)['arr']).to be_a(Array)
end

it 'to be an array when nested and include_missing is true' do
subject.get '/declared' do
declared(params, include_missing: true)
end

get '/declared?first=present&nested[fourth]=1'
expect(last_response.status).to eq(200)
expect(JSON.parse(last_response.body)['nested']['nested_arr']).to be_a(Array)
end

it 'to be nil when include_missing is false' do
Expand Down

0 comments on commit 785d28a

Please sign in to comment.