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