class Money

“Money is any object or record that is generally accepted as payment for goods and services and repayment of debts in a given socio-economic context or country.” -Wikipedia

An instance of Money represents an amount of a specific currency.

Money is a value object and should be treated as immutable.

@see en.wikipedia.org/wiki/Money

Constants

VERSION

Attributes

conversion_precision[RW]

@!attribute [rw] default_bank

Used to set a default bank for currency exchange.

Each Money object is associated with 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.

@return [Money::Bank::Base]

@!attribute default_formatting_rules

Used to define a default hash of rules for every time
+Money#format+ is called.  Rules provided on method call will be
merged with the default ones.  To overwrite a rule, just provide the
intended value while calling +format+.

@see Money::Formatter#initialize Money::Formatter for more details

@example
  Money.default_formatting_rules = { display_free: true }
  Money.new(0, "USD").format                          # => "free"
  Money.new(0, "USD").format(display_free: false)  # => "$0.00"

@return [Hash]

@!attribute [rw] use_i18n

Used to disable i18n even if it's used by other components of your app.

@return [Boolean]

@!attribute [rw] default_infinite_precision

@return [Boolean] Use this to enable infinite precision cents as the
  global default

@!attribute [rw] conversion_precision

Used to specify precision for converting Rational to BigDecimal

@return [Integer]
default_bank[W]
default_formatting_rules[RW]

@!attribute [rw] default_bank

Used to set a default bank for currency exchange.

Each Money object is associated with 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.

@return [Money::Bank::Base]

@!attribute default_formatting_rules

Used to define a default hash of rules for every time
+Money#format+ is called.  Rules provided on method call will be
merged with the default ones.  To overwrite a rule, just provide the
intended value while calling +format+.

@see Money::Formatter#initialize Money::Formatter for more details

@example
  Money.default_formatting_rules = { display_free: true }
  Money.new(0, "USD").format                          # => "free"
  Money.new(0, "USD").format(display_free: false)  # => "$0.00"

@return [Hash]

@!attribute [rw] use_i18n

Used to disable i18n even if it's used by other components of your app.

@return [Boolean]

@!attribute [rw] default_infinite_precision

@return [Boolean] Use this to enable infinite precision cents as the
  global default

@!attribute [rw] conversion_precision

Used to specify precision for converting Rational to BigDecimal

@return [Integer]
default_infinite_precision[RW]

@!attribute [rw] default_bank

Used to set a default bank for currency exchange.

Each Money object is associated with 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.

@return [Money::Bank::Base]

@!attribute default_formatting_rules

Used to define a default hash of rules for every time
+Money#format+ is called.  Rules provided on method call will be
merged with the default ones.  To overwrite a rule, just provide the
intended value while calling +format+.

@see Money::Formatter#initialize Money::Formatter for more details

@example
  Money.default_formatting_rules = { display_free: true }
  Money.new(0, "USD").format                          # => "free"
  Money.new(0, "USD").format(display_free: false)  # => "$0.00"

@return [Hash]

@!attribute [rw] use_i18n

Used to disable i18n even if it's used by other components of your app.

@return [Boolean]

@!attribute [rw] default_infinite_precision

@return [Boolean] Use this to enable infinite precision cents as the
  global default

@!attribute [rw] conversion_precision

Used to specify precision for converting Rational to BigDecimal

@return [Integer]
locale_backend[R]
use_i18n[R]
bank[R]

@!attribute [r] currency

@return [Currency] The money's currency.

@!attribute [r] bank

@return [Money::Bank::Base] The +Money::Bank+-based object which currency
  exchanges are performed with.
currency[R]

@!attribute [r] currency

@return [Currency] The money's currency.

@!attribute [r] bank

@return [Money::Bank::Base] The +Money::Bank+-based object which currency
  exchanges are performed with.

Public Class Methods

add_rate(from_currency, to_currency, rate) click to toggle source

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

@param [Currency, String, Symbol] from_currency Currency to exchange from. @param [Currency, String, Symbol] to_currency Currency to exchange to. @param [Numeric] rate Rate to exchange with.

@return [Numeric]

@example

Money.add_rate("USD", "CAD", 1.25) #=> 1.25
# File lib/money/money.rb, line 282
def self.add_rate(from_currency, to_currency, rate)
  Money.default_bank.add_rate(from_currency, to_currency, rate)
end
default_bank() click to toggle source
# File lib/money/money.rb, line 171
def self.default_bank
  if @default_bank.respond_to?(:call)
    @default_bank.call
  else
    @default_bank
  end
end
default_currency() click to toggle source

@!attribute default_currency

@return [Money::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.
# File lib/money/money.rb, line 152
def self.default_currency
  if @using_deprecated_default_currency
    warn '[WARNING] The default currency will change from `USD` to `nil` in the next major release. Make ' \
         'sure to set it explicitly using `Money.default_currency=` to avoid potential issues'
    @using_deprecated_default_currency = false
  end

  if @default_currency.respond_to?(:call)
    Money::Currency.new(@default_currency.call)
  else
    Money::Currency.new(@default_currency)
  end
end
default_currency=(currency) click to toggle source
# File lib/money/money.rb, line 166
def self.default_currency=(currency)
  @using_deprecated_default_currency = false
  @default_currency = currency
end
disallow_currency_conversion!() click to toggle source

Sets the default bank to be a SingleCurrency bank that raises on currency exchange. Useful when apps operate in a single currency at a time.

# File lib/money/money.rb, line 288
def self.disallow_currency_conversion!
  self.default_bank = Bank::SingleCurrency.instance
end
from_amount(amount, currency = default_currency, options = {}) click to toggle source

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

@param [Numeric] amount The numerical value of the money. @param [Currency, String, Symbol] currency The currency format. @param [Hash] options Optional settings for the new Money instance @option [Money::Bank::*] :bank The exchange bank to use.

@example

Money.from_amount(23.45, "USD") # => #<Money fractional:2345 currency:USD>
Money.from_amount(23.45, "JPY") # => #<Money fractional:23 currency:JPY>

@return [Money]

@see initialize

# File lib/money/money.rb, line 307
def self.from_amount(amount, currency = default_currency, options = {})
  raise ArgumentError, "'amount' must be numeric" unless Numeric === amount

  currency = Currency.wrap(currency) || Money.default_currency
  value = amount.to_d * currency.subunit_to_unit
  new(value, currency, options)
end
infinite_precision() click to toggle source
# File lib/money/money.rb, line 136
def infinite_precision
  warn '[DEPRECATION] `Money.infinite_precision` is deprecated - use `Money.default_infinite_precision` instead'
  default_infinite_precision
end
infinite_precision=(value) click to toggle source
# File lib/money/money.rb, line 141
def infinite_precision=(value)
  warn '[DEPRECATION] `Money.infinite_precision=` is deprecated - use `Money.default_infinite_precision= ` instead'
  self.default_infinite_precision = value
end
inherited(base) click to toggle source
# File lib/money/money.rb, line 224
def self.inherited(base)
  base.setup_defaults
end
locale_backend=(value) click to toggle source
# File lib/money/money.rb, line 179
def self.locale_backend=(value)
  @locale_backend = value ? LocaleBackend.find(value) : nil
end
new( obj, currency = Money.default_currency, options = {}) click to toggle source

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

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

@param [Object] obj Either the fractional value of the money,

a Money object, or a currency. (If passed a currency as the first
argument, a Money will be created in that currency with fractional value
= 0.

@param [Currency, String, Symbol] currency The currency format. @param [Hash] options Optional settings for the new Money instance @option [Money::Bank::*] :bank The exchange bank to use.

@return [Money]

@example

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">
# File lib/money/money.rb, line 340
def initialize( obj, currency = Money.default_currency, options = {})
  # For backwards compatability, if options is not a Hash, treat it as a bank parameter
  unless options.is_a?(Hash)
    options = { bank: options }
  end

  @fractional = as_d(obj.respond_to?(:fractional) ? obj.fractional : obj)
  @currency   = obj.respond_to?(:currency) ? obj.currency : Currency.wrap(currency)
  @currency ||= Money.default_currency
  @bank       = obj.respond_to?(:bank) ? obj.bank : options[:bank]
  @bank     ||= Money.default_bank

  # BigDecimal can be Infinity and NaN, money of that amount does not make sense
  raise ArgumentError, 'must be initialized with a finite value' unless @fractional.finite?
end
rounding_mode(mode = nil) { || ... } click to toggle source

Use this to return the rounding mode.

@param [BigDecimal::ROUND_MODE] mode

@return [BigDecimal::ROUND_MODE] rounding mode

# File lib/money/money.rb, line 235
def self.rounding_mode(mode = nil)
  if mode
    warn "[DEPRECATION] calling `rounding_mode` with a block is deprecated. Please use `.with_rounding_mode` instead."
    return with_rounding_mode(mode) { yield }
  end

  return Thread.current[:money_rounding_mode] if Thread.current[:money_rounding_mode]

  if @using_deprecated_default_rounding_mode
    warn '[WARNING] The default rounding mode will change from `ROUND_HALF_EVEN` to `ROUND_HALF_UP` in the ' \
         'next major release. Set it explicitly using `Money.rounding_mode=` to avoid potential problems.'
    @using_deprecated_default_rounding_mode = false
  end

  @rounding_mode
end
rounding_mode=(new_rounding_mode) click to toggle source

@attr_writer rounding_mode Use this to specify the rounding mode

# File lib/money/money.rb, line 184
def self.rounding_mode=(new_rounding_mode)
  @using_deprecated_default_rounding_mode = false
  @rounding_mode = new_rounding_mode
end
setup_defaults() click to toggle source
# File lib/money/money.rb, line 199
def self.setup_defaults
  # Set the default bank for creating new +Money+ objects.
  self.default_bank = Bank::VariableExchange.instance

  # Set the default currency for creating new +Money+ object.
  self.default_currency = Currency.new("USD")
  @using_deprecated_default_currency = true

  # Default to using i18n
  @use_i18n = true

  # Default to using legacy locale backend
  self.locale_backend = :legacy

  # Default to not using infinite precision cents
  self.default_infinite_precision = false

  # Default to bankers rounding
  self.rounding_mode = BigDecimal::ROUND_HALF_EVEN
  @using_deprecated_default_rounding_mode = true

  # Default the conversion of Rationals precision to 16
  self.conversion_precision = 16
end
use_i18n=(value) click to toggle source
# File lib/money/money.rb, line 189
def self.use_i18n=(value)
  if value
    warn '[DEPRECATION] `use_i18n` is deprecated - use `Money.locale_backend = :i18n` instead for locale based formatting'
  else
    warn '[DEPRECATION] `use_i18n` is deprecated - use `Money.locale_backend = :currency` instead for currency based formatting'
  end

  @use_i18n = value
end
with_rounding_mode(mode) { || ... } click to toggle source

Temporarily changes the rounding mode in a given block.

@param [BigDecimal::ROUND_MODE] mode

@yield The block within which rounding mode will be changed. Its return

value will also be the return value of the whole method.

@return [Object] block results

@example

fee = Money.with_rounding_mode(BigDecimal::ROUND_HALF_UP) do
  Money.new(1200) * BigDecimal('0.029')
end
# File lib/money/money.rb, line 265
def self.with_rounding_mode(mode)
  Thread.current[:money_rounding_mode] = mode
  yield
ensure
  Thread.current[:money_rounding_mode] = nil
end

Public Instance Methods

allocate(parts) click to toggle source

Splits a given amount in parts without losing pennies. The left-over pennies will be distributed round-robin amongst the parties. This means that parts listed first will likely receive more pennies than ones listed later.

Pass [2, 1, 1] as input to give twice as much to part1 as part2 or part3 which results in 50% of the cash to party1, 25% to part2, and 25% to part3. Passing a number instead of an array will split the amount evenly (without losing pennies when rounding).

@param [Array<Numeric>, Numeric] parts how amount should be distributed to parts

@return [Array<Money>]

@example

Money.new(5,   "USD").allocate([3, 7]) #=> [Money.new(2), Money.new(3)]
Money.new(100, "USD").allocate([1, 1, 1]) #=> [Money.new(34), Money.new(33), Money.new(33)]
Money.new(100, "USD").allocate(2) #=> [Money.new(50), Money.new(50)]
Money.new(100, "USD").allocate(3) #=> [Money.new(34), Money.new(33), Money.new(33)]
# File lib/money/money.rb, line 594
def allocate(parts)
  amounts = Money::Allocation.generate(fractional, parts, !Money.default_infinite_precision)
  amounts.map { |amount| dup_with(fractional: amount) }
end
Also aliased as: split
amount() click to toggle source

Returns the numerical value of the money

@return [BigDecimal]

@example

Money.new(1_00, "USD").amount    # => BigDecimal("1.00")

@see to_d @see fractional

# File lib/money/money.rb, line 385
def amount
  to_d
end
as_ca_dollar() click to toggle source

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

@return [Money]

@example

n = Money.new(100, "USD").as_ca_dollar
n.currency #=> #<Money::Currency id: cad>
# File lib/money/money.rb, line 560
def as_ca_dollar
  exchange_to("CAD")
end
as_euro() click to toggle source

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

@return [Money]

@example

n = Money.new(100, "USD").as_euro
n.currency #=> #<Money::Currency id: eur>
# File lib/money/money.rb, line 572
def as_euro
  exchange_to("EUR")
end
as_us_dollar() click to toggle source

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

@return [Money]

@example

n = Money.new(100, "CAD").as_us_dollar
n.currency #=> #<Money::Currency id: usd>
# File lib/money/money.rb, line 548
def as_us_dollar
  exchange_to("USD")
end
cents() click to toggle source

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

@return [Integer] when infinite_precision is false @return [BigDecimal] when infinite_precision is true

@see infinite_precision

# File lib/money/money.rb, line 33
def cents
  fractional
end
currency_as_string() click to toggle source

Return string representation of currency object

@return [String]

@example

Money.new(100, :USD).currency_as_string #=> "USD"
# File lib/money/money.rb, line 395
def currency_as_string
  warn "[DEPRECATION] `currency_as_string` is deprecated. Please use `.currency.to_s` instead."
  currency.to_s
end
currency_as_string=(val) click to toggle source

Set currency object using a string

@param [String] val The currency string.

@return [Money::Currency]

@example

Money.new(100).currency_as_string("CAD") #=> #<Money::Currency id: cad>
# File lib/money/money.rb, line 408
def currency_as_string=(val)
  warn "[DEPRECATION] `currency_as_string=` is deprecated - Money instances are immutable." \
    " Please use `with_currency` instead."
  @currency = Currency.wrap(val)
end
decimal_mark() click to toggle source

Returns a decimal mark according to the locale

@return [String]

# File lib/money/money.rb, line 643
def decimal_mark
  (locale_backend && locale_backend.lookup(:decimal_mark, currency)) ||
    Money::Formatter::DEFAULTS[:decimal_mark]
end
dollars() click to toggle source

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

Synonym of amount

@return [BigDecimal]

@example

Money.new(1_00, "USD").dollars   # => BigDecimal("1.00")

@see amount @see to_d @see cents

# File lib/money/money.rb, line 371
def dollars
  amount
end
dup_with(options = {}) click to toggle source
# File lib/money/money.rb, line 648
def dup_with(options = {})
  self.class.new(
    options[:fractional] || fractional,
    options[:currency] || currency,
    bank: options[:bank] || bank
  )
end
exchange_to(other_currency, &rounding_method) click to toggle source

Receive the amount of this money object in another Currency.

@param [Currency, String, Symbol] other_currency Currency to exchange to.

@yield [n] Optional block to use when rounding after exchanging one currency

for another.

@yieldparam [Float] n The resulting float after exchanging one currency for

another.

@yieldreturn [Integer]

@return [Money]

@example

Money.new(2000, "USD").exchange_to("EUR")
Money.new(2000, "USD").exchange_to("EUR") {|x| x.round}
Money.new(2000, "USD").exchange_to(Currency.new("EUR"))
# File lib/money/money.rb, line 531
def exchange_to(other_currency, &rounding_method)
  other_currency = Currency.wrap(other_currency)
  if self.currency == other_currency
    self
  else
    @bank.exchange_with(self, other_currency, &rounding_method)
  end
end
format(*rules) click to toggle source

Creates a formatted price string according to several rules.

@param [Hash] rules See {Money::Formatter Money::Formatter} for the list of formatting options

@return [String]

# File lib/money/money.rb, line 626
def format(*rules)
  Money::Formatter.new(self, *rules).to_s
end
fractional() click to toggle source

The value of the monetary amount represented in the fractional or subunit of the currency.

For example, in the US dollar currency the fractional unit is cents, and there are 100 cents in one US dollar. So given the Money representation of one US dollar, the fractional interpretation is 100.

Another example is that of the Kuwaiti dinar. In this case the fractional unit is the fils and there 1000 fils to one Kuwaiti dinar. So given the Money representation of one Kuwaiti dinar, the fractional interpretation is 1000.

@return [Integer] when infinite_precision is false @return [BigDecimal] when infinite_precision is true

@see infinite_precision

# File lib/money/money.rb, line 53
def fractional
  # Ensure we have a BigDecimal. If the Money object is created
  # from YAML, @fractional can end up being set to a Float.
  fractional = as_d(@fractional)

  return_value(fractional)
end
hash() click to toggle source

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

@return [Integer]

@example

Money.new(100).hash #=> 908351
# File lib/money/money.rb, line 421
def hash
  [fractional.hash, currency.hash].hash
end
inspect() click to toggle source

Common inspect function

@return [String]

# File lib/money/money.rb, line 438
def inspect
  "#<#{self.class.name} fractional:#{fractional} currency:#{currency}>"
end
round(rounding_mode = self.class.rounding_mode, rounding_precision = 0) click to toggle source

Round the monetary amount to smallest unit of coinage.

@note

This method is only useful when operating with infinite_precision turned
on. Without infinite_precision values are rounded to the smallest unit of
coinage automatically.

@return [Money]

@example

Money.new(10.1, 'USD').round #=> Money.new(10, 'USD')

@see

Money.default_infinite_precision
# File lib/money/money.rb, line 615
def round(rounding_mode = self.class.rounding_mode, rounding_precision = 0)
  rounded_amount = as_d(@fractional).round(rounding_precision, rounding_mode)
  dup_with(fractional: rounded_amount)
end
round_to_nearest_cash_value() click to toggle source

Round a given amount of money to the nearest possible amount in cash value. For example, in Swiss franc (CHF), the smallest possible amount of cash value is CHF 0.05. Therefore, this method rounds CHF 0.07 to CHF 0.05, and CHF 0.08 to CHF 0.10.

@return [Integer] when infinite_precision is false @return [BigDecimal] when infinite_precision is true

@see infinite_precision

# File lib/money/money.rb, line 70
def round_to_nearest_cash_value
  unless self.currency.smallest_denomination
    raise UndefinedSmallestDenomination, 'Smallest denomination of this currency is not defined'
  end

  fractional = as_d(@fractional)
  smallest_denomination = as_d(self.currency.smallest_denomination)
  rounded_value = (fractional / smallest_denomination).round(0, self.class.rounding_mode) * smallest_denomination

  return_value(rounded_value)
end
split(parts)
Alias for: allocate
symbol() click to toggle source

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

@return [String]

@example

Money.new(100, "USD").symbol #=> "$"
# File lib/money/money.rb, line 431
def symbol
  currency.symbol || "¤"
end
thousands_separator() click to toggle source

Returns a thousands separator according to the locale

@return [String]

# File lib/money/money.rb, line 634
def thousands_separator
  (locale_backend && locale_backend.lookup(:thousands_separator, currency)) ||
    Money::Formatter::DEFAULTS[:thousands_separator]
end
to_d() click to toggle source

Return the amount of money as a BigDecimal.

@return [BigDecimal]

@example

Money.us_dollar(1_00).to_d #=> BigDecimal("1.00")
# File lib/money/money.rb, line 461
def to_d
  as_d(fractional) / as_d(currency.subunit_to_unit)
end
to_f() click to toggle source

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 floats.

@return [Float]

@example

Money.us_dollar(100).to_f #=> 1.0
# File lib/money/money.rb, line 484
def to_f
  to_d.to_f
end
to_i() click to toggle source

Return the amount of money as a Integer.

@return [Integer]

@example

Money.us_dollar(1_00).to_i #=> 1
# File lib/money/money.rb, line 471
def to_i
  to_d.to_i
end
to_money(given_currency = nil) click to toggle source

Conversion to self.

@return [self]

# File lib/money/money.rb, line 506
def to_money(given_currency = nil)
  given_currency = Currency.wrap(given_currency)
  if given_currency.nil? || self.currency == given_currency
    self
  else
    exchange_to(given_currency)
  end
end
to_s() click to toggle source

Returns the amount of money as a string.

@return [String]

@example

Money.ca_dollar(100).to_s #=> "1.00"
# File lib/money/money.rb, line 448
def to_s
  format thousands_separator: '',
         no_cents_if_whole: currency.decimal_places == 0,
         symbol: false,
         ignore_defaults: true
end
with_currency(new_currency) click to toggle source

Returns a new Money instance in a given currency leaving the amount intact and not performing currency conversion.

@param [Currency, String, Symbol] new_currency Currency of the new object.

@return [self]

# File lib/money/money.rb, line 494
def with_currency(new_currency)
  new_currency = Currency.wrap(new_currency)
  if !new_currency || currency == new_currency
    self
  else
    dup_with(currency: new_currency)
  end
end

Private Instance Methods

as_d(num) click to toggle source
# File lib/money/money.rb, line 658
def as_d(num)
  if num.respond_to?(:to_d)
    num.is_a?(Rational) ? num.to_d(self.class.conversion_precision) : num.to_d
  else
    BigDecimal(num.to_s.empty? ? 0 : num.to_s)
  end
end
locale_backend() click to toggle source
# File lib/money/money.rb, line 674
def locale_backend
  self.class.locale_backend
end
return_value(value) click to toggle source
# File lib/money/money.rb, line 666
def return_value(value)
  if self.class.default_infinite_precision
    value
  else
    value.round(0, self.class.rounding_mode).to_i
  end
end