Skip to content

fix include_missing for nested hash #816

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 21, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* [#799](https://github.com/intridea/grape/pull/799): Fixed custom validators with required `Hash`, `Array` types - [@bwalex](https://github.com/bwalex).
* [#784](https://github.com/intridea/grape/pull/784): Fixed `present` to not overwrite the previously added contents of the response body whebn called more than once - [@mfunaro](https://github.com/mfunaro).
* [#809](https://github.com/intridea/grape/pull/809): Removed automatic `(.:format)` suffix on paths if you're using only one format (e.g., with `format :json`, `/path` will respond with JSON but `/path.xml` will be a 404) - [@ajvondrak](https://github.com/ajvondrak).
* [#816](https://github.com/intridea/grape/pull/816): Added ability to filter out missing params if params is a nested hash with `declared(params, include_missing: false)` - [@georgimitev](https://github.com/georgimitev).
* Your contribution here.

0.9.0 (8/27/2014)
Expand Down
172 changes: 172 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
- [Param](#param)
- [Describing Methods](#describing-methods)
- [Parameters](#parameters)
- [Declared](#declared)
- [Include Missing](#include-missing)
- [Parameter Validation and Coercion](#parameter-validation-and-coercion)
- [Built-in Validators](#built-in-validators)
- [Namespace Validation and Coercion](#namespace-validation-and-coercion)
Expand Down Expand Up @@ -461,6 +463,176 @@ In the case of conflict between either of:

route string parameters will have precedence.

#### Declared

Grape allows you to access only the parameters that have been declared by your `params` block. It filters out the params that have been passed, but are not allowed. Let's have the following api:

````ruby
format :json

post 'users/signup' do
{ "declared_params" => declared(params) }
end
````

If we do not specify any params, declared will return an empty hash.

**Request**

````bash
curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{"user": {"first_name":"first name", "last_name": "last name"}}'
````

**Response**

````json
{
"declared_params": {}
}

````

Once we add parameters requirements, grape will start returning only the declared params.

````ruby
format :json

params do
requires :user, type: Hash do
requires :first_name, type: String
requires :last_name, type: String
end
end

post 'users/signup' do
{ "declared_params" => declared(params) }
end
````

**Request**

````bash
curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{"user": {"first_name":"first name", "last_name": "last name", "random": "never shown"}}'
````

**Response**

````json
{
"declared_params": {
"user": {
"first_name": "first name",
"last_name": "last name"
}
}
}
````

#### Include missing

By default `declared(params)` returns parameters that has `nil` value. If you want to return only the parameters that have any value, you can use the `include_missing` option. By default it is `true`. Let's have the following api:

````ruby
format :json

params do
requires :first_name, type: String
optional :last_name, type: String
end

post 'users/signup' do
{ "declared_params" => declared(params, include_missing: false) }
end
````

**Request**

````bash
curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{"user": {"first_name":"first name", "random": "never shown"}}'
````

**Response with include_missing:false**

````json
{
"declared_params": {
"user": {
"first_name": "first name"
}
}
}
````

**Response with include_missing:true**

````json
{
"declared_params": {
"first_name": "first name",
"last_name": null
}
}
````

It also works on nested hashes:

````ruby
format :json

params do
requires :user, :type => Hash do
requires :first_name, type: String
optional :last_name, type: String
requires :address, :type => Hash do
requires :city, type: String
optional :region, type: String
end
end
end

post 'users/signup' do
{ "declared_params" => declared(params, include_missing: false) }
end
````

**Request**

````bash
curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{"user": {"first_name":"first name", "random": "never shown", "address": { "city": "SF"}}}'
````

**Response with include_missing:false**

````json
{
"declared_params": {
"user": {
"first_name": "first name",
"address": {
"city": "SF"
}
}
}
}
````

**Response with include_missing:true**

````json
{
"declared_params": {
"user": {
"first_name": "first name",
"last_name": null,
"address": {
"city": "Zurich",
"region": null
}
}
}
}
````

## Parameter Validation and Coercion

You can define validations and coercion options for your parameters using a `params` block.
Expand Down
3 changes: 3 additions & 0 deletions lib/grape/dsl/inside_route.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ def declared(params, options = {}, declared_params = nil)

key.each_pair do |parent, children|
output_key = options[:stringify] ? parent.to_s : parent.to_sym

next unless options[:include_missing] || children || params[parent]

if params.key?(parent) || options[:include_missing]
hash[output_key] = if children
declared(params[parent] || {}, options, Array(children))
Expand Down
34 changes: 34 additions & 0 deletions spec/grape/endpoint_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,40 @@ def app
get '/declared?first=one&other=two'
expect(last_response.status).to eq(200)
end

it 'does not include missing attributes when there are nested hashes' do
subject.get '/dummy' do
end

subject.params do
requires :first
optional :second
optional :third, default: nil
optional :nested, type: Hash do
optional :fourth, default: nil
optional :fifth, default: nil
requires :nested_nested, type: Hash do
optional :sixth, default: 'sixth-default'
optional :seven, default: nil
end
end
end

inner_params = nil
subject.get '/declared' do
inner_params = declared(params, include_missing: false)
""
end

get '/declared?first=present&nested[fourth]=&nested[nested_nested][sixth]=sixth'

expect(last_response.status).to eq(200)
expect(inner_params[:first]).to eq "present"
expect(inner_params[:nested].keys).to eq [:fourth, :nested_nested]
expect(inner_params[:nested][:fourth]).to eq ""
expect(inner_params[:nested][:nested_nested].keys).to eq [:sixth]
expect(inner_params[:nested][:nested_nested][:sixth]).to eq "sixth"
end
end

describe '#declared; call from child namespace' do
Expand Down