Skip to content
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

Returns array for hash parameter when not present #1596

Closed
nicooga opened this issue Mar 21, 2017 · 5 comments
Closed

Returns array for hash parameter when not present #1596

nicooga opened this issue Mar 21, 2017 · 5 comments
Labels

Comments

@nicooga
Copy link

nicooga commented Mar 21, 2017

Using grape 0.19.1, also hapening in 0.19.0.

Whats happening

Given I declared a parameter key with type Hash and the parameter was not included when calling the controller, I't returns an empty array for that key.

Expected behavior

It should return an empty hash, or nil, or perhaps not include the key in the parsed output.

Extended Explanation

Given this parameters:

params do
  with coerce: String do
    optional :avatar
    optional :first_name
    optional :last_name
    optional :school_year
    optional :phone_number
    optional :age
    optional :description
  end

  optional :location_attributes, type: Hash, default: {} do
    optional :city
    optional :district
    optional :street
    optional :street_number
    optional :notes
  end
end

... when I hit the controller with this params:

{"user"=>
  {"id"=>288,
   "avatar"=>
    "http://localhost:3000/system/users/avatars/000/000/288/original/profile_pic.jpg?1489450290",
   "first_name"=>"Numbers",
   "last_name"=>"Terry",
   "age"=>nil,
   "description"=>nil,
   "school_year"=>nil,
   "phone_number"=>nil,
   "email"=>"teacher@gmail.com",
   "teaches_online"=>false,
   "teaches_at_own_place"=>false,
   "teaches_at_public_place"=>true,
   "teaches_at_students_place"=>false,
   "is_teacher"=>true,
   "is_student"=>false},
 "id"=>"288",
 "location_attributes"=>{}}

... and I extract the declared params like this:

attributes = declared params[:user]

the result is this:

attributes #=>
{"avatar"=>
  "http://localhost:3000/system/users/avatars/000/000/288/original/profile_pic.jpg?1489450290",
 "first_name"=>"Numbers",
 "last_name"=>"Terry",
 "school_year"=>nil,
 "phone_number"=>nil,
 "age"=>nil,
 "description"=>nil,
 "location_attributes"=>[]}

Note that location_attributes gets parsed into an empty array. I do not know exactly what is the expected behavior, but an empty array is not what I've expected. I'd either have nil or an empty hash. It breaks with activerecord's accepts_nested_attributes_for :location with:

NoMethodError (undefined method `with_indifferent_access' for #<Hashie::Array []>)

I am missing something obvious here?

@nicooga nicooga changed the title Returns array for hash parameter when not present. Returns array for hash parameter when not present Mar 21, 2017
@namusyaka
Copy link
Contributor

You should pass raw params into the #declared helper, and your params declaration should be surrounded by group :user, type: Hash.

@nicooga
Copy link
Author

nicooga commented Mar 21, 2017

Thanks for the quick response @namusyaka, using group solves the issue. As a result I get the location_attributes key with a value of "location_attributes"=>{"city"=>nil, "district"=>nil, "street"=>nil, "street_number"=>nil, "notes"=>nil}. I still need that default: {} to make this work, otherwise I get an empty array.

By the way, I was using required :user, type: Hash do ... before, with the same results.

params do
  group :user, type: Hash do
    # ...

    optional :location_attributes, type: Hash, default: {} do
      # ...
    end
  end
end

put do
  User.find(params.fetch(:id)).tap do |u|
    u.update! declared(params).fetch(:user)
  end
end

However, It's not clear why the change from using required to group changes the behavior of the default option in the DSL. You may want to take a look at this.

@dblock dblock added the bug? label Mar 21, 2017
@arempe93
Copy link
Contributor

arempe93 commented May 9, 2017

I encountered this issue today also on grape v0.17.0. If I hit the following api only supplying body:

params do
  requires :body, type: String
  optional :metadata, type: Hash do
    optional :media_type, type: Integer
  end
end
put do
  changed_attrs = declared(params).compact

  changed_attrs[:metadata] # => []
end

I would expect metadata to be nil so it would be filtered out of the changed attributes using Hash#compact. It being an Array is extra weird

@arempe93
Copy link
Contributor

arempe93 commented May 9, 2017

Just encountered another strange thing testing it, if I pass metadata as explicitly null, ie to clear it, such as this in rspec:

put "/api/notes/#{id}", body: 'foo', metadata: nil

Then the value in declared(params) is { :metadata => nil }, which is also kinda weird I think.

For reference

Given the following api

params do
  optional :metadata, type: Hash do
    optional :media_type, type: Integer
  end
end
put do
  present :params, params
  present :declared, declared(params)
end

When metadata omitted

put "/api/notes/#{id}"

Output

{
  params: { id: 53 },
  declared: { id: 53, metadata: [] }
}

When metadata sent as null

put "/api/notes/#{id}", metadata: nil

Output

{
  params: { id: 53, metadata: nil },
  declared: { id: 53, metadata: { media_type: nil } }
}

Desired behavior in both scenarios

Output

{
  declared: { id: 53, metadata: nil }
}

@dnesteryuk
Copy link
Member

@kadotami fixed this issue in #2043 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants