Skip to content

Commit 7a56fe9

Browse files
thogg4dblock
authored andcommitted
refactor declared and include nil values for missing nested params (#1575)
1 parent 3b46b47 commit 7a56fe9

File tree

3 files changed

+84
-79
lines changed

3 files changed

+84
-79
lines changed

CHANGELOG.md

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

1010
* [#1555](https://github.com/ruby-grape/grape/pull/1555): Added code coverage w/Coveralls - [@dblock](https://github.com/dblock).
1111
* [#1568](https://github.com/ruby-grape/grape/pull/1568): Add `proc` option to `values` validator to allow custom checks - [@jlfaber](https://github.com/jlfaber).
12+
* [#1575](https://github.com/ruby-grape/grape/pull/1575): Include nil values for missing nested params in declared - [@thogg4](https://github.com/thogg4).
1213
* Your contribution here.
1314

1415
#### Fixes

lib/grape/dsl/inside_route.rb

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,41 +26,57 @@ def self.post_filter_methods(type)
2626
# Methods which should not be available in filters until the before filter
2727
# has completed
2828
module PostBeforeFilter
29-
def declared(params, options = {}, declared_params = nil)
29+
def declared(passed_params, options = {}, declared_params = nil)
3030
options = options.reverse_merge(include_missing: true, include_parent_namespaces: true)
31+
declared_params ||= optioned_declared_params(options)
3132

32-
# Declared params including parent namespaces
33-
all_declared_params = route_setting(:saved_declared_params).flatten | Array(route_setting(:declared_params))
33+
if passed_params.is_a?(Array)
34+
declared_array(passed_params, options, declared_params)
35+
else
36+
declared_hash(passed_params, options, declared_params)
37+
end
38+
end
3439

35-
# Declared params at current namespace
36-
current_namespace_declared_params = route_setting(:saved_declared_params).last & Array(route_setting(:declared_params))
40+
private
3741

38-
declared_params ||= options[:include_parent_namespaces] ? all_declared_params : current_namespace_declared_params
42+
def declared_array(passed_params, options, declared_params)
43+
passed_params.map do |passed_param|
44+
declared(passed_param || {}, options, declared_params)
45+
end
46+
end
3947

40-
raise ArgumentError, 'Tried to filter for declared parameters but none exist.' unless declared_params
48+
def declared_hash(passed_params, options, declared_params)
49+
declared_params.each_with_object(Hashie::Mash.new) do |declared_param, memo|
50+
# If it is not a Hash then it does not have children.
51+
# Find its value or set it to nil.
52+
if !declared_param.is_a?(Hash)
53+
memo[optioned_param_key(declared_param, options)] = passed_params[declared_param]
54+
else
55+
declared_param.each_pair do |declared_parent_param, declared_children_params|
56+
next unless options[:include_missing] || passed_params.key?(declared_parent_param)
4157

42-
if params.is_a? Array
43-
params.map do |param|
44-
declared(param || {}, options, declared_params)
58+
passed_children_params = passed_params[declared_parent_param] || Hashie::Mash.new
59+
memo[optioned_param_key(declared_parent_param, options)] = declared(passed_children_params, @options, declared_children_params)
60+
end
4561
end
46-
else
47-
declared_params.each_with_object(Hashie::Mash.new) do |key, hash|
48-
key = { key => nil } unless key.is_a? Hash
62+
end
63+
end
4964

50-
key.each_pair do |parent, children|
51-
output_key = options[:stringify] ? parent.to_s : parent.to_sym
65+
def optioned_param_key(declared_param, options)
66+
options[:stringify] ? declared_param.to_s : declared_param.to_sym
67+
end
5268

53-
next unless options[:include_missing] || params.key?(parent)
69+
def optioned_declared_params(options)
70+
declared_params = if options[:include_parent_namespaces]
71+
# Declared params including parent namespaces
72+
route_setting(:saved_declared_params).flatten | Array(route_setting(:declared_params))
73+
else
74+
# Declared params at current namespace
75+
route_setting(:saved_declared_params).last & Array(route_setting(:declared_params))
76+
end
5477

55-
hash[output_key] = if children
56-
children_params = params[parent] || (children.is_a?(Array) ? [] : {})
57-
declared(children_params, options, Array(children))
58-
else
59-
params[parent]
60-
end
61-
end
62-
end
63-
end
78+
raise ArgumentError, 'Tried to filter for declared parameters but none exist.' unless declared_params
79+
declared_params
6480
end
6581
end
6682

spec/grape/endpoint_spec.rb

Lines changed: 42 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -251,16 +251,34 @@ def app
251251

252252
describe '#declared' do
253253
before do
254+
subject.format :json
254255
subject.params do
255256
requires :first
256257
optional :second
257258
optional :third, default: 'third-default'
258259
optional :nested, type: Hash do
259260
optional :fourth
261+
optional :fifth
262+
optional :nested_two, type: Hash do
263+
optional :sixth
264+
optional :nested_three, type: Hash do
265+
optional :seventh
266+
end
267+
end
260268
end
261269
end
262270
end
263271

272+
it 'should show nil for nested params if include_missing is true' do
273+
subject.get '/declared' do
274+
declared(params, include_missing: true)
275+
end
276+
277+
get '/declared?first=present'
278+
expect(last_response.status).to eq(200)
279+
expect(JSON.parse(last_response.body)['nested']['fourth']).to be_nil
280+
end
281+
264282
it 'does not work in a before filter' do
265283
subject.before do
266284
declared(params)
@@ -273,37 +291,31 @@ def app
273291
end
274292

275293
it 'has as many keys as there are declared params' do
276-
inner_params = nil
277294
subject.get '/declared' do
278-
inner_params = declared(params).keys
279-
''
295+
declared(params)
280296
end
281297
get '/declared?first=present'
282298
expect(last_response.status).to eq(200)
283-
expect(inner_params.size).to eq(4)
299+
expect(JSON.parse(last_response.body).keys.size).to eq(4)
284300
end
285301

286302
it 'has a optional param with default value all the time' do
287-
inner_params = nil
288303
subject.get '/declared' do
289-
inner_params = declared(params)
290-
''
304+
declared(params)
291305
end
292306
get '/declared?first=one'
293307
expect(last_response.status).to eq(200)
294-
expect(inner_params[:third]).to eql('third-default')
308+
expect(JSON.parse(last_response.body)['third']).to eql('third-default')
295309
end
296310

297311
it 'builds nested params' do
298-
inner_params = nil
299312
subject.get '/declared' do
300-
inner_params = declared(params)
301-
''
313+
declared(params)
302314
end
303315

304316
get '/declared?first=present&nested[fourth]=1'
305317
expect(last_response.status).to eq(200)
306-
expect(inner_params[:nested].keys.size).to eq 1
318+
expect(JSON.parse(last_response.body)['nested'].keys.size).to eq 3
307319
end
308320

309321
it 'builds nested params when given array' do
@@ -317,76 +329,54 @@ def app
317329
optional :fourth
318330
end
319331
end
320-
inner_params = nil
321332
subject.get '/declared' do
322-
inner_params = declared(params)
323-
''
333+
declared(params)
324334
end
325335

326336
get '/declared?first=present&nested[][fourth]=1&nested[][fourth]=2'
327337
expect(last_response.status).to eq(200)
328-
expect(inner_params[:nested].size).to eq 2
338+
expect(JSON.parse(last_response.body)['nested'].size).to eq 2
329339
end
330340

331-
it 'builds nested params' do
332-
inner_params = nil
333-
subject.get '/declared' do
334-
inner_params = declared(params)
335-
''
336-
end
337-
338-
get '/declared?first=present&nested[fourth]=1'
339-
expect(last_response.status).to eq(200)
340-
expect(inner_params[:nested].keys.size).to eq 1
341-
end
342-
343-
context 'sets nested array when the param is missing' do
341+
context 'sets nested hash when the param is missing' do
344342
it 'to be array when include_missing is true' do
345-
inner_params = nil
346343
subject.get '/declared' do
347-
inner_params = declared(params, include_missing: true)
348-
''
344+
declared(params, include_missing: true)
349345
end
350346

351347
get '/declared?first=present'
352348
expect(last_response.status).to eq(200)
353-
expect(inner_params[:nested]).to be_a(Array)
349+
expect(JSON.parse(last_response.body)['nested']).to be_a(Hash)
354350
end
355351

356352
it 'to be nil when include_missing is false' do
357-
inner_params = nil
358353
subject.get '/declared' do
359-
inner_params = declared(params, include_missing: false)
360-
''
354+
declared(params, include_missing: false)
361355
end
362356

363357
get '/declared?first=present'
364358
expect(last_response.status).to eq(200)
365-
expect(inner_params[:nested]).to be_nil
359+
expect(JSON.parse(last_response.body)['nested']).to be_nil
366360
end
367361
end
368362

369363
it 'filters out any additional params that are given' do
370-
inner_params = nil
371364
subject.get '/declared' do
372-
inner_params = declared(params)
373-
''
365+
declared(params)
374366
end
375367
get '/declared?first=one&other=two'
376368
expect(last_response.status).to eq(200)
377-
expect(inner_params.key?(:other)).to eq false
369+
expect(JSON.parse(last_response.body).key?(:other)).to eq false
378370
end
379371

380372
it 'stringifies if that option is passed' do
381-
inner_params = nil
382373
subject.get '/declared' do
383-
inner_params = declared(params, stringify: true)
384-
''
374+
declared(params, stringify: true)
385375
end
386376

387377
get '/declared?first=one&other=two'
388378
expect(last_response.status).to eq(200)
389-
expect(inner_params['first']).to eq 'one'
379+
expect(JSON.parse(last_response.body)['first']).to eq 'one'
390380
end
391381

392382
it 'does not include missing attributes if that option is passed' do
@@ -447,20 +437,18 @@ def app
447437
end
448438
end
449439

450-
inner_params = nil
451440
subject.get '/declared' do
452-
inner_params = declared(params, include_missing: false)
453-
''
441+
declared(params, include_missing: false)
454442
end
455443

456444
get '/declared?first=present&nested[fourth]=&nested[nested_nested][sixth]=sixth'
457-
445+
json = JSON.parse(last_response.body)
458446
expect(last_response.status).to eq(200)
459-
expect(inner_params[:first]).to eq 'present'
460-
expect(inner_params[:nested].keys).to eq %w(fourth fifth nested_nested)
461-
expect(inner_params[:nested][:fourth]).to eq ''
462-
expect(inner_params[:nested][:nested_nested].keys).to eq %w(sixth seven)
463-
expect(inner_params[:nested][:nested_nested][:sixth]).to eq 'sixth'
447+
expect(json['first']).to eq 'present'
448+
expect(json['nested'].keys).to eq %w(fourth fifth nested_nested)
449+
expect(json['nested']['fourth']).to eq ''
450+
expect(json['nested']['nested_nested'].keys).to eq %w(sixth seven)
451+
expect(json['nested']['nested_nested']['sixth']).to eq 'sixth'
464452
end
465453
end
466454

0 commit comments

Comments
 (0)