class MoneyParser
Parse an amount from a string
Constants
- CHINESE_NUMERIC_REGEX
1,1123,4567.89
- COMMA_DECIMAL_REGEX
1.234.567,89
- DOT_DECIMAL_REGEX
1,234,567.89
- ESCAPED_MARKS
- ESCAPED_NON_COMMA_MARKS
- ESCAPED_NON_DOT_MARKS
- ESCAPED_NON_SPACE_MARKS
- INDIAN_NUMERIC_REGEX
12,34,567.89
- MARKS
- NUMERIC_REGEX
Public Class Methods
parse(input, currency = nil, **options)
click to toggle source
# File lib/money/money_parser.rb, line 60 def self.parse(input, currency = nil, **options) new.parse(input, currency, **options) end
Public Instance Methods
parse(input, currency = nil, strict: false)
click to toggle source
# File lib/money/money_parser.rb, line 64 def parse(input, currency = nil, strict: false) currency = Money::Helpers.value_to_currency(currency) amount = extract_amount_from_string(input, currency, strict) Money.new(amount, currency) end
Private Instance Methods
extract_amount_from_string(input, currency, strict)
click to toggle source
# File lib/money/money_parser.rb, line 72 def extract_amount_from_string(input, currency, strict) unless input.is_a?(String) return input end if input.strip.empty? return '0' end number = input.scan(NUMERIC_REGEX).flatten.first number = number.to_s.strip if number.empty? raise MoneyFormatError, "invalid money string: #{input}" if strict Money.deprecate("invalid money strings will raise in the next major release \"#{input}\"") return '0' end marks = number.scan(/[#{ESCAPED_MARKS}]/).flatten if marks.empty? return number end if marks.size == 1 return normalize_number(number, marks, currency) end # remove end of string mark number.sub!(/[#{ESCAPED_MARKS}]\z/, '') if amount = number[DOT_DECIMAL_REGEX] || number[INDIAN_NUMERIC_REGEX] || number[CHINESE_NUMERIC_REGEX] return amount.tr(ESCAPED_NON_DOT_MARKS, '') end if amount = number[COMMA_DECIMAL_REGEX] return amount.tr(ESCAPED_NON_COMMA_MARKS, '').sub(',', '.') end raise MoneyFormatError, "invalid money string: #{input}" if strict Money.deprecate("invalid money strings will raise in the next major release \"#{input}\"") normalize_number(number, marks, currency) end
last_digits_decimals?(digits, marks, currency)
click to toggle source
# File lib/money/money_parser.rb, line 127 def last_digits_decimals?(digits, marks, currency) # Thousands marks are always different from decimal marks # Example: 1,234,456 *other_marks, last_mark = marks other_marks.uniq! if other_marks.size == 1 return other_marks.first != last_mark end # Thousands always have more than 2 digits # Example: 1,23 must be 1 dollar and 23 cents if digits.last.size < 3 return !digits.last.empty? end # 0 before the final mark indicates last digits are decimals # Example: 0,23 if digits.first.to_i.zero? return true end # legacy support for 1.000 USD if digits.last.size == 3 && digits.first.size <= 3 && currency.minor_units < 3 return false end # The last mark matches the one used by the provided currency to delimiter decimals currency.decimal_mark == last_mark end
normalize_number(number, marks, currency)
click to toggle source
# File lib/money/money_parser.rb, line 116 def normalize_number(number, marks, currency) digits = number.rpartition(marks.last) digits.first.tr!(ESCAPED_MARKS, '') if last_digits_decimals?(digits, marks, currency) "#{digits.first}.#{digits.last}" else "#{digits.first}#{digits.last}" end end