Class: Money

Inherits:
Object
  • Object
show all
Includes:
Comparable, Arithmetic, Formatting, Parsing
Defined in:
lib/money/money.rb,
lib/money/currency.rb,
lib/money/bank/base.rb,
lib/money/money/parsing.rb,
lib/money/money/arithmetic.rb,
lib/money/money/formatting.rb,
lib/money/bank/variable_exchange.rb

Overview

Represents an amount of money in a given currency.

Defined Under Namespace

Modules: Arithmetic, Bank, Formatting, Parsing Classes: Currency

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Parsing

included

Methods included from Formatting

#decimal_mark, #format, #thousands_separator

Methods included from Arithmetic

#%, #*, #+, #-, #-@, #/, #<=>, #==, #abs, #div, #divmod, #eql?, #modulo, #negative?, #nonzero?, #positive?, #remainder, #zero?

Constructor Details

#initialize(fractional, currency = Money.default_currency, bank = Money.default_bank) ⇒ Money

Creates a new Money object of value given in the fractional unit of the given currency.

Alternatively you can use the convenience methods like ca_dollar and us_dollar.

Examples:

Money.new(100)
#=> #<Money @fractional=100 @currency="USD">
Money.new(100, "USD")
#=> #<Money @fractional=100 @currency="USD">
Money.new(100, "EUR")
#=> #<Money @fractional=100 @currency="EUR">

Parameters:

  • fractional (Numeric)

    The value given in the fractional unit.

  • currency (Currency, String, Symbol) (defaults to: Money.default_currency)

    The currency format.

  • bank (Money::Bank::*) (defaults to: Money.default_bank)

    The exchange bank to use.

See Also:



240
241
242
243
244
245
246
247
248
249
250
# File 'lib/money/money.rb', line 240

def initialize(fractional, currency = Money.default_currency, bank = Money.default_bank)
  @fractional = if fractional.is_a?(Rational)
                  fractional.to_d(self.class.conversion_precision)
                elsif fractional.respond_to?(:to_d)
                  fractional.to_d
                else
                  BigDecimal.new(fractional.to_s)
                end
  @currency = Currency.wrap(currency)
  @bank     = bank
end

Class Attribute Details

.assume_from_symboltrue, false

Use this to enable the ability to assume the currency from a passed symbol

Returns:

  • (true, false)


71
72
73
# File 'lib/money/money.rb', line 71

def assume_from_symbol
  @assume_from_symbol
end

.conversion_precisionInteger

Use this to specify precision for converting Rational to BigDecimal

Returns:

  • (Integer)


86
87
88
# File 'lib/money/money.rb', line 86

def conversion_precision
  @conversion_precision
end

.default_bankMoney::Bank::*

Each Money object is associated to a bank object, which is responsible for currency exchange. This property allows you to specify the default bank object. The default value for this property is an instance of Bank::VariableExchange. It allows one to specify custom exchange rates.

Returns:



54
55
56
# File 'lib/money/money.rb', line 54

def default_bank
  @default_bank
end

.default_currencyMoney::Currency

The default currency, which is used when Money.new is called without an explicit currency argument. The default value is Currency.new(“USD”). The value must be a valid Money::Currency instance.

Returns:



61
62
63
# File 'lib/money/money.rb', line 61

def default_currency
  @default_currency
end

.infinite_precisiontrue, false

Use this to enable infinite precision cents

Returns:

  • (true, false)


76
77
78
# File 'lib/money/money.rb', line 76

def infinite_precision
  @infinite_precision
end

.rounding_modeBigDecimal::ROUND_MODE

Use this to specify the rounding mode

Returns:

  • (BigDecimal::ROUND_MODE)


81
82
83
# File 'lib/money/money.rb', line 81

def rounding_mode
  @rounding_mode
end

.use_i18ntrue, false

Use this to disable i18n even if it’s used by other objects in your app.

Returns:

  • (true, false)


66
67
68
# File 'lib/money/money.rb', line 66

def use_i18n
  @use_i18n
end

Instance Attribute Details

#bankMoney::Bank::* (readonly)

The Money::Bank based object used to perform currency exchanges with.

Returns:



44
45
46
# File 'lib/money/money.rb', line 44

def bank
  @bank
end

#currencyCurrency (readonly)

The currency the money is in.

Returns:



39
40
41
# File 'lib/money/money.rb', line 39

def currency
  @currency
end

Class Method Details

.add_rate(from_currency, to_currency, rate) ⇒ Numeric

Adds a new exchange rate to the default bank and return the rate.

Examples:

Money.add_rate("USD", "CAD", 1.25) #=> 1.25

