提交 704c6585 编写于 作者: S Sean Griffin

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
上级 8226bad7
......@@ -23,6 +23,10 @@ def validate_each(record, attr_name, value)
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 @@ def allow_only_integer?(record)
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 @@ def changes
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 @@ def attributes_changed_in_place
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 @@ def test_validators
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
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册