Fix `read_attribute_before_type_cast` to consider attribute aliases

Numericality validations for aliased attributes are not able
to get the value of the attribute before type cast because
activerecord was trying to get the value of the attribute based
on attribute alias name and not the original attribute name.

Example of validation which would pass even if a invalid value
would be provided

    class MyModel < ActiveRecord::Base
      validates :aliased_balance, numericality: { greater_than_or_equal_to: 0 }
    end

If we instantiate MyModel like bellow it will be valid because
when numericality validation runs it will not be able to get the
value before type cast, so it uses the type casted value
which will be `0.0` and the validation will match.

    subject = MyModel.new(aliased_balance: "abcd")
    subject.valid?

But if we declare MyModel like this

    class MyModel < ActiveRecord::Base
      validates :balance, numericality: { greater_than_or_equal_to: 0 }
    end

and assign "abcd" value to `balance` when the validations
run the model will be invalid because activerecord will be able
to get the value before type cast.

With this change `read_attribute_before_type_cast` will be able to
get the value before type cast even when the attr_name is an
attribute_alias.
This commit is contained in:
Marcelo Lauxen 2020-10-15 09:06:44 -03:00
parent 28a4d97331
commit 5fdc7d385f
5 changed files with 24 additions and 1 deletions

View File

@ -1,3 +1,7 @@
* Fix `read_attribute_before_type_cast` to consider attribute aliases.
*Marcelo Lauxen*
* Support passing record to uniqueness validator `:conditions` callable:
```ruby

View File

@ -46,7 +46,10 @@ module ActiveRecord
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
# task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
def read_attribute_before_type_cast(attr_name)
attribute_before_type_cast(attr_name.to_s)
name = attr_name.to_s
name = self.class.attribute_aliases[name] || name
attribute_before_type_cast(name)
end
# Returns a hash of attributes before typecasting and deserialization.

View File

@ -12,6 +12,7 @@ require "models/category"
require "models/reply"
require "models/contact"
require "models/keyboard"
require "models/numeric_data"
class AttributeMethodsTest < ActiveRecord::TestCase
include InTimeZone
@ -1087,6 +1088,11 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "Topic::GeneratedAttributeMethods", mod.inspect
end
test "read_attribute_before_type_cast with aliased attribute" do
model = NumericData.new(new_bank_balance: "abcd")
assert_equal "abcd", model.read_attribute_before_type_cast("new_bank_balance")
end
private
def new_topic_like_ar_class(&block)
klass = Class.new(ActiveRecord::Base) do

View File

@ -102,4 +102,12 @@ class NumericalityValidationTest < ActiveRecord::TestCase
assert_not_predicate subject, :valid?
end
def test_aliased_attribute
model_class.validates_numericality_of(:new_bank_balance, greater_or_equal_than: 0)
subject = model_class.new(new_bank_balance: "abcd")
assert_not_predicate subject, :valid?
end
end

View File

@ -7,4 +7,6 @@ class NumericData < ActiveRecord::Base
attribute :world_population, :big_integer
attribute :my_house_population, :big_integer
attribute :atoms_in_universe, :big_integer
alias_attribute :new_bank_balance, :bank_balance
end