class Money

Constants

NULL_CURRENCY
VERSION

Attributes

default_currency[RW]
parser[RW]
currency[R]
value[R]

Public Class Methods

current_currency() click to toggle source
# File lib/money/money.rb, line 55
def current_currency
  Thread.current[:money_currency]
end
current_currency=(currency) click to toggle source
# File lib/money/money.rb, line 59
def current_currency=(currency)
  Thread.current[:money_currency] = currency
end
default_settings() click to toggle source
# File lib/money/money.rb, line 77
def default_settings
  self.parser = MoneyParser
  self.default_currency = Money::NULL_CURRENCY
end
from_amount(value = 0, currency = nil)
Alias for: new
from_subunits(subunits, currency_iso, format: :iso4217) click to toggle source
# File lib/money/money.rb, line 33
def from_subunits(subunits, currency_iso, format: :iso4217)
  currency = Helpers.value_to_currency(currency_iso)

  subunit_to_unit_value = if format == :iso4217
    currency.subunit_to_unit
  elsif format == :stripe
    Helpers::STRIPE_SUBUNIT_OVERRIDE.fetch(currency.iso_code, currency.subunit_to_unit)
  else
    raise ArgumentError, "unknown format #{format}"
  end

  value = Helpers.value_to_decimal(subunits) / subunit_to_unit_value
  new(value, currency)
end
new(value = 0, currency = nil) click to toggle source
Calls superclass method
# File lib/money/money.rb, line 16
def new(value = 0, currency = nil)
  value = Helpers.value_to_decimal(value)
  currency = Helpers.value_to_currency(currency)

  if value.zero?
    @@zero_money ||= {}
    @@zero_money[currency.iso_code] ||= super(Helpers::DECIMAL_ZERO, currency)
  else
    super(value, currency)
  end
end
Also aliased as: from_amount
new(value, currency) click to toggle source
# File lib/money/money.rb, line 84
def initialize(value, currency)
  raise ArgumentError if value.nan?
  @currency = Helpers.value_to_currency(currency)
  @value = BigDecimal(value.round(@currency.minor_units))
  freeze
end
parse(*args, **kwargs) click to toggle source
# File lib/money/money.rb, line 29
def parse(*args, **kwargs)
  parser.parse(*args, **kwargs)
end
rational(money1, money2) click to toggle source
# File lib/money/money.rb, line 48
def rational(money1, money2)
  money1.send(:arithmetic, money2) do
    factor = money1.currency.subunit_to_unit * money2.currency.subunit_to_unit
    Rational((money1.value * factor).to_i, (money2.value * factor).to_i)
  end
end
with_currency(new_currency) { || ... } click to toggle source

Set Money.default_currency inside the supplied block, resets it to the previous value when done to prevent leaking state. Similar to I18n.with_locale and ActiveSupport's Time.use_zone. This won't affect instances being created with explicitly set currency.

# File lib/money/money.rb, line 67
def with_currency(new_currency)
  begin
    old_currency = Money.current_currency
    Money.current_currency = new_currency
    yield
  ensure
    Money.current_currency = old_currency
  end
end

Public Instance Methods

*(numeric) click to toggle source
# File lib/money/money.rb, line 139
def *(numeric)
  unless numeric.is_a?(Numeric)
    Money.deprecate("Multiplying Money with #{numeric.class.name} is deprecated and will be removed in the next major release.")
  end
  Money.new(value.to_r * numeric, currency)
end
+(other) click to toggle source
# File lib/money/money.rb, line 127
def +(other)
  arithmetic(other) do |money|
    Money.new(value + money.value, calculated_currency(money.currency))
  end
end
-(other) click to toggle source
# File lib/money/money.rb, line 133
def -(other)
  arithmetic(other) do |money|
    Money.new(value - money.value, calculated_currency(money.currency))
  end
end
-@() click to toggle source
# File lib/money/money.rb, line 116
def -@
  Money.new(-value, currency)
end
/(numeric) click to toggle source
# File lib/money/money.rb, line 146
def /(numeric)
  raise "[Money] Dividing money objects can lose pennies. Use #split instead"
end
<=>(other) click to toggle source
# File lib/money/money.rb, line 120
def <=>(other)
  return unless other.respond_to?(:to_money)
  arithmetic(other) do |money|
    value <=> money.value
  end
end
==(other) click to toggle source
# File lib/money/money.rb, line 154
def ==(other)
  eql?(other)
end
abs() click to toggle source
# File lib/money/money.rb, line 240
def abs
  Money.new(value.abs, currency)
end
allocate(splits, strategy = :roundrobin) click to toggle source

@see Money::Allocator#allocate

# File lib/money/money.rb, line 260
def allocate(splits, strategy = :roundrobin)
  Money::Allocator.new(self).allocate(splits, strategy)
end
allocate_max_amounts(maximums) click to toggle source

@see Money::Allocator#allocate_max_amounts

# File lib/money/money.rb, line 265
def allocate_max_amounts(maximums)
  Money::Allocator.new(self).allocate_max_amounts(maximums)
end
as_json(*args) click to toggle source
# File lib/money/money.rb, line 236
def as_json(*args)
  to_s
end
calculate_splits(num) click to toggle source

Calculate the splits evenly without losing pennies. Returns the number of high and low splits and the value of the high and low splits. Where high represents the Money value with the extra penny and low a Money without the extra penny.

@param [2] number of parties.

@return [Hash<Money, Integer>]

@example

