提交 f72f743d 编写于 作者: G Gannon McGibbon

Add scale support to ActiveRecord::Validations::NumericalityValidator

上级 46285183
......@@ -22,7 +22,7 @@ def check_validity!
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 @@ def validate_each(record, attr_name, value, precision: Float::DIG)
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 @@ def validate_each(record, attr_name, value, precision: Float::DIG)
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 @@ def validate_each(record, attr_name, value, precision: Float::DIG)
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 @@ def validate_each(record, attr_name, value, precision: Float::DIG)
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
......
* 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 @@ def setup
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 @@ def test_column_with_precision_higher_than_double_fig
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 @@ def self.name
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 @@
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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册