Class: Mint::Money
- Inherits:
-
Object
- Object
- Mint::Money
- Includes:
- Comparable
- Defined in:
- lib/minting/money/coercion.rb,
lib/minting/money/money.rb,
lib/minting/money/allocation.rb,
lib/minting/money/comparable.rb,
lib/minting/money/conversion.rb,
lib/minting/money/formatting.rb,
lib/minting/money/arithmetics.rb
Overview
:nodoc Arithmetic functions for money objects
Defined Under Namespace
Classes: CoercedNumber
Constant Summary collapse
- DEFAULT_FORMAT =
'%<symbol>s%<amount>f'.freeze
Instance Attribute Summary collapse
-
#amount ⇒ Object
readonly
Returns the value of attribute amount.
-
#currency ⇒ Object
readonly
Returns the value of attribute currency.
Instance Method Summary collapse
- #*(multiplicand) ⇒ Object
- #+(addend) ⇒ Object
- #-(subtrahend) ⇒ Object
- #-@ ⇒ Object
- #/(divisor) ⇒ Object
- #<=>(other) ⇒ Object
-
#==(other) ⇒ Object
True if both are zero, or both have same amount and same currency.
- #abs ⇒ Object
- #allocate(proportions) ⇒ Object
- #coerce(other) ⇒ Object
- #currency_code ⇒ Object
- #eql?(other) ⇒ Boolean
- #format_amount(format) ⇒ Object
- #hash ⇒ Object
-
#initialize(amount, currency) ⇒ Money
constructor
Creates a new Money immutable object with the specified amount and currency.
- #inspect ⇒ Object
-
#mint(new_amount) ⇒ Money
Returns a new Money object with the specified amount, or self if unchanged.
- #negative? ⇒ Boolean
- #nonzero? ⇒ Boolean
- #positive? ⇒ Boolean
- #same_currency?(other) ⇒ Boolean
- #split(quantity) ⇒ Object
- #succ ⇒ Object
- #to_d ⇒ Object
- #to_f ⇒ Object
- #to_html(format = DEFAULT_FORMAT) ⇒ Object
- #to_i ⇒ Object
- #to_json(*_args) ⇒ Object
- #to_r ⇒ Object
-
#to_s(format: '%<symbol>s%<amount>f', decimal: '.', thousand: ',', width: nil) ⇒ String
Formats money as a string with customizable format, thousand delimiter, and decimal.
- #zero? ⇒ Boolean
Constructor Details
#initialize(amount, currency) ⇒ Money
Creates a new Money immutable object with the specified amount and currency
11 12 13 14 15 16 17 18 19 20 21 |
# File 'lib/minting/money/money.rb', line 11 def initialize(amount, currency) raise ArgumentError, 'amount must be Numeric' unless amount.is_a?(Numeric) unless currency.is_a?(Currency) raise ArgumentError, 'currency must be a Currency object' end @amount = amount.to_r.round(currency.subunit) @currency = currency end |
Instance Attribute Details
#amount ⇒ Object (readonly)
Returns the value of attribute amount.
5 6 7 |
# File 'lib/minting/money/money.rb', line 5 def amount @amount end |
#currency ⇒ Object (readonly)
Returns the value of attribute currency.
5 6 7 |
# File 'lib/minting/money/money.rb', line 5 def currency @currency end |
Instance Method Details
#*(multiplicand) ⇒ Object
33 34 35 36 37 |
# File 'lib/minting/money/arithmetics.rb', line 33 def *(multiplicand) return mint(amount * multiplicand.to_r) if multiplicand.is_a?(Numeric) raise TypeError, "#{self} can't be multiplied by #{multiplicand}" end |
#+(addend) ⇒ Object
13 14 15 16 17 18 |
# File 'lib/minting/money/arithmetics.rb', line 13 def +(addend) return self if addend.respond_to?(:zero?) && addend.zero? return mint(amount + addend.amount) if addend.is_a?(Money) && same_currency?(addend) raise TypeError, "#{addend} can't be added to #{self}" end |
#-(subtrahend) ⇒ Object
20 21 22 23 24 25 26 27 |
# File 'lib/minting/money/arithmetics.rb', line 20 def -(subtrahend) return self if subtrahend.respond_to?(:zero?) && subtrahend.zero? if subtrahend.is_a?(Money) && same_currency?(subtrahend) return mint(amount - subtrahend.amount) end raise TypeError, "#{subtrahend} can't be subtracted from #{self}" end |
#-@ ⇒ Object
29 30 31 |
# File 'lib/minting/money/arithmetics.rb', line 29 def -@ mint(-amount) end |
#/(divisor) ⇒ Object
39 40 41 42 43 44 |
# File 'lib/minting/money/arithmetics.rb', line 39 def /(divisor) return mint(amount / divisor) if divisor.is_a?(Numeric) return amount / divisor.amount if same_currency? divisor raise TypeError, "#{self} can't be divided by #{divisor}" end |
#<=>(other) ⇒ Object
24 25 26 27 28 29 30 31 32 |
# File 'lib/minting/money/comparable.rb', line 24 def <=>(other) case other when Numeric return amount <=> other if other.zero? when Mint::Money return amount <=> other.amount if currency == other.currency end raise TypeError, "#{inspect} can't be compared to #{other.inspect}" end |
#==(other) ⇒ Object
Returns true if both are zero, or both have same amount and same currency.
8 9 10 11 12 13 |
# File 'lib/minting/money/comparable.rb', line 8 def ==(other) return true if zero? && other.respond_to?(:zero?) && other.zero? return false unless other.is_a?(Mint::Money) amount == other.amount && currency == other.currency end |
#abs ⇒ Object
5 |
# File 'lib/minting/money/arithmetics.rb', line 5 def abs = mint(amount.abs) |
#allocate(proportions) ⇒ Object
5 6 7 8 9 10 11 |
# File 'lib/minting/money/allocation.rb', line 5 def allocate(proportions) raise ArgumentError, 'Need at least 1 proportion element' if proportions.empty? whole = proportions.sum.to_r amounts = proportions.map! { |rate| (amount * rate.to_r / whole).round(currency.subunit) } allocate_left_over!(amounts: amounts, left_over: amount - amounts.sum) end |
#coerce(other) ⇒ Object
5 6 7 |
# File 'lib/minting/money/coercion.rb', line 5 def coerce(other) [CoercedNumber.new(other), self] end |
#currency_code ⇒ Object
23 24 25 |
# File 'lib/minting/money/money.rb', line 23 def currency_code currency.code end |
#eql?(other) ⇒ Boolean
34 35 36 |
# File 'lib/minting/money/comparable.rb', line 34 def eql?(other) self == other end |
#format_amount(format) ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/minting/money/formatting.rb', line 44 def format_amount(format) # binding.irb if format.is_a? Hash format = { positive: format } if format.is_a?(String) value = amount if amount.negative? && format[:negative] format = format[:negative] value = -amount elsif amount.zero? && format[:zero] format = format[:zero] else format = format[:positive] end format ||= '%<symbol>s%<amount>f' # Automatically adjust decimal places based on currency subunit adjusted_format = format.gsub(/%<amount>(\+?\d*)f/, "%<amount>\\1.#{currency.subunit}f") Kernel.format(adjusted_format, amount: value, currency: currency_code, symbol: currency.symbol) end |
#hash ⇒ Object
27 28 29 |
# File 'lib/minting/money/money.rb', line 27 def hash zero? ? 0.hash : [amount, currency.code].hash end |
#inspect ⇒ Object
38 39 40 |
# File 'lib/minting/money/money.rb', line 38 def inspect Kernel.format "[#{currency_code} %0.#{currency.subunit}f]", amount end |
#mint(new_amount) ⇒ Money
Returns a new Money object with the specified amount, or self if unchanged
34 35 36 |
# File 'lib/minting/money/money.rb', line 34 def mint(new_amount) new_amount.to_r == amount ? self : Money.new(new_amount, currency).freeze end |
#negative? ⇒ Boolean
7 |
# File 'lib/minting/money/arithmetics.rb', line 7 def negative? = amount.negative? |
#nonzero? ⇒ Boolean
38 39 40 |
# File 'lib/minting/money/comparable.rb', line 38 def nonzero? amount.nonzero? end |
#positive? ⇒ Boolean
9 |
# File 'lib/minting/money/arithmetics.rb', line 9 def positive? = amount.positive? |
#same_currency?(other) ⇒ Boolean
42 43 44 |
# File 'lib/minting/money/money.rb', line 42 def same_currency?(other) other.respond_to?(:currency) && other.currency == currency end |
#split(quantity) ⇒ Object
13 14 15 16 17 18 19 20 21 22 |
# File 'lib/minting/money/allocation.rb', line 13 def split(quantity) unless quantity.positive? && quantity.integer? raise ArgumentError, 'quantity must be an integer > 0' end fraction = (amount / quantity).round(currency.subunit) allocate_left_over!(amounts: Array.new(quantity, fraction), left_over: amount - (fraction * quantity)) end |
#succ ⇒ Object
11 |
# File 'lib/minting/money/arithmetics.rb', line 11 def succ = mint(amount + currency.minimum_amount) |
#to_d ⇒ Object
4 5 6 |
# File 'lib/minting/money/conversion.rb', line 4 def to_d amount.to_d 0 end |
#to_f ⇒ Object
8 9 10 |
# File 'lib/minting/money/conversion.rb', line 8 def to_f amount.to_f end |
#to_html(format = DEFAULT_FORMAT) ⇒ Object
12 13 14 15 |
# File 'lib/minting/money/conversion.rb', line 12 def to_html(format = DEFAULT_FORMAT) title = Kernel.format("#{currency_code} %0.#{currency.subunit}f", amount) %(<data class='money' title='#{title}'>#{to_s(format: format)}</data>) end |
#to_i ⇒ Object
17 18 19 |
# File 'lib/minting/money/conversion.rb', line 17 def to_i amount.to_i end |
#to_json(*_args) ⇒ Object
21 22 23 24 25 26 27 |
# File 'lib/minting/money/conversion.rb', line 21 def to_json(*_args) subunit = currency.subunit Kernel.format( %({"currency": "#{currency_code}", "amount": "%0.#{subunit}f"}), amount ) end |
#to_r ⇒ Object
29 30 31 |
# File 'lib/minting/money/conversion.rb', line 29 def to_r amount end |
#to_s(format: '%<symbol>s%<amount>f', decimal: '.', thousand: ',', width: nil) ⇒ String
Formats money as a string with customizable format, thousand delimiter, and decimal
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/minting/money/formatting.rb', line 27 def to_s(format: '%<symbol>s%<amount>f', decimal: '.', thousand: ',', width: nil) raise ArgumentError, 'Invalid format' unless format.is_a?(String) || format.is_a?(Hash) formatted = format_amount(format) formatted.tr!('.', decimal) if decimal != '.' unless thousand.empty? # Regular expression courtesy of Money gem # Matches digits followed by groups of 3 digits until non-digit or end formatted.gsub!(/(\d)(?=(?:\d{3})+(?:[^\d]{1}|$))/, "\\1#{thousand}") end formatted = formatted.rjust(width) if width formatted end |
#zero? ⇒ Boolean
42 43 44 |
# File 'lib/minting/money/comparable.rb', line 42 def zero? amount.zero? end |