Parameters:

Returns:



213
214
215
# File 'lib/money/money.rb', line 213

def self.add_rate(from_currency, to_currency, rate)
  Money.default_bank.add_rate(from_currency, to_currency, rate)
end

.ca_dollar(cents) ⇒ Money

Creates a new Money object of the given value, using the Canadian dollar currency.

Examples:

n = Money.ca_dollar(100)
n.cents    #=> 100
n.currency #=> #<Money::Currency id: cad>

Parameters:

  • cents (Integer)

    The cents value.

Returns:



133
134
135
# File 'lib/money/money.rb', line 133

def self.ca_dollar(cents)
  Money.new(cents, "CAD")
end

.empty(currency = default_currency) ⇒ Money

Create a new money object with value 0.

Examples:

Money.empty #=> #<Money @fractional=0>

Parameters:

Returns:



118
119
120
# File 'lib/money/money.rb', line 118

def self.empty(currency = default_currency)
  Money.new(0, currency)
end

.euro(cents) ⇒ Money

Creates a new Money object of the given value, using the Euro currency.

Examples:

n = Money.euro(100)
n.cents    #=> 100
n.currency #=> #<Money::Currency id: eur>

Parameters:

  • cents (Integer)

    The cents value.

Returns:



162
163
164
# File 'lib/money/money.rb', line 162

def self.euro(cents)
  Money.new(cents, "EUR")
end

.new_with_amount(amount, currency = Money.default_currency, bank = Money.default_bank) ⇒ Money

Creates a new Money object of amount value , with given currency.

The amount value is expressed in the main monetary unit, opposite to the subunit-based representation used internally by this library called cents.

Examples:

Money.new_with_amount(100)
#=> #<Money @fractional=10000 @currency="USD">
Money.new_with_amount(100, "USD")
#=> #<Money @fractional=10000 @currency="USD">
Money.new_with_amount(100, "EUR")
#=> #<Money @fractional=10000 @currency="EUR">

Parameters:

  • amount (Numeric)

    The money amount, in main monetary unit.

  • currency (Currency, String, Symbol) (defaults to: Money.default_currency)

    The currency format.

  • bank (Money::Bank::*) (defaults to: Money.default_bank)

    The exchange bank to use.

Returns:

See Also:

  • new


189
190
191
192
193
194
# File 'lib/money/money.rb', line 189

def self.new_with_amount(amount, currency = Money.default_currency, bank = Money.default_bank)
  money = from_numeric(amount, currency)
  # Hack! You can't change a bank
  money.instance_variable_set("@bank", bank)
  money
end

.new_with_dollars(*args) ⇒ Object

Synonym of #new_with_amount

See Also:



199
200
201
# File 'lib/money/money.rb', line 199

def self.new_with_dollars(*args)
  self.new_with_amount(*args)
end

.us_dollar(cents) ⇒ Money

Creates a new Money object of the given value, using the American dollar currency.

Examples:

n = Money.us_dollar(100)
n.cents    #=> 100
n.currency #=> #<Money::Currency id: usd>

Parameters:

  • cents (Integer)

    The cents value.

Returns:



148
149
150
# File 'lib/money/money.rb', line 148

def self.us_dollar(cents)
  Money.new(cents, "USD")
end

Instance Method Details

#allocate(splits) ⇒ Array<Money, Money, Money>

Allocates money between different parties without loosing pennies. After the mathmatically split has been performed, left over pennies will be distributed round-robin amongst the parties. This means that parties listed first will likely recieve more pennies then ones that are listed later

Examples:

Money.new(5, "USD").allocate([0.3,0.7)) #=> [Money.new(2), Money.new(3)]
Money.new(100, "USD").allocate([0.33,0.33,0.33]) #=> [Money.new(34), Money.new(33), Money.new(33)]

Parameters:

  • splits (Array<Float, Float, Float>)
    0.50, 0.25, 0.25

    to give 50% of the cash to party1, 25% to party2, and 25% to party3.

Returns:



478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
# File 'lib/money/money.rb', line 478

def allocate(splits)
  allocations = splits.inject(BigDecimal("0")) do |sum, n|
    n = BigDecimal(n.to_s) unless n.is_a?(BigDecimal)
    sum + n
  end

  if (allocations - BigDecimal("1")) > Float::EPSILON
    raise ArgumentError, "splits add to more then 100%"
  end

  left_over = fractional

  amounts = splits.map do |ratio|
    if self.class.infinite_precision
      fraction = fractional * ratio
    else
      fraction = (fractional * ratio / allocations).floor
      left_over -= fraction
      fraction
    end
  end

  unless self.class.infinite_precision
    left_over.to_i.times { |i| amounts[i % amounts.length] += 1 }
  end

  amounts.collect { |fractional| Money.new(fractional, currency) }
