rails: param data types should not be converted to strings in rails 5
Steps to reproduce
During a test, my parameters being sent to the controller method are
patch :update, params: params
example params below:
{:id=>10848000,
:save_and_confirm=>false,
:confirmation=>
{:confirmer_email=>"test@example.com",
:confirmer_first_name=>"First Name",
:confirmer_last_name=>"Last Name",
:hide_confirmer_name=>false,
}
}
Inside the controller the params object is represented as below
<ActionController::Parameters {"confirmation"=>{"confirmer_email"=>"test@example.com", "confirmer_first_name"=>"First Name", "confirmer_last_name"=>"Last Name",
"hide_confirmer_name"=>"false"}, "save_and_confirm"=>"false", "id"=>"10848000", "controller"=>"confirmations", "action"=>"update"} permitted: false>
Notice how all of the values are now strings instead of their respective data typs (Booleans, integers).
This makes expressions like if params[:save_and_confirm]
always run even when the value is 'false'
.
Expected behavior
The expected behavior should be that all values passed in retain their data types and not be converted to a string. Rails 4.2.7 works like this.
So the params object should look like:
<ActionController::Parameters {"confirmation"=>{"confirmer_email"=>"test@example.com", "confirmer_first_name"=>"First Name", "confirmer_last_name"=>"Last Name",
"hide_confirmer_name"=>false}, "save_and_confirm"=>false, "id"=>10848000, "controller"=>"confirmations", "action"=>"update"} permitted: false>
Actual behavior
All parameters are converted to a string.
System configuration
5.0:
2.3.1:
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 44
- Comments: 16 (12 by maintainers)
Commits related to this issue
- Changes params checking in works controller to reflect new methods of changing params https://github.com/rails/rails/issues/26075 — committed to littlelines/otwarchive by deleted user 7 years ago
- AO3-5033 Rails 5.0 upgrade (#2958) * Updates to Rails 5.0.3 and clears up all dependency conflicts * Removes test_after_commit gem - now available in Rails by default Comments out rpm_contrib -... — committed to otwcode/otwarchive by deleted user 7 years ago
- AO3-5034 Rails 5 dot 1 upgrade (#2980) * Updates to Rails 5.0.3 and clears up all dependency conflicts * Removes test_after_commit gem - now available in Rails by default Comments out rpm_contr... — committed to otwcode/otwarchive by deleted user 7 years ago
@att14 your problem isn’t the same as @logicminds - it’s the problem in #26212 that @matthewd linked to and the TL;DR is using
as: :json
will work in Rails 5.0.1. In the meantime you can manually setrequest.content_type = 'application/json'
in your test.This is frustrating. I’m having an issue on a legacy Rails 4.2.10 app and its converting my test param hashes into a structure that I know is not happening in the production system. I copied real life params off of my staging server and am copying them into my test suite.
Why doesn’t this go into my request as is? This seems silly. All of my Integers are converted into strings!
When I
pry
into my controller inside of the test I wrote, I see the following:Grrrrr… 😡
This is frustrating because this isn’t how the params are being received in the Production app. So the assertions above about making a test case behave more like a real production app is not true. At least in my case its not.
@jrochkind I had wondered the same and remembered I saw the Rails code that handled the
as: :json
andformat: :json
parts. Basicallyas: :json
does the same as format but also sets the content type: https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/test_case.rb#L480See also #610, a version of this bug that was in rails 3. I mention to link the two issues, in case anyone ends up there by googling.
as: :json
seems to fix the problem in Rails 5.0.1. I completely don’t understand whatas: :json
is doing exactly vsformat: :json
, if anyone wants to link to relevant docs here in this issue it would probably help others who end up here.I’d consider this a bug fix more than anything less. That your controller tests convert params to strings is a good thing, it reflects what happens in development and production.
Try sending a request to your controller with
curl
then you should see those “boolean” fields look a lot like strings 😁Action Controller has never converted string types to anything else, however, Active Record does accept strings and will make sure to type cast those params. So if all you’re doing is
@post.update(post_params)
your controller will be fine. However, if you do:You will always be entering a world of pain, because the param won’t be an integer it will be a string. Hope that helps!
This doesn’t appear to respect the
format
argument anymore. I’ve tried both:I am using
rspec-rails
, but these methods should still come fromActionController::TestCase::Behavior
. Am I missing something?So it turns out the reason it works in Rails 4.2 is that
to_param
forTrueClass
andFalseClass
return self for reason that aren’t clear (original commit: b20c575). In Rails 5.0 we now actually convert the params into a query string and pass them through the request object so they reflect reality. It wasn’t documented because we weren’t aware of there being a change and the original intention was to make a request look as much like a real request as possible. SinceNilClass#to_param
also returns self I suspect they will also be different.We could add a line to the upgrading guide but since an app will already be broken because it’s assuming non-string values I doubt it’ll help anyone.
@logicminds is this a controller or integration test?
99% sure that 4.2.7 would’ve converted booleans to strings as well in controller tests since I worked on the code. Fairly sure the same applies to integration tests too but I could be wrong since I don’t remember working on such a feature at any point.
The fact that your tests convert booleans to strings is a good thing - trust me 😄
The reason being that the requests you get for real from browsers will all be strings so relying on tests returning boolean values won’t accurately reflect reality.
Here’s the permalink to the code in @javierjulio 's comment:
https://github.com/rails/rails/blob/69f976b859cae7f9d050152103da018b7f5dda6d/actionpack/lib/action_controller/test_case.rb#L483-L486
https://github.com/rails/rails/pull/26212
This is a controller test using
ActionController::TestCase
. Where is this change documented? I don’t see anybody talking about it on the web. And its a big gotcha when conditional logic is performed on what would have been a boolean value.So I assume in earlier versions of rails these params were converted to their respective data types automatically and now we must convert manually. When doing mass assignments are the values converted automatically?