mirror of https://github.com/rails/rails
Merge pull request #38212 from gmcgibbon/numericality_validation_with_scale
Add scale support to ActiveRecord::Validations::NumericalityValidator
This commit is contained in:
commit
8c71793b3b
|
@ -22,7 +22,7 @@ module ActiveModel
|
|||
end
|
||||
end
|
||||
|
||||
def validate_each(record, attr_name, value, precision: Float::DIG)
|
||||
def validate_each(record, attr_name, value, precision: Float::DIG, scale: nil)
|
||||
came_from_user = :"#{attr_name}_came_from_user?"
|
||||
|
||||
if record.respond_to?(came_from_user)
|
||||
|
@ -43,7 +43,7 @@ module ActiveModel
|
|||
raw_value = value
|
||||
end
|
||||
|
||||
unless is_number?(raw_value, precision)
|
||||
unless is_number?(raw_value, precision, scale)
|
||||
record.errors.add(attr_name, :not_a_number, **filtered_options(raw_value))
|
||||
return
|
||||
end
|
||||
|
@ -53,7 +53,7 @@ module ActiveModel
|
|||
return
|
||||
end
|
||||
|
||||
value = parse_as_number(raw_value, precision)
|
||||
value = parse_as_number(raw_value, precision, scale)
|
||||
|
||||
options.slice(*CHECKS.keys).each do |option, option_value|
|
||||
case option
|
||||
|
@ -69,7 +69,7 @@ module ActiveModel
|
|||
option_value = record.send(option_value)
|
||||
end
|
||||
|
||||
option_value = parse_as_number(option_value, precision)
|
||||
option_value = parse_as_number(option_value, precision, scale)
|
||||
|
||||
unless value.send(CHECKS[option], option_value)
|
||||
record.errors.add(attr_name, option, **filtered_options(value).merge!(count: option_value))
|
||||
|
@ -79,20 +79,24 @@ module ActiveModel
|
|||
end
|
||||
|
||||
private
|
||||
def parse_as_number(raw_value, precision)
|
||||
def parse_as_number(raw_value, precision, scale)
|
||||
if raw_value.is_a?(Float)
|
||||
raw_value.to_d(precision)
|
||||
parse_float(raw_value, precision, scale)
|
||||
elsif raw_value.is_a?(Numeric)
|
||||
raw_value
|
||||
elsif is_integer?(raw_value)
|
||||
raw_value.to_i
|
||||
elsif !is_hexadecimal_literal?(raw_value)
|
||||
Kernel.Float(raw_value).to_d(precision)
|
||||
parse_float(Kernel.Float(raw_value), precision, scale)
|
||||
end
|
||||
end
|
||||
|
||||
def is_number?(raw_value, precision)
|
||||
!parse_as_number(raw_value, precision).nil?
|
||||
def parse_float(raw_value, precision, scale)
|
||||
(scale ? raw_value.truncate(scale) : raw_value).to_d(precision)
|
||||
end
|
||||
|
||||
def is_number?(raw_value, precision, scale)
|
||||
!parse_as_number(raw_value, precision, scale).nil?
|
||||
rescue ArgumentError, TypeError
|
||||
false
|
||||
end
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
* Add scale support to `ActiveRecord::Validations::NumericalityValidator`.
|
||||
|
||||
*Gannon McGibbon*
|
||||
|
||||
* Find orphans by looking for missing relations through chaining `where.missing`:
|
||||
|
||||
Before:
|
||||
|
|
|
@ -3,15 +3,20 @@
|
|||
module ActiveRecord
|
||||
module Validations
|
||||
class NumericalityValidator < ActiveModel::Validations::NumericalityValidator # :nodoc:
|
||||
def validate_each(record, attribute, value, precision: nil)
|
||||
precision = [column_precision_for(attribute, record) || BigDecimal.double_fig, BigDecimal.double_fig].min
|
||||
super
|
||||
def validate_each(record, attribute, value, precision: nil, scale: nil)
|
||||
precision = [column_precision_for(record, attribute) || BigDecimal.double_fig, BigDecimal.double_fig].min
|
||||
scale = column_scale_for(record, attribute)
|
||||
super(record, attribute, value, precision: precision, scale: scale)
|
||||
end
|
||||
|
||||
private
|
||||
def column_precision_for(attribute, record)
|
||||
def column_precision_for(record, attribute)
|
||||
record.class.type_for_attribute(attribute.to_s)&.precision
|
||||
end
|
||||
|
||||
def column_scale_for(record, attribute)
|
||||
record.class.type_for_attribute(attribute.to_s)&.scale
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
|
|
@ -12,10 +12,10 @@ class NumericalityValidationTest < ActiveRecord::TestCase
|
|||
|
||||
def test_column_with_precision
|
||||
model_class.validates_numericality_of(
|
||||
:bank_balance, equal_to: 10_000_000.12
|
||||
:unscaled_bank_balance, equal_to: 10_000_000.12
|
||||
)
|
||||
|
||||
subject = model_class.new(bank_balance: 10_000_000.121)
|
||||
subject = model_class.new(unscaled_bank_balance: 10_000_000.121)
|
||||
|
||||
assert_predicate subject, :valid?
|
||||
end
|
||||
|
@ -30,6 +30,16 @@ class NumericalityValidationTest < ActiveRecord::TestCase
|
|||
assert_predicate subject, :valid?
|
||||
end
|
||||
|
||||
def test_column_with_scale
|
||||
model_class.validates_numericality_of(
|
||||
:bank_balance, greater_than: 10
|
||||
)
|
||||
|
||||
subject = model_class.new(bank_balance: 10.001)
|
||||
|
||||
assert_not_predicate subject, :valid?
|
||||
end
|
||||
|
||||
def test_no_column_precision
|
||||
model_class.validates_numericality_of(
|
||||
:decimal_number, equal_to: 1_000_000_000.123454
|
||||
|
@ -70,4 +80,26 @@ class NumericalityValidationTest < ActiveRecord::TestCase
|
|||
|
||||
assert_predicate(subject, :valid?)
|
||||
end
|
||||
|
||||
def test_virtual_attribute_with_precision
|
||||
model_class.attribute(:virtual_decimal_number, :decimal, precision: 5)
|
||||
model_class.validates_numericality_of(
|
||||
:virtual_decimal_number, equal_to: 123.45
|
||||
)
|
||||
|
||||
subject = model_class.new(virtual_decimal_number: 123.455)
|
||||
|
||||
assert_predicate subject, :valid?
|
||||
end
|
||||
|
||||
def test_virtual_attribute_with_scale
|
||||
model_class.attribute(:virtual_decimal_number, :decimal, scale: 2)
|
||||
model_class.validates_numericality_of(
|
||||
:virtual_decimal_number, greater_than: 1
|
||||
)
|
||||
|
||||
subject = model_class.new(virtual_decimal_number: 1.001)
|
||||
|
||||
assert_not_predicate subject, :valid?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -580,6 +580,7 @@ ActiveRecord::Schema.define do
|
|||
create_table :numeric_data, force: true do |t|
|
||||
t.decimal :bank_balance, precision: 10, scale: 2
|
||||
t.decimal :big_bank_balance, precision: 15, scale: 2
|
||||
t.decimal :unscaled_bank_balance, precision: 10
|
||||
t.decimal :world_population, precision: 20, scale: 0
|
||||
t.decimal :my_house_population, precision: 2, scale: 0
|
||||
t.decimal :decimal_number
|
||||
|
|
Loading…
Reference in New Issue