class ActiveModel::Validations::NumericalityValidator
:nodoc:
def allow_only_integer?(record)
def allow_only_integer?(record) case options[:only_integer] when Symbol record.send(options[:only_integer]) when Proc options[:only_integer].call(record) else options[:only_integer] end end
def check_validity!
def check_validity! options.slice(*COMPARE_CHECKS.keys).each do |option, value| unless value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol) raise ArgumentError, ":#{option} must be a number, a symbol or a proc" end end options.slice(*RANGE_CHECKS.keys).each do |option, value| unless value.is_a?(Range) raise ArgumentError, ":#{option} must be a range" end end end
def filtered_options(value)
def filtered_options(value) filtered = options.except(*RESERVED_OPTIONS) filtered[:value] = value filtered end
def is_hexadecimal_literal?(raw_value)
def is_hexadecimal_literal?(raw_value) HEXADECIMAL_REGEX.match?(raw_value.to_s) end
def is_integer?(raw_value)
def is_integer?(raw_value) INTEGER_REGEX.match?(raw_value.to_s) end
def is_number?(raw_value, precision, scale)
def is_number?(raw_value, precision, scale) !parse_as_number(raw_value, precision, scale).nil? rescue ArgumentError, TypeError false end
def option_as_number(record, option_value, precision, scale)
def option_as_number(record, option_value, precision, scale) parse_as_number(option_value(record, option_value), precision, scale) end
def parse_as_number(raw_value, precision, scale)
def parse_as_number(raw_value, precision, scale) if raw_value.is_a?(Float) parse_float(raw_value, precision, scale) elsif raw_value.is_a?(BigDecimal) round(raw_value, scale) elsif raw_value.is_a?(Numeric) raw_value elsif is_integer?(raw_value) raw_value.to_i elsif !is_hexadecimal_literal?(raw_value) parse_float(Kernel.Float(raw_value), precision, scale) end end
def parse_float(raw_value, precision, scale)
def parse_float(raw_value, precision, scale) round(raw_value, scale).to_d(precision) end
def prepare_value_for_validation(value, record, attr_name)
def prepare_value_for_validation(value, record, attr_name) return value if record_attribute_changed_in_place?(record, attr_name) came_from_user = :"#{attr_name}_came_from_user?" if record.respond_to?(came_from_user) if record.public_send(came_from_user) raw_value = record.public_send(:"#{attr_name}_before_type_cast") elsif record.respond_to?(:read_attribute) raw_value = record.read_attribute(attr_name) end else before_type_cast = :"#{attr_name}_before_type_cast" if record.respond_to?(before_type_cast) raw_value = record.public_send(before_type_cast) end end raw_value || value end
def record_attribute_changed_in_place?(record, attr_name)
def record_attribute_changed_in_place?(record, attr_name) record.respond_to?(:attribute_changed_in_place?) && record.attribute_changed_in_place?(attr_name.to_s) end
def round(raw_value, scale)
def round(raw_value, scale) scale ? raw_value.round(scale) : raw_value end
def validate_each(record, attr_name, value, precision: Float::DIG, scale: nil)
def validate_each(record, attr_name, value, precision: Float::DIG, scale: nil) unless is_number?(value, precision, scale) record.errors.add(attr_name, :not_a_number, **filtered_options(value)) return end if allow_only_integer?(record) && !is_integer?(value) record.errors.add(attr_name, :not_an_integer, **filtered_options(value)) return end value = parse_as_number(value, precision, scale) options.slice(*RESERVED_OPTIONS).each do |option, option_value| if NUMBER_CHECKS.include?(option) unless value.to_i.public_send(NUMBER_CHECKS[option]) record.errors.add(attr_name, option, **filtered_options(value)) end elsif RANGE_CHECKS.include?(option) unless value.public_send(RANGE_CHECKS[option], option_value) record.errors.add(attr_name, option, **filtered_options(value).merge!(count: option_value)) end elsif COMPARE_CHECKS.include?(option) option_value = option_as_number(record, option_value, precision, scale) unless value.public_send(COMPARE_CHECKS[option], option_value) record.errors.add(attr_name, option, **filtered_options(value).merge!(count: option_value)) end end end end