-
Notifications
You must be signed in to change notification settings - Fork 21.8k
Raising an error when nil is passed to update_attributes. #9860
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
Raising an error when nil is passed to update_attributes. #9860
Conversation
return if new_attributes.blank? | ||
if !new_attributes | ||
raise ArgumentError, "Must pass a non-nil hash as an argument when assigning attributes." | ||
elsif new_attributes.empty? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this really necessary ? The behaviour would be the same without the elsif clause.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It wouldn't: see the early return
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
He's saying the behavior would be the same because returning vs. not returning has the same result when new_attributes
is empty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly @mhuggins, the attributes won't be changed wether or not you return early.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a personnal point of view, but something like
raise ArgumentError, "Must pass a non-nil hash as an argument when assigning attributes." if new_attributes.nil?
is better looking to me than the if / elsif branch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Intrepidd @mhuggins It actually does change the behavior. This is because if an empty string is passed asnew_attributes
then the call to stringify_keys
will raise a NoMethodError
.
See the following test: https://github.com/rails/rails/blob/master/activerecord/test/cases/attribute_methods_test.rb#L85-88
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would someone pass an empty string to this method? The documentation makes it clear that a hash is expected, and I can't think of a scenario where someone would even consider passing a string. The nil check I can understand if a controller param isn't passed by the user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's true. I think this test is outdated. It was originally added here: 786342e. Which brings me to a second discussion, should we be raising an ArgumentError when the input is not a hash?
It seems like this second check will ensure that all arguments are correct and valid, and that stringify_keys
will never throw a NoMethodError if we are sure that a new_attributes
is a hash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the documentation clearly states that the parameter must be a hash, I
don't think a check should be done on the type of the parameter.
Thousands of methods in rails expect hashes without raising an exception if
the parameter is not a hash.
I even think that throwing a NoMethodError when nil is passed might be a
good thing.
Adrien Siami
On Mar 22, 2013 12:33 AM, "John J. Wang" notifications@github.com wrote:
In activerecord/lib/active_record/attribute_assignment.rb:
@@ -12,7 +12,11 @@ module AttributeAssignment
# of this method is +false+ an ActiveModel::ForbiddenAttributesError
# exception is raised.
def assign_attributes(new_attributes)
return if new_attributes.blank?
if !new_attributes
raise ArgumentError, "Must pass a non-nil hash as an argument when assigning attributes."
elsif new_attributes.empty?
That's true. I think this test is outdated. It was originally added here:
786342ehttps://github.com/rails/rails/commit/786342e17f42799ef889cf6127fe97e9598272e0.
Which brings me to a second discussion, should we be raising an
ArgumentError when the input is not a hash?It seems like this second check will ensure that all arguments are correct
and valid, and that stringify_keys will never throw a NoMethodError if we
are sure that a new_attributes is a hash.—
Reply to this email directly or view it on GitHubhttps://github.com//pull/9860/files#r3482868
.
What if the users wants to pass a custom object that responds to stringify_keys ? I'm totally for the error on nil because it is common and this allows developers to know what the problem is real quick. However, I'm not fond of the racism concerning the type of the parameter ;) As I said in the outdated diff, everywhere in rails, hashes are expected without being type checked, that's the beauty of ruby, if you want strong type check, ruby is not the good solution I think :) What do you think of :
Please let me know what you think of this, I'm not an expert so feel free to point out things that I may have missed. |
@Intrepidd That's a pretty reasonable idea. I think checking for The PR has been updated. |
Thanks! Let's see what the core team thinks about it! Adrien Siami
|
@@ -12,7 +12,9 @@ module AttributeAssignment | |||
# of this method is +false+ an <tt>ActiveModel::ForbiddenAttributesError</tt> | |||
# exception is raised. | |||
def assign_attributes(new_attributes) | |||
return if new_attributes.blank? | |||
unless new_attributes && new_attributes.respond_to?(:stringify_keys) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I always get confused when I have to read an unless
with more than one condition. I'd like to write them using if
.
That might only be my taste though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that only checking for respond_to would be enough? We don't expect nils to respond to stringify_keys
I guess?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ya, I just noticed that after I implemented @senny's change to an if statement.
this looks good to me. /cc @rafaelfranca |
As this changes the functionality I think we should add a CHANGELOG entry. |
@@ -12,7 +12,9 @@ module AttributeAssignment | |||
# of this method is +false+ an <tt>ActiveModel::ForbiddenAttributesError</tt> | |||
# exception is raised. | |||
def assign_attributes(new_attributes) | |||
return if new_attributes.blank? | |||
unless new_attributes && new_attributes.respond_to?(:stringify_keys) | |||
raise ArgumentError, "Must pass a non-nil hash as an argument when assigning attributes." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's a non-nil hash
? :)
@senny Added a changelog entry and rebased. @carlosantoniodasilva thanks for the comments! |
@wangjohn this does no longer apply cleanly, can you push a rebased version? |
@senny I've just rebased and pushed. |
@rafaelfranca @carlosantoniodasilva can you take another look? |
Any updates on this PR? If not, I'll go ahead and close it. |
let's wait for more feedback. I like this change but I'm not sure if there are implications because it changes the public API. |
I feel conflicted about this one. If user is passing nil then obviously user does not want anything to happen. After this code now user has to do |
@neerajdotname but for the default case with strong parameters you already get |
@senny you might but ActiveRecord can be used without rest of rails goodies. Updated: replaced |
@neerajdotname sure, I guess it's a matter of taste. It's important that the default case does not get overcomplicated. That's why I mentioned strong params. Maybe we can get a word from @rafaelfranca and @jonleighton to wrap this PR up. |
@@ -1,3 +1,13 @@ | |||
* Calling ``update_attributes`` will now throw an ArgumentError whenever it | |||
gets a nil argument. More specifically, it will throw an error if the | |||
argument that it gets passed does not respond to to ``stringify_keys``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can use single backticks in the description here.
Please rebase from master. Thanks. |
@carlosantoniodasilva Rebased and changed the changelog. |
+1 It will make debugging a lot easier for the developer as typos will be caught easily. In previous tickets the core team has stated that this is the typo of thing Rails should cater to. The old behavior can be easily returnned by doing |
I'm fine with this change on master but I'd not put in a stable release. I'm merging. |
…_with_nil Raising an error when nil is passed to update_attributes. Conflicts: activerecord/CHANGELOG.md
Causes failures in Agile Web Development with Rails test. Minimal test case: http://intertwingly.net/tmp/update_attributes.html |
@rubys The problem with the failures is that I removed the line The simplest fix would be to just add this line back in. I'll make a PR to do this. |
In 9e4b715, @josevalim allowed the ability to send a nil argument to
update_attributes
(in response to #478). However, the reasoning behind that was because a pretty cryptic error was thrown. The current behavior in Rails is thatthing.update_attributes(nil)
will return and not update any attributes without throwing any error.However, Instead of allowing you to send a nil argument, it might be better to raise an
ArgumentError
which has a clear error message. I've made that change here, but this PR is more intended as a discussion on which behavior is better.Closes #9858.