lib/monetize.rb in monetize-1.7.0 vs lib/monetize.rb in monetize-1.8.0

- old
+ new

@@ -2,45 +2,14 @@ require 'money' require 'monetize/core_extensions' require 'monetize/errors' require 'monetize/version' -require 'collection' +require 'monetize/parser' +require 'monetize/collection' module Monetize - CURRENCY_SYMBOLS = { - '$' => 'USD', - '€' => 'EUR', - '£' => 'GBP', - '₤' => 'GBP', - 'R$' => 'BRL', - 'R' => 'ZAR', - '¥' => 'JPY', - 'C$' => 'CAD', - '₼' => 'AZN', - '元' => 'CNY', - 'Kč' => 'CZK', - 'Ft' => 'HUF', - '₹' => 'INR', - '₽' => 'RUB', - '₺' => 'TRY', - '₴' => 'UAH', - 'Fr' => 'CHF', - 'zł' => 'PLN', - '₸' => 'KZT', - "₩" => 'KRW', - } - - MULTIPLIER_SUFFIXES = { - 'K' => 3, - 'M' => 6, - 'B' => 9, - 'T' => 12 - } - MULTIPLIER_SUFFIXES.default = 0 - MULTIPLIER_REGEXP = Regexp.new(format('^(.*?\d)(%s)\b([^\d]*)$', MULTIPLIER_SUFFIXES.keys.join('|')), 'i') - # Class methods class << self # @attr_accessor [true, false] assume_from_symbol Use this to enable the # ability to assume the currency from a passed symbol attr_accessor :assume_from_symbol @@ -60,23 +29,14 @@ def self.parse!(input, currency = Money.default_currency, options = {}) return input if input.is_a?(Money) return from_numeric(input, currency) if input.is_a?(Numeric) - input = input.to_s.strip + parser = Monetize::Parser.new(input, currency, options) + currency_from_input = Money::Currency.wrap(parser.parse_currency) - computed_currency = if options.fetch(:assume_from_symbol) { assume_from_symbol } - compute_currency(input) - else - input[/[A-Z]{2,3}/] - end - - currency = computed_currency || currency || Money.default_currency - currency = Money::Currency.wrap(currency) - - fractional = extract_cents(input, currency) - Money.new(fractional, currency) + Money.new(parser.parse_cents(currency_from_input), currency_from_input) rescue Money::Currency::UnknownCurrency => e fail ParseError, e.message end def self.parse_collection(input, currency = Money.default_currency, options = {}) @@ -99,14 +59,11 @@ value = BigDecimal.new(value.to_s) from_bigdecimal(value, currency) end def self.from_bigdecimal(value, currency = Money.default_currency) - currency = Money::Currency.wrap(currency) - value *= currency.subunit_to_unit - value = value.round unless Money.infinite_precision - Money.new(value, currency) + Money.from_amount(value, currency) end def self.from_numeric(value, currency = Money.default_currency) case value when Integer @@ -118,139 +75,8 @@ fail ArgumentError, "'value' should be a type of Numeric" end end def self.extract_cents(input, currency = Money.default_currency) - multiplier_exp, input = extract_multiplier(input) - - num = input.gsub(/(?:^#{currency.symbol}|[^\d.,'-]+)/, '') - - negative, num = extract_sign(num) - - num.chop! if num.match(/[\.|,]$/) - - major, minor = extract_major_minor(num, currency) - - major, minor = apply_multiplier(multiplier_exp, major.to_i, minor) - - cents = major.to_i * currency.subunit_to_unit - - cents += set_minor_precision(minor, currency) - - apply_sign(negative, cents) - end - - private - - def self.apply_multiplier(multiplier_exp, major, minor) - major *= 10**multiplier_exp - minor = minor.to_s + ('0' * multiplier_exp) - shift = minor[0...multiplier_exp].to_i - major += shift - minor = (minor[multiplier_exp..-1] || '') - [major, minor] - end - - def self.apply_sign(negative, cents) - negative ? cents * -1 : cents - end - - def self.contains_currency_symbol?(amount) - amount =~ currency_symbol_regex - end - - def self.compute_currency(amount) - if contains_currency_symbol?(amount) - matches = amount.match(currency_symbol_regex) - CURRENCY_SYMBOLS[matches[:symbol]] - else - amount[/[A-Z]{2,3}/] - end - end - - def self.extract_major_minor(num, currency) - used_delimiters = num.scan(/[^\d]/).uniq - - case used_delimiters.length - when 0 - [num, 0] - when 2 - thousands_separator, decimal_mark = used_delimiters - split_major_minor(num.gsub(thousands_separator, ''), decimal_mark) - when 1 - extract_major_minor_with_single_delimiter(num, currency, used_delimiters.first) - else - fail ParseError, 'Invalid amount' - end - end - - def self.extract_major_minor_with_single_delimiter(num, currency, delimiter) - if delimiter == currency.decimal_mark - split_major_minor(num, delimiter) - elsif enforce_currency_delimiters and delimiter == currency.thousands_separator - [num.gsub(delimiter, ''), 0] - else - extract_major_minor_with_tentative_delimiter(num, delimiter) - end - end - - def self.extract_major_minor_with_tentative_delimiter(num, delimiter) - if num.scan(delimiter).length > 1 - # Multiple matches; treat as thousands separator - [num.gsub(delimiter, ''), '00'] - else - possible_major, possible_minor = split_major_minor(num, delimiter) - - if possible_minor.length != 3 or possible_major.length > 3 or delimiter == '.' - # Doesn't look like thousands separator - [possible_major, possible_minor] - else - ["#{possible_major}#{possible_minor}", '00'] - end - end - end - - def self.extract_multiplier(input) - if (matches = MULTIPLIER_REGEXP.match(input)) - multiplier_suffix = matches[2].upcase - [MULTIPLIER_SUFFIXES[multiplier_suffix], "#{$1}#{$3}"] - else - [0, input] - end - end - - def self.extract_sign(input) - result = (input =~ /^-+(.*)$/ or input =~ /^(.*)-+$/) ? [true, $1] : [false, input] - fail ParseError, 'Invalid amount (hyphen)' if result[1].include?('-') - result - end - - def self.regex_safe_symbols - CURRENCY_SYMBOLS.keys.map { |key| Regexp.escape(key) }.join('|') - end - - def self.set_minor_precision(minor, currency) - if Money.infinite_precision - (BigDecimal.new(minor) / (10**minor.size)) * currency.subunit_to_unit - elsif minor.size < currency.decimal_places - (minor + ('0' * currency.decimal_places))[0, currency.decimal_places].to_i - elsif minor.size > currency.decimal_places - if minor[currency.decimal_places, 1].to_i >= 5 - minor[0, currency.decimal_places].to_i + 1 - else - minor[0, currency.decimal_places].to_i - end - else - minor.to_i - end - end - - def self.split_major_minor(num, delimiter) - major, minor = num.split(delimiter) - minor = '00' unless minor - [major, minor] - end - - def self.currency_symbol_regex - /\A[\+|\-]?(?<symbol>#{regex_safe_symbols})/ + Monetize::Parser.new(input).parse_cents(currency) end end