Module: Money::Parsing::ClassMethods
- Defined in:
- lib/money/money/parsing.rb
Instance Method Summary collapse
-
#extract_cents(input, currency = Money.default_currency) ⇒ Integer
Takes a number string and attempts to massage out the number.
-
#from_bigdecimal(value, currency = Money.default_currency) ⇒ Money
Converts a BigDecimal into a Money object treating the
value
as dollars and converting to corresponding fractional unit, according tocurrency
subunit property, before instantiating the Money object. -
#from_fixnum(value, currency = Money.default_currency) ⇒ Money
Converts a Fixnum into a Money object treating the
value
as amount and to corresponding fractional unit, according tocurrency
subunit property, before instantiating the Money object. -
#from_float(value, currency = Money.default_currency) ⇒ Money
Converts a Float into a Money object treating the
value
as dollars and to corresponding fractional unit, according tocurrency
subunit property, before instantiating the Money object. -
#from_numeric(value, currency = Money.default_currency) ⇒ Money
Converts a Numeric value into a Money object treating the
value
as dollars and converting to corresponding fractional unit, according tocurrency
subunit property, before instantiating the Money object. -
#from_string(value, currency = Money.default_currency) ⇒ Money
Converts a String into a Money object treating the
value
as amount and converting to fractional unit, according tocurrency
subunit property, before instantiating the Money object. -
#parse(input, currency = nil) ⇒ Money
Parses the current string and converts it to a
Money
object.
Instance Method Details
#extract_cents(input, currency = Money.default_currency) ⇒ Integer
Takes a number string and attempts to massage out the number.
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
# File 'lib/money/money/parsing.rb', line 240 def 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 |
#from_bigdecimal(value, currency = Money.default_currency) ⇒ Money
Converts a BigDecimal into a Money object treating the value
as dollars and converting to corresponding fractional unit, according to currency
subunit property, before instantiating the Money object.
184 185 186 187 188 |
# File 'lib/money/money/parsing.rb', line 184 def from_bigdecimal(value, currency = Money.default_currency) currency = Money::Currency.wrap(currency) amount = value * currency.subunit_to_unit new(amount.round, currency) end |
#from_fixnum(value, currency = Money.default_currency) ⇒ Money
Converts a Fixnum into a Money object treating the value
as amount and to corresponding fractional unit, according to currency
subunit property, before instantiating the Money object.
125 126 127 128 129 |
# File 'lib/money/money/parsing.rb', line 125 def from_fixnum(value, currency = Money.default_currency) currency = Money::Currency.wrap(currency) amount = value * currency.subunit_to_unit new(amount, currency) end |
#from_float(value, currency = Money.default_currency) ⇒ Money
Converts a Float into a Money object treating the value
as dollars and to corresponding fractional unit, according to currency
subunit property, before instantiating the Money object.
Behind the scenes, this method relies on Money.from_bigdecimal to avoid problems with floating point precision.
157 158 159 |
# File 'lib/money/money/parsing.rb', line 157 def from_float(value, currency = Money.default_currency) from_bigdecimal(BigDecimal.new(value.to_s), currency) end |
#from_numeric(value, currency = Money.default_currency) ⇒ Money
Converts a Numeric value into a Money object treating the value
as dollars and converting to corresponding fractional unit, according to currency
subunit property, before instantiating the Money object.
This method relies on various Money.from_*
methods and tries to forwards the call to the most appropriate method in order to reduce computation effort. For instance, if value
is an Integer, this method calls #from_fixnum instead of using the default #from_bigdecimal which adds the overload to converts the value into a slower BigDecimal instance.
223 224 225 226 227 228 229 230 231 232 |
# File 'lib/money/money/parsing.rb', line 223 def from_numeric(value, currency = Money.default_currency) case value when Fixnum from_fixnum(value, currency) when Numeric from_bigdecimal(BigDecimal.new(value.to_s), currency) else raise ArgumentError, "`value' should be a Numeric object" end end |
#from_string(value, currency = Money.default_currency) ⇒ Money
Converts a String into a Money object treating the value
as amount and converting to fractional unit, according to currency
subunit property, before instantiating the Money object.
Behind the scenes, this method relies on #from_bigdecimal to avoid problems with string-to-numeric conversion.
98 99 100 |
# File 'lib/money/money/parsing.rb', line 98 def from_string(value, currency = Money.default_currency) from_bigdecimal(BigDecimal.new(value.to_s), currency) end |
#parse(input, currency = nil) ⇒ Money
Parses the current string and converts it to a Money
object. Excess characters will be discarded.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/money/money/parsing.rb', line 36 def parse(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) new(fractional, currency) end |