end

#amountFloat

Returns the numerical value of the money

Examples:

Money.new(100).amount            # => 1.0
Money.new_with_amount(1).amount  # => 1.0

Returns:

  • (Float)

See Also:



283
284
285
# File 'lib/money/money.rb', line 283

def amount
  to_f
end

#as_ca_dollarMoney

Receive a money object with the same amount as the current Money object in canadian dollar.

Examples:

n = Money.new(100, "USD").as_ca_dollar
n.currency #=> #<Money::Currency id: cad>

Returns:



450
451
452
# File 'lib/money/money.rb', line 450

def as_ca_dollar
  exchange_to("CAD")
end

#as_euroMoney

Receive a money object with the same amount as the current Money object in euro.

Examples:

n = Money.new(100, "USD").as_euro
n.currency #=> #<Money::Currency id: eur>

Returns:



462
463
464
# File 'lib/money/money.rb', line 462

def as_euro
  exchange_to("EUR")
end

#as_us_dollarMoney

Receive a money object with the same amount as the current Money object in american dollars.

Examples:

n = Money.new(100, "CAD").as_us_dollar
n.currency #=> #<Money::Currency id: usd>

Returns:



438
439
440
# File 'lib/money/money.rb', line 438

def as_us_dollar
  exchange_to("USD")
end

#centsInteger

Convenience method for fractional part of the amount. Synonym of #fractional

Returns:

  • (Integer)


17
18
19
# File 'lib/money/money.rb', line 17

def cents
  fractional
end

#currency_as_stringString

Return string representation of currency object

Examples:

Money.new(100, :USD).currency_as_string #=> "USD"

Returns:



293
294
295
# File 'lib/money/money.rb', line 293

def currency_as_string
  currency.to_s
end

#currency_as_string=(val) ⇒ Money::Currency

Set currency object using a string

Examples:

Money.new(100).currency_as_string("CAD") #=> #<Money::Currency id: cad>

Parameters:

  • val (String)

    The currency string.

Returns:



305
306
307
# File 'lib/money/money.rb', line 305

def currency_as_string=(val)
  @currency = Currency.wrap(val)
end

#dollarsFloat

Assuming using a currency using dollars: Returns the value of the money in dollars, instead of in the fractional unit cents.

Synonym of #amount

Examples:

Money.new(100).dollars           # => 1.0
Money.new_with_dollars(1).dollar # => 1.0

Returns:

  • (Float)

See Also:



268
269
270
# File 'lib/money/money.rb', line 268

def dollars
  amount
end

#exchange_to(other_currency) ⇒ Money

Receive the amount of this money object in another Currency.

Examples:

Money.new(2000, "USD").exchange_to("EUR")
Money.new(2000, "USD").exchange_to(Currency.new("EUR"))

Parameters:

Returns:



425
426
427
428
# File 'lib/money/money.rb', line 425

def exchange_to(other_currency)
  other_currency = Currency.wrap(other_currency)
  @bank.exchange_with(self, other_currency)
end

#fractionalInteger

The value of the amount represented in the fractional unit of the currency. Example: USD, 1 dollar (amount) == 100 cents (fractional unit).

Returns:

  • (Integer)


24
25
26
27
28
29
30
31
32
33
34
# File 'lib/money/money.rb', line 24

def fractional
  if self.class.infinite_precision
    @fractional
  else
    # If the Money object is created from a serialized YAML string, 
    # @fractional can end up being set to a Float. We need to ensure 
    # it is BigDecimal before calling #round with two paramers. 
    # Float class only provides #round with 0 or 1 parameter.
    BigDecimal.new(@fractional.to_s, 0).round(0, self.class.rounding_mode).to_i
  end
end

#hashFixnum

Returns a Fixnum hash value based on the fractional and currency attributes in order to use functions like & (intersection), group_by, etc.

Examples:

Money.new(100).hash #=> 908351

Returns:

  • (Fixnum)


316
317
318
# File 'lib/money/money.rb', line 316

def hash
  [fractional.hash, currency.hash].hash
end

#inspectString

Common inspect function

Returns:



333
334
335
# File 'lib/money/money.rb', line 333

def inspect
  "#<Money fractional:#{fractional} currency:#{currency}>"
end

#localize_formatting_rules(rules) ⇒ Object



307
308
309
310
311
312
313
314
# File 'lib/money/money/formatting.rb', line 307