Money.new(100, "USD").calculate_splits(3) #=> {Money.new(34) => 1, Money.new(33) => 2}
# File lib/money/money.rb, line 292
def calculate_splits(num)
  raise ArgumentError, "need at least one party" if num < 1
  subunits = self.subunits
  low = Money.from_subunits(subunits / num, currency)
  high = Money.from_subunits(low.subunits + 1, currency)

  num_high = subunits % num

  {}.tap do |result|
    result[high] = num_high if num_high > 0
    result[low] = num - num_high
  end
end
clamp(min, max) click to toggle source

Clamps the value to be within the specified minimum and maximum. Returns self if the value is within bounds, otherwise a new Money object with the closest min or max value.

@example

Money.new(50, "CAD").clamp(1, 100) #=> Money.new(50, "CAD")

Money.new(120, "CAD").clamp(0, 100) #=> Money.new(100, "CAD")
# File lib/money/money.rb, line 314
def clamp(min, max)
  raise ArgumentError, 'min cannot be greater than max' if min > max

  clamped_value = min if self.value < min
  clamped_value = max if self.value > max

  if clamped_value.nil?
    self
  else
    Money.new(clamped_value, self.currency)
  end
end
coerce(other) click to toggle source
# File lib/money/money.rb, line 189
def coerce(other)
  raise TypeError, "Money can't be coerced into #{other.class}" unless other.is_a?(Numeric)
  [ReverseOperationProxy.new(other), self]
end
encode_with(coder) click to toggle source
# File lib/money/money.rb, line 95
def encode_with(coder)
  coder['value'] = @value.to_s('F')
  coder['currency'] = @currency.iso_code
end
eql?(other) click to toggle source

TODO: Remove once cross-currency mathematical operations are no longer allowed

# File lib/money/money.rb, line 159
def eql?(other)
  return false unless other.is_a?(Money)
  return false unless currency.compatible?(other.currency)
  value == other.value
end
floor() click to toggle source
# File lib/money/money.rb, line 244
def floor
  Money.new(value.floor, currency)
end
fraction(rate) click to toggle source
# File lib/money/money.rb, line 252
def fraction(rate)
  raise ArgumentError, "rate should be positive" if rate < 0

  result = value / (1 + rate)
  Money.new(result, currency)
end
init_with(coder) click to toggle source
# File lib/money/money.rb, line 91
def init_with(coder)
  initialize(Helpers.value_to_decimal(coder['value']), coder['currency'])
end
inspect() click to toggle source
# File lib/money/money.rb, line 150
def inspect
  "#<#{self.class} value:#{self} currency:#{self.currency}>"
end
no_currency?() click to toggle source
# File lib/money/money.rb, line 112
def no_currency?
  currency.is_a?(NullCurrency)
end
round(ndigits=0) click to toggle source
# File lib/money/money.rb, line 248
def round(ndigits=0)
  Money.new(value.round(ndigits), currency)
end
split(num) click to toggle source

Split money amongst parties evenly without losing pennies.

@param [2] number of parties.

@return [Array<Money, Money, Money>]

@example

Money.new(100, "USD").split(3) #=> [Money.new(34), Money.new(33), Money.new(33)]
# File lib/money/money.rb, line 277
def split(num)
  calculate_splits(num).sum([]) { |value, count| Array.new(count, value) }
end
subunits(format: :iso4217) click to toggle source
# File lib/money/money.rb, line 100
def subunits(format: :iso4217)
  subunit_to_unit_value = if format == :iso4217
    @currency.subunit_to_unit
  elsif format == :stripe
    Helpers::STRIPE_SUBUNIT_OVERRIDE.fetch(@currency.iso_code, @currency.subunit_to_unit)
  else
    raise ArgumentError, "unknown format #{format}"
  end

  (@value * subunit_to_unit_value).to_i
end
to_d() click to toggle source
# File lib/money/money.rb, line 208
def to_d
  value
end
to_json(options = {}) click to toggle source
# File lib/money/money.rb, line 232
def to_json(options = {})
  to_s
end
to_money(curr = nil) click to toggle source
# File lib/money/money.rb, line 194
def to_money(curr = nil)
  if !curr.nil? && no_currency?
    return Money.new(value, curr)
  end

  curr = Helpers.value_to_currency(curr)
  unless currency.compatible?(curr)
    Money.deprecate("mathematical operation not permitted for Money objects with different currencies #{curr} and #{currency}. " \
      "A Money::IncompatibleCurrencyError will raise in the next major release")
  end

  self
end
to_s(style = nil) click to toggle source
# File lib/money/money.rb, line 212
def to_s(style = nil)
  units = case style
  when :legacy_dollars
    2
  when :amount, nil
    currency.minor_units
  else
    raise ArgumentError, "Unexpected style: #{style}"
  end

  rounded_value = value.round(units)
  if units == 0
    sprintf("%d", rounded_value)
  else
    sign = rounded_value < 0 ? '-' : ''
    rounded_value = rounded_value.abs
    sprintf("%s%d.%0#{units}d", sign, rounded_value.truncate, rounded_value.frac * (10 ** units))
  end
end

Private Instance Methods

arithmetic(money_or_numeric) { |other| ... } click to toggle source
# File lib/money/money.rb, line 329
def arithmetic(money_or_numeric)
  raise TypeError, "#{money_or_numeric.class.name} can't be coerced into Money" unless money_or_numeric.respond_to?(:to_money)
  other = money_or_numeric.to_money(currency)

  yield(other)
end
calculated_currency(other) click to toggle source
# File lib/money/money.rb, line 336
def calculated_currency(other)
  no_currency? ? other : currency
end