lib/money_helper.rb in money_helper-0.0.4 vs lib/money_helper.rb in money_helper-0.0.5
- old
+ new
@@ -25,216 +25,19 @@
# number_only: (Boolean) optional flag to exclude currency indicators (retains number formatting
# specific to currency)
def self.money_to_text(amount, currency, number_only = false)
return nil unless amount.present?
currency = "USD" if currency.blank?
+ valid_currency = code_valid?(currency) ? currency : "USD"
symbol = symbol_for_code(currency)
include_symbol = !number_only && symbol.present? && OK_SYMBOLS.include?(symbol)
- valid_currency = currency if code_valid?(currency)
+ subunit_factor = Money::Currency.new(valid_currency).subunit_to_unit
(number_only || SYMBOL_ONLY.include?(currency) ? "" : currency + " ") +
- parse_money(amount.ceil, valid_currency).format({
+ Money.new(amount*subunit_factor.ceil, valid_currency).format({
no_cents: true,
symbol_position: :before,
symbol: include_symbol
}).delete(' ')
- end
-
- # Parses the current string and converts it to a +Money+ object.
- # Excess characters will be discarded.
- #
- # @param [String, #to_s] input The input to parse.
- # @param [Currency, String, Symbol] currency The currency format.
- # The currency to set the resulting +Money+ object to.
- #
- # @return [Money]
- #
- # @raise [ArgumentError] If any +currency+ is supplied and
- # given value doesn't match the one extracted from
- # the +input+ string.
- #
- # @example
- # '100'.to_money #=> #<Money @fractional=10000>
- # '100.37'.to_money #=> #<Money @fractional=10037>
- # '100 USD'.to_money #=> #<Money @fractional=10000, @currency=#<Money::Currency id: usd>>
- # 'USD 100'.to_money #=> #<Money @fractional=10000, @currency=#<Money::Currency id: usd>>
- # '$100 USD'.to_money #=> #<Money @fractional=10000, @currency=#<Money::Currency id: usd>>
- # 'hello 2000 world'.to_money #=> #<Money @fractional=200000 @currency=#<Money::Currency id: usd>>
- #
- # @example Mismatching currencies
- # 'USD 2000'.to_money("EUR") #=> ArgumentError
- #
- # @see #from_string
- #
- def self.parse_money(input, currency = nil)
- i = input.to_s.strip
-
- # raise Money::Currency.table.collect{|c| c[1][:symbol]}.inspect
-
- # Check the first character for a currency symbol, alternatively get it
- # from the stated currency string
- c = if Money.assume_from_symbol && i =~ /^(\$|€|£)/
- case i
- when /^\$/ then "USD"
- when /^€/ then "EUR"
- when /^£/ then "GBP"
- end
- else
- i[/[A-Z]{2,3}/]
- end
-
- # check that currency passed and embedded currency are the same,
- # and negotiate the final currency
- if currency.nil? and c.nil?
- currency = Money.default_currency
- elsif currency.nil?
- currency = c
- elsif c.nil?
- currency = currency
- elsif currency != c
- # TODO: ParseError
- raise ArgumentError, "Mismatching Currencies"
- end
- currency = Money::Currency.wrap(currency)
-
- fractional = extract_cents(i, currency)
- Money.new(fractional, currency)
- end
-
- # Takes a number string and attempts to massage out the number.
- #
- # @param [String] input The string containing a potential number.
- #
- # @return [Integer]
- #
- def self.extract_cents(input, currency = Money.default_currency)
- # remove anything that's not a number, potential thousands_separator, or minus sign
- num = input.gsub(/[^\d.,'-]/, '')
-
- # set a boolean flag for if the number is negative or not
- negative = num =~ /^-|-$/ ? true : false
-
- # decimal mark character
- decimal_char = currency.decimal_mark
-
- # if negative, remove the minus sign from the number
- # if it's not negative, the hyphen makes the value invalid
- if negative
- num = num.sub(/^-|-$/, '')
- end
-
- raise ArgumentError, "Invalid currency amount (hyphen)" if num.include?('-')
-
- #if the number ends with punctuation, just throw it out. If it means decimal,
- #it won't hurt anything. If it means a literal period or comma, this will
- #save it from being mis-interpreted as a decimal.
- num.chop! if num.match(/[\.|,]$/)
-
- # gather all decimal_marks within the result number
- used_delimiters = num.scan(/[^\d]/)
-
- # determine the number of unique decimal_marks within the number
- #
- # e.g.
- # $1,234,567.89 would return 2 (, and .)
- # $125,00 would return 1
- # $199 would return 0
- # $1 234,567.89 would raise an error (decimal_marks are space, comma, and period)
- case used_delimiters.uniq.length
- # no decimal_mark or thousands_separator; major (dollars) is the number, and minor (cents) is 0
- when 0 then major, minor = num, 0
-
- # two decimal_marks, so we know the last item in this array is the
- # major/minor thousands_separator and the rest are decimal_marks
- when 2
- thousands_separator, decimal_mark = used_delimiters.uniq
-
- # remove all thousands_separator, split on the decimal_mark
- major, minor = num.gsub(thousands_separator, '').split(decimal_mark)
- min = 0 unless min
- when 1
- # we can't determine if the comma or period is supposed to be a decimal_mark or a thousands_separator
- # e.g.
- # 1,00 - comma is a thousands_separator
- # 1.000 - period is a thousands_separator
- # 1,000 - comma is a decimal_mark
- # 1,000,000 - comma is a decimal_mark
- # 10000,00 - comma is a thousands_separator
- # 1000,000 - comma is a thousands_separator
-
- # assign first decimal_mark for reusability
- decimal_mark = used_delimiters.first
-
- # When we have identified the decimal mark character
- if decimal_char == decimal_mark
- major, minor = num.split(decimal_char)
-
- else
- # decimal_mark is used as a decimal_mark when there are multiple instances, always
- if num.scan(decimal_mark).length > 1 # multiple matches; treat as decimal_mark
- major, minor = num.gsub(decimal_mark, ''), 0
- else
- # ex: 1,000 - 1.0000 - 10001.000
- # split number into possible major (dollars) and minor (cents) values
- possible_major, possible_minor = num.split(decimal_mark)
- possible_major ||= "0"
- possible_minor ||= "00"
-
- # if the minor (cents) length isn't 3, assign major/minor from the possibles
- # e.g.
- # 1,00 => 1.00
- # 1.0000 => 1.00
- # 1.2 => 1.20
- if possible_minor.length != 3 # thousands_separator
- major, minor = possible_major, possible_minor
- else
- # minor length is three
- # let's try to figure out intent of the thousands_separator
-
- # the major length is greater than three, which means
- # the comma or period is used as a thousands_separator
- # e.g.
- # 1000,000
- # 100000,000
- if possible_major.length > 3
- major, minor = possible_major, possible_minor
- else
- # number is in format ###{sep}### or ##{sep}### or #{sep}###
- # handle as , is sep, . is thousands_separator
- if decimal_mark == '.'
- major, minor = possible_major, possible_minor
- else
- major, minor = "#{possible_major}#{possible_minor}", 0
- end
- end
- end
- end
- end
- else
- # TODO: ParseError
- raise ArgumentError, "Invalid currency amount"
- end
-
- # build the string based on major/minor since decimal_mark/thousands_separator have been removed
- # avoiding floating point arithmetic here to ensure accuracy
- cents = (major.to_i * currency.subunit_to_unit)
- # Because of an bug in JRuby, we can't just call #floor
- minor = minor.to_s
- minor = if 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
-
- cents += minor
-
- # if negative, multiply by -1; otherwise, return positive cents
- negative ? cents * -1 : cents
end
##
# Formats a low and high amount in the given currency into a price string
#