def localize_formatting_rules(rules)
  if currency.iso_code == "JPY" && I18n.locale == :ja
    rules[:symbol] = ""
    rules[:symbol_position] = :after
    rules[:symbol_after_without_space] = true
  end
  rules
end

#regexp_format(formatted, rules, decimal_mark, symbol_value) ⇒ Object



293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/money/money/formatting.rb', line 293

def regexp_format(formatted, rules, decimal_mark, symbol_value)
  regexp_decimal = Regexp.escape(decimal_mark)
  if rules[:south_asian_number_formatting]
    /(\d+?)(?=(\d\d)+(\d)(?:\.))/
  else
    # Symbols may contain decimal marks (E.g "դր.")
    if formatted.sub(symbol_value, "") =~ /#{regexp_decimal}/
      /(\d)(?=(?:\d{3})+(?:#{regexp_decimal}))/
    else
      /(\d)(?=(?:\d{3})+(?:[^\d]{1}|$))/
    end
  end
end

#split(num) ⇒ Array<Money, Money, Money>

Split money amongst parties evenly without loosing pennies.

Examples:

Money.new(100, "USD").split(3) #=> [Money.new(34), Money.new(33), Money.new(33)]

Parameters:

  • num (Numeric)

    number of parties.

Returns:

Raises:

  • (ArgumentError)


515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
# File 'lib/money/money.rb', line 515

def split(num)
  raise ArgumentError, "need at least one party" if num < 1

  if self.class.infinite_precision
    amt = self.div(BigDecimal(num.to_s))
    return 1.upto(num).map{amt}
  end

  low = Money.new(fractional / num, self.currency)
  high = Money.new(low.fractional + 1, self.currency)

  remainder = fractional % num
  result = []

  num.times do |index|
    result[index] = index < remainder ? high : low
  end

  result
end

#symbolString

Uses Currency#symbol. If nil is returned, defaults to “¤”.

Examples:

Money.new(100, "USD").symbol #=> "$"

Returns:



326
327
328
# File 'lib/money/money.rb', line 326

def symbol
  currency.symbol || "¤"
end

#to_dBigDecimal

Return the amount of money as a BigDecimal.

Examples:

Money.us_dollar(100).to_d => BigDecimal.new("1.0")

Returns:

  • (BigDecimal)


387
388
389
# File 'lib/money/money.rb', line 387

def to_d
  BigDecimal.new(fractional.to_s) / BigDecimal.new(currency.subunit_to_unit.to_s)
end

#to_fFloat

Return the amount of money as a float. Floating points cannot guarantee precision. Therefore, this function should only be used when you no longer need to represent currency or working with another system that requires decimals.

Examples:

Money.us_dollar(100).to_f => 1.0

Returns:

  • (Float)


400
401
402
# File 'lib/money/money.rb', line 400

def to_f
  to_d.to_f
end

#to_money(given_currency = nil) ⇒ self

Conversation to self.

Returns:

  • (self)


407
408
409
410
411
412
413
414
# File 'lib/money/money.rb', line 407

def to_money(given_currency = nil)
  given_currency = Currency.wrap(given_currency) if given_currency
  if given_currency.nil? || self.currency == given_currency
    self
  else
    exchange_to(given_currency)
  end
end

#to_sString

Returns the amount of money as a string.

Examples:

Money.ca_dollar(100).to_s #=> "1.00"

Returns:



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
369
370
371
372
373
374
375
376
377
378
379
# File 'lib/money/money.rb', line 343

def to_s
  unit, subunit = fractional().abs.divmod(currency.subunit_to_unit)

  unit_str       = ""
  subunit_str    = ""
  fraction_str   = ""

  if self.class.infinite_precision
    subunit, fraction = subunit.divmod(BigDecimal("1"))

    unit_str       = unit.to_i.to_s
    subunit_str    = subunit.to_i.to_s
    fraction_str   = fraction.to_s("F")[2..-1] # want fractional part "0.xxx"

    fraction_str = "" if fraction_str =~ /^0+$/
  else
    unit_str, subunit_str = unit.to_s, subunit.to_s
  end

  absolute_str = if currency.decimal_places == 0
    if fraction_str == ""
      unit_str
    else
      "#{unit_str}#{decimal_mark}#{fraction_str}"
    end
  else
    # need to pad subunit to right position,
    # for example 1 usd 3 cents should be 1.03 not 1.3
    subunit_str.insert(0, '0') while subunit_str.length < currency.decimal_places

    "#{unit_str}#{decimal_mark}#{subunit_str}#{fraction_str}"
  end

  absolute_str.tap do |str|
    str.insert(0, "-") if fractional() < 0
  end
end