Array of hashes param not being parsed correctly in a spec
It might be a Rack issue, but:
before do
subject.params do
requires :contacts, type: Array do
optional :name
optional :phone
optional :email
end
end
subject.before_validation do
pp form_vars: env['rack.request.form_vars'], form_hash: env['rack.request.form_hash']
pp before: params.to_h
end
subject.post '/post' do
end
end
it 'posts an array of hashes' do
post '/post', contacts: [
{ name: 'John', phone: '123' },
{ email: '[email protected]' },
{ name: 'Doe', email: '[email protected]' },
]
end
Output is:
{:form_vars=>
"contacts[][name]=John&contacts[][phone]=123&contacts[][email]=a%40mail.com&contacts[][name]=Doe&contacts[][email]=b%40mail.com",
:form_hash=>
{"contacts"=>
[{"name"=>"John", "phone"=>"123", "email"=>"[email protected]"},
{"name"=>"Doe", "email"=>"[email protected]"}]}}
{:before=>
{"contacts"=>
[{"name"=>"John", "phone"=>"123", "email"=>"[email protected]"},
{"name"=>"Doe", "email"=>"[email protected]"}]}}
Rack(?) couldn't infer where each element starts so it combined both first items.
For a workaround I can send a Hash {0=>{name: ..., email:}, 1=>{email: ...}} and before validation do:
params[:contacts] = params[:contacts].values if params[:contacts].is_a?(Hash)
Any other suggestions?
This looks legit, but I am not sure how to fix it. Arrays of hashes is not exactly something you can express in a query string.
For something like this I would POST JSON.
The client sends JSON body so I'm covered there.
Maybe, if a param is defined as a type: Array and it looks like a hash with numeric keys it should convert automatically to an array of the values?
I understand why Rack does what it does, the only solution I see is not using key-less params (as in contacts[] but contacts[0]).
I would just let this go, but it's me :)
@elado I would use a midlleware to convert hashes with numeric keys to array. For example the similar is already implemented here (spec)
@dm1try thanks!
Having the same problem now..
My HTTP post request has to pass in form data that looks like this:
listings_attributes[0][count] = 1
listings_attributes[1][name] = "Ipsum"
listings_attributes[1][count] = 2
listings_attributes[2][name] = "Dolor"
listings_attributes[2][count] = 3
This is represented as a ruby hash with numeric keys. Is there a way to declare the params so that the presence and type validations apply in my endpoint?
Tried the following param declaration
requires :listings_attributes, type: Array do
optional :name, type: String
optional :count, type: Integer
end
but this results in
(Grape::Exceptions::ValidationErrors) - listings_attributes is invalid
@martinverdejo post a repro, and see my comment