Skip to content

Commit

Permalink
Merge pull request #816 from georgimitev/fix_nested_hash_include_missing
Browse files Browse the repository at this point in the history
fix include_missing for nested hash
  • Loading branch information
dblock committed Nov 21, 2014
2 parents 31b9b01 + be98595 commit 15fd71e
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,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

0 comments on commit 15fd71e

Please sign in to comment.