class Money
Constants
- NULL_CURRENCY
- VERSION
Attributes
Public Class Methods
# File lib/money/money.rb, line 55 def current_currency Thread.current[:money_currency] end
# File lib/money/money.rb, line 59 def current_currency=(currency) Thread.current[:money_currency] = currency end
# File lib/money/money.rb, line 77 def default_settings self.parser = MoneyParser self.default_currency = Money::NULL_CURRENCY end
# 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
# 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
# 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
# File lib/money/money.rb, line 29 def parse(*args, **kwargs) parser.parse(*args, **kwargs) end
# 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
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
# 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
# File lib/money/money.rb, line 127 def +(other) arithmetic(other) do |money| Money.new(value + money.value, calculated_currency(money.currency)) end end
# File lib/money/money.rb, line 133 def -(other) arithmetic(other) do |money| Money.new(value - money.value, calculated_currency(money.currency)) end end
# File lib/money/money.rb, line 116 def -@ Money.new(-value, currency) end
# File lib/money/money.rb, line 146 def /(numeric) raise "[Money] Dividing money objects can lose pennies. Use #split instead" end
# 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
# File lib/money/money.rb, line 154 def ==(other) eql?(other) end
# File lib/money/money.rb, line 240 def abs Money.new(value.abs, currency) end
@see Money::Allocator#allocate
# File lib/money/money.rb, line 260 def allocate(splits, strategy = :roundrobin) Money::Allocator.new(self).allocate(splits, strategy) end
@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
# File lib/money/money.rb, line 236 def as_json(*args) to_s end
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
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
# 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
# File lib/money/money.rb, line 95 def encode_with(coder) coder['value'] = @value.to_s('F') coder['currency'] = @currency.iso_code end
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
# File lib/money/money.rb, line 244 def floor Money.new(value.floor, currency) end
# 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
# File lib/money/money.rb, line 91 def init_with(coder) initialize(Helpers.value_to_decimal(coder['value']), coder['currency']) end
# File lib/money/money.rb, line 150 def inspect "#<#{self.class} value:#{self} currency:#{self.currency}>" end
# File lib/money/money.rb, line 112 def no_currency? currency.is_a?(NullCurrency) end
# File lib/money/money.rb, line 248 def round(ndigits=0) Money.new(value.round(ndigits), currency) end
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
# 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
# File lib/money/money.rb, line 208 def to_d value end
# File lib/money/money.rb, line 232 def to_json(options = {}) to_s end
# 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
# 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
# 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
# File lib/money/money.rb, line 336 def calculated_currency(other) no_currency? ? other : currency end