Class: Mint::Money
- Inherits:
-
Object
- Object
- Mint::Money
- Includes:
- Comparable
- Defined in:
- lib/minting/money/money.rb,
lib/minting/money/coercion.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,
lib/minting/money/constructors.rb
Overview
Money constructors
Constant Summary collapse
- DEFAULT_FORMAT =
The default display format pattern for formatting monetary values. Uses ‘%<symbol>s` for the currency symbol and `%<amount>f` for the rounded amount.
'%<symbol>s%<amount>f'
Instance Attribute Summary collapse
-
#amount ⇒ Object
readonly
Returns the value of attribute amount.
-
#currency ⇒ Object
readonly
Returns the value of attribute currency.
Class Method Summary collapse
-
.create(amount, currency) ⇒ Object
Creates a new Money immutable object with the specified amount and currency.
-
.from_fractional(fractional, currency) ⇒ Money
Builds a Money from a fractional (smallest-unit) Integer amount.
Instance Method Summary collapse
-
#*(multiplicand) ⇒ Money
private
Performs multiplication of the monetary value by a standard scalar Numeric.
-
#**(exponent) ⇒ Money
private
Performs exponentiation of the monetary value by a standard scalar Numeric.
-
#+(addend) ⇒ Money
private
Performs addition with another Money instance or standard zero Numeric.
-
#-(subtrahend) ⇒ Money
private
Performs subtraction with another Money instance or standard zero Numeric.
-
#-@ ⇒ Money
private
Unary negation operator.
-
#/(divisor) ⇒ Money, Numeric
private
Performs division of the monetary value by a scalar Numeric or identical currency Money.
- #<=>(other) ⇒ Object private
-
#==(other) ⇒ Object
private
True if both are zero, or both have same amount and same currency.
-
#abs ⇒ Money
private
Returns the absolute value of the monetary amount as a new Money instance.
-
#allocate(proportions) ⇒ Array<Money>
private
Proportionally allocates the monetary amount among a list of ratios.
-
#clamp(min_or_range, max = nil) ⇒ Money
Constrains
selfto the inclusive range [min,max]. -
#coerce(other) ⇒ Array(CoercedNumber, Money)
private
Allows Money to interact seamlessly as the right-hand operand in Numeric arithmetic.
-
#currency_code ⇒ String
Returns the ISO 3-letter currency code string.
- #eql?(other) ⇒ Boolean private
-
#fractional ⇒ Integer
Returns the monetary amount expressed in the currency’s smallest unit (fractional units).
-
#hash ⇒ Integer
Generates a stable hash key for Money instances.
-
#inspect ⇒ String
Returns a standard developer-oriented string inspection of the Money object.
-
#mint(new_amount) ⇒ Money
Returns a new Money object with the specified amount, or self if unchanged.
-
#negative? ⇒ Boolean
private
Returns true if the monetary amount is less than zero.
- #nonzero? ⇒ Boolean private
-
#positive? ⇒ Boolean
private
Returns true if the monetary amount is greater than zero.
-
#same_currency?(other) ⇒ Boolean
Helper method to verify if another object has the identical currency.
-
#split(slices) ⇒ Array<Money>
private
Splits the monetary amount into a given quantity of equal parts.
-
#succ ⇒ Money
private
Returns the successor of the Money instance by adding the minimum possible subunit amount.
-
#to_d ⇒ BigDecimal
private
Converts the monetary amount to a BigDecimal object.
-
#to_f ⇒ Float
private
Converts the monetary amount to a standard float.
-
#to_hash ⇒ Hash
private
Returns a Hash representation of the money instance.
-
#to_html(format = DEFAULT_FORMAT) ⇒ String
private
Renders a safe HTML5 ‘<data>` element containing the formatted currency.
-
#to_i ⇒ Integer
private
Truncates and converts the monetary amount to an Integer.
-
#to_json(*_args) ⇒ String
private
Serializes the money instance to a standard JSON object containing the amount and currency.
-
#to_r ⇒ Rational
private
Returns the exact internal Rational representation of the monetary amount.
-
#to_s(format: '%<symbol>s%<amount>f', decimal: '.', thousand: ',', width: nil) ⇒ String
private
Formats money as a string with customizable format, thousand delimiter, and decimal.
- #zero? ⇒ Boolean private
Instance Attribute Details
#amount ⇒ Object (readonly)
Returns the value of attribute amount.
10 11 12 |
# File 'lib/minting/money/money.rb', line 10 def amount @amount end |
#currency ⇒ Object (readonly)
Returns the value of attribute currency.
10 11 12 |
# File 'lib/minting/money/money.rb', line 10 def currency @currency end |
Class Method Details
.create(amount, currency) ⇒ Object
Creates a new Money immutable object with the specified amount and currency
10 11 12 13 14 15 16 17 |
# File 'lib/minting/money/constructors.rb', line 10 def self.create(amount, currency) raise ArgumentError, 'amount must be Numeric' unless amount.is_a?(Numeric) checked_currency = Mint.currency(currency) raise ArgumentError, "Currency not found (#{currency})" unless checked_currency new(checked_currency.normalize_amount(amount), checked_currency) end |
.from_fractional(fractional, currency) ⇒ Money
Builds a Money from a fractional (smallest-unit) Integer amount. This is the inverse of #fractional: for USD, the fractional unit is 1 cent; for JPY it is 1 yen; for IQD it is 1 dinar (subunit 3).
37 38 39 40 41 42 43 44 45 |
# File 'lib/minting/money/constructors.rb', line 37 def self.from_fractional(fractional, currency) raise ArgumentError, 'fractional must be an Integer' unless fractional.is_a?(Integer) checked_currency = Mint.currency(currency) raise ArgumentError, "Currency not found (#{currency})" unless checked_currency amount = Rational(fractional, checked_currency.fractional_multiplier) new(amount, checked_currency) end |
Instance Method Details
#*(multiplicand) ⇒ Money
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Performs multiplication of the monetary value by a standard scalar Numeric.
63 64 65 66 67 |
# File 'lib/minting/money/arithmetics.rb', line 63 def *(multiplicand) raise TypeError, "#{self} can't be multiplied by #{multiplicand}" unless multiplicand.is_a?(Numeric) mint(amount * multiplicand) end |
#**(exponent) ⇒ Money
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Performs exponentiation of the monetary value by a standard scalar Numeric.
88 89 90 91 92 |
# File 'lib/minting/money/arithmetics.rb', line 88 def **(exponent) return mint(amount**exponent) if exponent.is_a?(Numeric) raise TypeError, "#{self} can't be powered by #{exponent}" end |
#+(addend) ⇒ Money
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Performs addition with another Mint::Money instance or standard zero Numeric.
32 33 34 35 36 37 38 |
# File 'lib/minting/money/arithmetics.rb', line 32 def +(addend) case addend in 0 then self in Money if same_currency?(addend) then mint(amount + addend.amount) else raise TypeError, "#{addend} can't be added to #{self}" end end |
#-(subtrahend) ⇒ Money
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Performs subtraction with another Mint::Money instance or standard zero Numeric.
45 46 47 48 49 50 51 |
# File 'lib/minting/money/arithmetics.rb', line 45 def -(subtrahend) case subtrahend when 0 then return self when Money then return mint(amount - subtrahend.amount) if same_currency?(subtrahend) end raise TypeError, "#{subtrahend} can't be subtracted from #{self}" end |
#-@ ⇒ Money
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Unary negation operator. Returns a new Mint::Money instance with the inverted sign.
56 |
# File 'lib/minting/money/arithmetics.rb', line 56 def -@ = mint(-amount) |
#/(divisor) ⇒ Money, Numeric
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Performs division of the monetary value by a scalar Numeric or identical currency Mint::Money.
75 76 77 78 79 80 81 |
# File 'lib/minting/money/arithmetics.rb', line 75 def /(divisor) case divisor when Numeric then return mint(amount / divisor) when Money then return amount / divisor.amount if same_currency? divisor end raise TypeError, "#{self} can't be divided by #{divisor}" end |
#<=>(other) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
32 33 34 35 36 37 38 |
# File 'lib/minting/money/comparable.rb', line 32 def <=>(other) case other in 0 then amount <=> other in Mint::Money if same_currency?(other) then amount <=> other.amount else raise TypeError, "#{inspect} can't be compared to #{other.inspect}" end end |
#==(other) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns true if both are zero, or both have same amount and same currency.
9 10 11 12 13 14 15 |
# File 'lib/minting/money/comparable.rb', line 9 def ==(other) case other when 0 then zero? when Mint::Money then amount == other.amount && currency == other.currency else false end end |
#abs ⇒ Money
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns the absolute value of the monetary amount as a new Mint::Money instance.
9 |
# File 'lib/minting/money/arithmetics.rb', line 9 def abs = mint(amount.abs) |
#allocate(proportions) ⇒ Array<Money>
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Proportionally allocates the monetary amount among a list of ratios. Disperses any subunit rounding amounts across the initial slots
15 16 17 18 19 20 21 22 23 |
# File 'lib/minting/money/allocation.rb', line 15 def allocate(proportions) whole = proportions.sum.to_r raise ArgumentError, 'Need at least 1 proportion element' if proportions.empty? raise ArgumentError, 'Proportions total must not be zero' if whole.zero? subunit = currency.subunit amounts = proportions.map { |rate| Rational(amount * rate, whole).round(subunit) } allocate_left_over(amounts: amounts, left_over: amount - amounts.sum) end |
#clamp(min_or_range, max = nil) ⇒ Money
Constrains self to the inclusive range [min, max].
Bounds may be:
-
nil meaning no boundary
-
same-currency Mint::Money or Range
-
Numeric amount, or Range
Numeric is interpreted as an amount in self‘s currency, so the common pricing idiom price.clamp(0, 100) reads as “0 to 100 in the same currency as price”.
When self is already in range the receiver is returned (no new object allocated). When out of range, the nearest bound is returned as a new frozen Mint::Money in self‘s currency.
83 84 85 86 87 88 89 90 91 92 |
# File 'lib/minting/money/money.rb', line 83 def clamp(min_or_range, max = nil) if min_or_range.is_a?(Range) raise(ArgumentError, "Either amount range alone or two amounts accepted: #{max}") if max min, max = min_or_range.minmax else min = min_or_range end mint(amount.clamp(normalize_boundary(min), normalize_boundary(max))) end |
#coerce(other) ⇒ Array(CoercedNumber, Money)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Allows Mint::Money to interact seamlessly as the right-hand operand in Numeric arithmetic. This enables expressions like ‘5 * money` where `5` is a Numeric and `money` is a Money object.
14 15 16 |
# File 'lib/minting/money/coercion.rb', line 14 def coerce(other) [CoercedNumber.new(other), self] end |
#currency_code ⇒ String
Returns the ISO 3-letter currency code string.
17 |
# File 'lib/minting/money/money.rb', line 17 def currency_code = currency.code |
#eql?(other) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
17 18 19 20 21 |
# File 'lib/minting/money/comparable.rb', line 17 def eql?(other) other.is_a?(Mint::Money) && amount == other.amount && currency == other.currency end |
#fractional ⇒ Integer
Returns the monetary amount expressed in the currency’s smallest unit (fractional units). For example, cents for USD (subunit 2), yen for JPY (subunit 0), fils for IQD (subunit 3).
27 |
# File 'lib/minting/money/money.rb', line 27 def fractional = (amount * currency.fractional_multiplier).to_i |
#hash ⇒ Integer
Generates a stable hash key for Money instances.
32 |
# File 'lib/minting/money/money.rb', line 32 def hash = [amount, currency_code].hash |
#inspect ⇒ String
Returns a standard developer-oriented string inspection of the Money object.
37 38 39 |
# File 'lib/minting/money/money.rb', line 37 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. This is the primary method for creating a modified copy of a Money instance while preserving immutability.
57 58 59 60 |
# File 'lib/minting/money/constructors.rb', line 57 def mint(new_amount) new_amount = currency.normalize_amount(new_amount) new_amount == amount ? self : Money.new(new_amount, currency) end |
#negative? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns true if the monetary amount is less than zero.
14 |
# File 'lib/minting/money/arithmetics.rb', line 14 def negative? = amount.negative? |
#nonzero? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
40 |
# File 'lib/minting/money/comparable.rb', line 40 def nonzero? = amount.nonzero? |
#positive? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns true if the monetary amount is greater than zero.
19 |
# File 'lib/minting/money/arithmetics.rb', line 19 def positive? = amount.positive? |
#same_currency?(other) ⇒ Boolean
Helper method to verify if another object has the identical currency.
45 |
# File 'lib/minting/money/money.rb', line 45 def same_currency?(other) = other.currency == currency |
#split(slices) ⇒ Array<Money>
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Splits the monetary amount into a given quantity of equal parts. Disperses any fractional subunit rounding differences across the initial slots so that the sum is preserved.
36 37 38 39 40 41 42 |
# File 'lib/minting/money/allocation.rb', line 36 def split(slices) raise ArgumentError, 'Slices quantity must be an poitive integer' unless slices.positive? && slices.integer? fraction = (amount / slices).round(currency.subunit) allocate_left_over(amounts: Array.new(slices, fraction), left_over: amount - (fraction * slices)) end |
#succ ⇒ Money
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns the successor of the Money instance by adding the minimum possible subunit amount. Enables standard ranges and stepping (e.g. ‘1.dollar..10.dollars`).
25 |
# File 'lib/minting/money/arithmetics.rb', line 25 def succ = mint(amount + currency.minimum_amount) |
#to_d ⇒ BigDecimal
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Converts the monetary amount to a BigDecimal object.
14 |
# File 'lib/minting/money/conversion.rb', line 14 def to_d = amount.to_d 0 |
#to_f ⇒ Float
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Converts the monetary amount to a standard float. Note: Using float conversion loses precision guarantees.
20 |
# File 'lib/minting/money/conversion.rb', line 20 def to_f = amount.to_f |
#to_hash ⇒ Hash
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns a Hash representation of the money instance.
47 48 49 |
# File 'lib/minting/money/conversion.rb', line 47 def to_hash { currency: currency_code, amount: Kernel.format("%0.#{currency.subunit}f", amount) } end |
#to_html(format = DEFAULT_FORMAT) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Renders a safe HTML5 ‘<data>` element containing the formatted currency. Embeds the ISO currency description and raw value as the metadata `title` attribute.
27 28 29 30 31 |
# File 'lib/minting/money/conversion.rb', line 27 def to_html(format = DEFAULT_FORMAT) title = Kernel.format("#{currency_code} %0.#{currency.subunit}f", amount) body = to_s(format: format) %(<data class='money' title='#{title}'>#{ERB::Util.html_escape(body)}</data>) end |
#to_i ⇒ Integer
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Truncates and converts the monetary amount to an Integer.
39 |
# File 'lib/minting/money/conversion.rb', line 39 def to_i = amount.to_i |
#to_json(*_args) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Serializes the money instance to a standard JSON object containing the amount and currency. Highly optimized to run without external dependencies.
55 56 57 58 59 |
# File 'lib/minting/money/conversion.rb', line 55 def to_json(*_args) Kernel.format( %({"currency": "#{currency_code}", "amount": "%0.#{currency.subunit}f"}), amount ) end |
#to_r ⇒ Rational
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns the exact internal Rational representation of the monetary amount.
64 |
# File 'lib/minting/money/conversion.rb', line 64 def to_r = amount |
#to_s(format: '%<symbol>s%<amount>f', decimal: '.', thousand: ',', width: nil) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Formats money as a string with customizable format, thousand delimiter, and decimal
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/minting/money/formatting.rb', line 46 def to_s(format: '%<symbol>s%<amount>f', decimal: '.', thousand: ',', width: nil) case format when {}, '' then raise ArgumentError, 'format must not be empty' when Hash then validate_format_hash(format) when String then format = { positive: format } else raise ArgumentError, 'Invalid format. Only String or Hash are accepted' end 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 width ? formatted.rjust(width) : formatted end |
#zero? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
42 |
# File 'lib/minting/money/comparable.rb', line 42 def zero? = amount.zero? |