Always assume strings with non-numeric characters change numeric types

We previously only did this if the old value was zero, to make sure
numericality validations run and failed if the user gave 'wibble' as the
value, which would be type cast to 0. However, numericality validations
will fail if there are any non-numeric characters in the string, so 5 ->
'5wibble' should also be marked as changed.
This commit is contained in:
Sean Griffin 2014-06-24 06:55:24 -06:00
parent 9ac1ce11ad
commit 50fa366783
3 changed files with 35 additions and 3 deletions

@ -1,3 +1,17 @@
* Assume numeric types have changed if they were assigned to a value that
would fail numericality validation, regardless of the old value. Previously
this would only occur if the old value was 0.
Example:
model = Model.create!(number: 5)
model.number = '5wibble'
model.number_changed? # => true
Fixes #14731.
*Sean Griffin*
* `reload` no longer merges with the existing attributes.
The attribute hash is fully replaced. The record is put into the same state
as it would be with `Model.find(model.id)`.

@ -16,13 +16,13 @@ def type_cast(value)
end
def changed?(old_value, _new_value, new_value_before_type_cast) # :nodoc:
super || zero_to_non_number?(old_value, new_value_before_type_cast)
super || number_to_non_number?(old_value, new_value_before_type_cast)
end
private
def zero_to_non_number?(old_value, new_value_before_type_cast)
old_value == 0 && non_numeric_string?(new_value_before_type_cast)
def number_to_non_number?(old_value, new_value_before_type_cast)
old_value != nil && non_numeric_string?(new_value_before_type_cast)
end
def non_numeric_string?(value)

@ -79,11 +79,29 @@ def test_type_cast_nan_and_infinity_to_integer
assert_nil type.type_cast_from_user(1.0/0.0)
end
def test_changing_integers
type = Type::Integer.new
assert type.changed?(5, 5, '5wibble')
assert_not type.changed?(5, 5, '5')
assert_not type.changed?(5, 5, '5.0')
assert_not type.changed?(nil, nil, nil)
end
def test_type_cast_float
type = Type::Float.new
assert_equal 1.0, type.type_cast_from_user("1")
end
def test_changing_float
type = Type::Float.new
assert type.changed?(5.0, 5.0, '5wibble')
assert_not type.changed?(5.0, 5.0, '5')
assert_not type.changed?(5.0, 5.0, '5.0')
assert_not type.changed?(nil, nil, nil)
end
def test_type_cast_decimal
type = Type::Decimal.new
assert_equal BigDecimal.new("0"), type.type_cast_from_user(BigDecimal.new("0"))