class MoneyRails::ActiveModel::MoneyValidator

Constants

DEFAULTS

Private Instance Methods

add_error!(record, attr, details) click to toggle source
# File lib/money-rails/active_model/validator.rb, line 78
def add_error!(record, attr, details)
  attr_name = attr.to_s.tr('.', '_').humanize
  attr_name = record.class.human_attribute_name(attr, default: attr_name)

  record.errors.add(attr, :invalid_currency,
    thousands: details.thousands_separator,
    decimal: details.decimal_mark,
    currency: details.abs_raw_value,
    attribute: attr_name
  )
end
generate_details(raw_value, currency) click to toggle source
# File lib/money-rails/active_model/validator.rb, line 67
def generate_details(raw_value, currency)
  thousands_separator = lookup(:thousands_separator, currency)
  decimal_mark = lookup(:decimal_mark, currency)

  Details.new(raw_value, thousands_separator, decimal_mark)
end
invalid_thousands_separation(details) click to toggle source
# File lib/money-rails/active_model/validator.rb, line 100
def invalid_thousands_separation(details)
  pieces_array = details.decimal_pieces[0].split(details.thousands_separator.presence)

  return false if pieces_array.length <= 1
  return true  if pieces_array[0].length > 3

  pieces_array[1..-1].any? do |thousands_group|
    thousands_group.length != 3
  end
end
locale_backend() click to toggle source
# File lib/money-rails/active_model/validator.rb, line 129
def locale_backend
  Money.locale_backend
end
lookup(key, currency) click to toggle source
# File lib/money-rails/active_model/validator.rb, line 121
def lookup(key, currency)
  if locale_backend
    locale_backend.lookup(key, currency) || DEFAULTS[key]
  else
    DEFAULTS[key]
  end
end
normalize(details) click to toggle source

Remove thousands separators, normalize decimal mark, remove whitespaces and _ (E.g. 99 999 999 or 12_300_200.20)

# File lib/money-rails/active_model/validator.rb, line 113
def normalize(details)
  details.raw_value
    .to_s
    .gsub(details.thousands_separator, '')
    .gsub(details.decimal_mark, '.')
    .gsub(/[\s_]/, '')
end
record_already_has_error?(record, attr, raw_value) click to toggle source
# File lib/money-rails/active_model/validator.rb, line 74
def record_already_has_error?(record, attr, raw_value)
  record.errors.added?(attr, :not_a_number, value: raw_value)
end
thousand_separator_after_decimal_mark(details) click to toggle source
# File lib/money-rails/active_model/validator.rb, line 94
def thousand_separator_after_decimal_mark(details)
  details.thousands_separator.present? &&
    details.decimal_pieces.length == 2 &&
    details.decimal_pieces[1].include?(details.thousands_separator)
end
validate_each(record, attr, _value) click to toggle source
Calls superclass method
# File lib/money-rails/active_model/validator.rb, line 14
def validate_each(record, attr, _value)
  subunit_attr = record.class.monetized_attributes[attr.to_s]
  currency = record.public_send("currency_for_#{attr}")

  # WARNING: Currently this is only defined in ActiveRecord extension!
  before_type_cast = :"#{attr}_money_before_type_cast"
  raw_value = record.try(before_type_cast)

  # If raw value is nil and changed subunit is nil, then
  # nil is a assigned value, else we should treat the
  # subunit value as the one assigned.
  if raw_value.nil? && record.public_send(subunit_attr)
    subunit_value = record.public_send(subunit_attr)
    raw_value = subunit_value.to_f / currency.subunit_to_unit
  end

  return if options[:allow_nil] && raw_value.nil?

  # Set this before we modify raw_value below.
  stringy = raw_value.present? && !raw_value.is_a?(Numeric) && !raw_value.is_a?(Money)

  if stringy
    # TODO: This is supporting legacy behaviour where a symbol can come from a i18n locale,
    #       however practical implications of that are most likely non-existent
    symbol = lookup(:symbol, currency) || currency.symbol

    # remove currency symbol
    raw_value = raw_value.to_s.gsub(symbol, "")
  end

  # Cache abs_raw_value before normalizing because it's used in
  # many places and relies on the original raw_value.
  details = generate_details(raw_value, currency)
  normalized_raw_value = normalize(details)

  super(record, attr, normalized_raw_value)

  return unless stringy
  return if record_already_has_error?(record, attr, normalized_raw_value)

  add_error!(record, attr, details) if
    value_has_too_many_decimal_points(details) ||
    thousand_separator_after_decimal_mark(details) ||
    invalid_thousands_separation(details)
end
value_has_too_many_decimal_points(details) click to toggle source
# File lib/money-rails/active_model/validator.rb, line 90
def value_has_too_many_decimal_points(details)
  ![1, 2].include?(details.decimal_pieces.length)
end