My model has a decimal amount attribute.
create_table :foos do |t|
t.decimal :amount
end
class Foo < ApplicationRecord
end
I always want the amount to be negative, so I add a normalisation:
class Foo < ApplicationRecord
normalizes :amount, with: -> amount { - amount.abs }
end
This seems to work perfectly.
Now, to be safe, I add a validation:
class Foo < ApplicationRecord
normalizes :amount, with: -> amount { - amount.abs }
validates :amount, numericality: {less_than: 0}
end
Now when I set the amount to a positive value, although the normalisation converts it to a negative value, the validator seems to think the value is still positive and adds a validation error.
foo = Foo.new amount: 4
foo.amount # => -4
foo.valid? # => false
foo.errors # => #<ActiveModel::Error attribute=amount, type=less_than, options={:value=>4, :count=>0}>
According to the tests for normalizes
, normalisation happens before validation.
How can I get this to work?
Numericality validator seems to be specifically using raw value for validation without taking normalization into account:
https://github.com/rails/rails/blob/v7.1.3/activemodel/lib/active_model/validations/numericality.rb#L129
if record.respond_to?(came_from_user)
if record.public_send(came_from_user)
raw_value = record.public_send(:"#{attr_name}_before_type_cast")
It needs to be this way because strings normalize to numbers ("foo".to_d # => 0.0
) so validation wouldn't work if it happened after normalization.
You could write your own validation to bypass this problem:
validate do
errors.add(:amount, :less_than, value: amount, count: 0) unless amount.negative?
end