mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Ensure numericality validations work with mutation
The detection of in-place changes caused a weird unexpected issue with numericality validations. That validator (out of necessity) works on the `_before_type_cast` version of the attribute, since on an `:integer` type column, a non-numeric string would type cast to 0. However, strings are mutable, and we changed strings to ensure that the post type cast version of the attribute was a different instance than the before type cast version (so the mutation detection can work properly). Even though strings are the only mutable type for which a numericality validation makes sense, special casing strings would feel like a strange change to make here. Instead, we can make the assumption that for all mutable types, we should work on the post-type-cast version of the attribute, since all cases which would return 0 for non-numeric strings are immutable. Fixes #17852
This commit is contained in:
parent
8226bad710
commit
704c658531
3 changed files with 30 additions and 6 deletions
|
@ -23,6 +23,10 @@ module ActiveModel
|
|||
raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast)
|
||||
raw_value ||= value
|
||||
|
||||
if record_attribute_changed_in_place?(record, attr_name)
|
||||
raw_value = value
|
||||
end
|
||||
|
||||
return if options[:allow_nil] && raw_value.nil?
|
||||
|
||||
unless value = parse_raw_value_as_a_number(raw_value)
|
||||
|
@ -86,6 +90,13 @@ module ActiveModel
|
|||
options[:only_integer]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def record_attribute_changed_in_place?(record, attr_name)
|
||||
record.respond_to?(:attribute_changed_in_place?) &&
|
||||
record.attribute_changed_in_place?(attr_name.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
module HelperMethods
|
||||
|
|
|
@ -69,6 +69,11 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
def attribute_changed_in_place?(attr_name)
|
||||
old_value = original_raw_attribute(attr_name)
|
||||
@attributes[attr_name].changed_in_place_from?(old_value)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def calculate_changes_from_defaults
|
||||
|
@ -141,15 +146,10 @@ module ActiveRecord
|
|||
|
||||
def changed_in_place
|
||||
self.class.attribute_names.select do |attr_name|
|
||||
changed_in_place?(attr_name)
|
||||
attribute_changed_in_place?(attr_name)
|
||||
end
|
||||
end
|
||||
|
||||
def changed_in_place?(attr_name)
|
||||
old_value = original_raw_attribute(attr_name)
|
||||
@attributes[attr_name].changed_in_place_from?(old_value)
|
||||
end
|
||||
|
||||
def original_raw_attribute(attr_name)
|
||||
original_raw_attributes.fetch(attr_name) do
|
||||
read_attribute_before_type_cast(attr_name)
|
||||
|
|
|
@ -149,4 +149,17 @@ class ValidationsTest < ActiveRecord::TestCase
|
|||
assert_equal 1, Company.validators_on(:name).size
|
||||
end
|
||||
|
||||
def test_numericality_validation_with_mutation
|
||||
Topic.class_eval do
|
||||
attribute :wibble, ActiveRecord::Type::String.new
|
||||
validates_numericality_of :wibble, only_integer: true
|
||||
end
|
||||
|
||||
topic = Topic.new(wibble: '123-4567')
|
||||
topic.wibble.gsub!('-', '')
|
||||
|
||||
assert topic.valid?
|
||||
ensure
|
||||
Topic.reset_column_information
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue