-
Notifications
You must be signed in to change notification settings - Fork 1.4k
MONGOID-5842 validates_numericality_of is valid when allow_blank is true and string is assigned #6074
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
base: master
Are you sure you want to change the base?
Conversation
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.
Pull request overview
This PR addresses MONGOID-5842 by fixing validation behavior when validates_numericality_of is used with allow_blank: true and a non-numeric string is assigned to a numeric field. The fix ensures that non-numeric strings are properly validated as invalid, rather than being coerced to nil/0 before validation.
Key Changes
- Modified
read_attribute_for_validationto return the pre-type-cast value for BigDecimal, Float, and Integer fields - Added comprehensive test coverage for
allow_blank: truescenarios with both Integer and Float field types
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| lib/mongoid/validatable.rb | Added logic to return pre-type-cast values for numeric field types during validation |
| spec/mongoid/validatable/numericality_spec.rb | Added test cases for allow_blank: true validation behavior with Integer and Float fields |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
jamis
left a comment
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.
Very nice. Thank you for your work on this!
I know I suggested this solution, but I was thinking more about it this morning and I worry a bit about changing read_attribute_for_validation like this. It's a Rails API, not a Mongoid one, which means we don't have any control over how Rails decides to use it. A new version of Rails could easily call that method somewhere unexpected, and expect a numeric value to be returned for a numeric field. The default implementation of prepare_value_for_validation, for example, simply returns the value it is given.
At the core of the issue, and this incompatibility, is the fact that ActiveRecord returns 0, not nil, when a non-numeric value is set and allow_blank is true. It's also how Ruby itself works (e.g. "hello".to_i # => 0). I wonder if we ought to roll that change back, so that Mongoid mirrors this? The alternative is probably to subclass NumericalValidator and write our own #validate method, to accommodate the fact that Mongoid returns nil instead of 0, here.
We should discuss this as a team next week, but this is a very good example of what we're trying to do with the Mongoid X initiative: this used to work, and now it doesn't. Do we revert the change? Do we allow the change and find a way to work around it? Or do we say that the new way is correct and the old way is wrong, and we describe how people ought to update their apps to accommodate that?
So. Let's hold off on further work here until we know what we want to do. Thank you for your investigation on this!
I strongly, strongly vote against changing this. |
Co-authored-by: Jamis Buck <jamisbuck@gmail.com>
|
@johnnyshields, I agree that this conversion makes much more sense if it returns nil. Many programming languages even raise an error when such conversions are attempted, which is probably an even better solution (as it avoids any ambiguity). However, we are kind of bound by two primary constraints in Mongoid. The first is that the project explicitly tries to mimic ActiveRecord in order to maximize compatibility with the wider Rails ecosystem. If you have an ActiveRecord model with an integer field, and you don’t have any class Person < ActiveRecord::Base
# create_table :people do |t|
# t.string :name
# t.integer :age
# end
end
user = User.create!(name: 'Alice', age: 'hello')
p user.age # => 0The way to avoid this (as I'm sure you already know) is to validate the value first. If you use However, even if we decide that parity with ActiveRecord is undesirable in this case, we have an even stronger precedent: Ruby itself. The default method for converting strings to integers will cast any non-numeric string to 0. (Yes, you can use If we decide that all of this is still insufficient and we insist that we know best and that casting Is it worth it? I don’t think so. This is Mongoid, which intentionally is trying to behave like ActiveRecord. Let’s embrace that constraint, and embrace Rails’ (and Ruby’s) opinions. If we push back, it only makes more work for us and more code to maintain. For the sake of compatibility with Rails and its ecosystem, Mongoid’s numeric fields should be casting arbitrary strings to 0, instead of nil. |
|
@jamis Unlike ActiveRecord / SQL, MongoDB can in theory have any value in any field. So this is uncharted territory as far as AR is concerned--there is no "AR precedent" here. What Mongoid should do is in this proposal here: https://jira.mongodb.org/browse/MONGOID-5609. We should introduce a concept of But before introducing new breaking behaviors, I would really like to see the team first reduce all the breaks/changes from Mongoid 8/9...
This has never been a core philosophy of Mongoid, and if you go down this path, it will cause much heartbreak. Mongoid should not "embrace" ActiveRecord--we should keep it at arm's length. Use conventions from AR where it makes sense, but do not go out of our way to adopt AR conventions which deviate from Mongo-like norms, especially when it comes to flexible type handling (a core feature of MongoDB, not of SQL). Double-down on MongoDB, not on AR/SQL. |
|
@johnnyshields You're right about MongoDB. But this isn't about MongoDB; it's about Mongoid, and Mongoid is constrained as I described. If someone were to create an entirely new ODM, with no baggage from ActiveRecord (oh, glorious thought), the story would very definitely be different, on many fronts.
Funny you should say that. This ticket is actually one of the (large) backlog of tickets we're looking at for our "Mongoid X" initiative. It relates to a change in Mongoid 8 that broke existing behavior from Mongoid 7.5, which is why it is being looked at right now. |
No it is not, and it never has been. The worst mistakes of Mongoid, such as the and/or-query chaining debacle (before your time at MongoDB), all have arisen from trying to "embrace" ActiveRecord behavior. ("But this time is different" you will say...) Anyway, in absence of a strong reason to change this, let's please keep the status quo of returning |
Prior to this change, the validation for numericality was skipped if allow_blank was set to true and a string was provided. This happened because typecasting the string resulted in nil, which caused the validation check to be bypassed here. We have updated read_attribute_for_validation to return the original, non-typecasted value if typecasting to a numeric type fails.