Class: Mint::Money
- Inherits:
-
Object
- Object
- Mint::Money
- Includes:
- Comparable
- Defined in:
- lib/minting/money/parse.rb,
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
Overview
Formatting functionality for Money objects
Defined Under Namespace
Classes: CoercedNumber
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'.freeze
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
-
.parse(input, currency = nil) ⇒ Money
Parses a human-readable money string into a Money object.
-
.zero ⇒ Object
Returns default zero no currency money.
Instance Method Summary collapse
-
#*(multiplicand) ⇒ Money
Performs multiplication of the monetary value by a standard scalar Numeric.
-
#+(addend) ⇒ Money
Performs addition with another Money instance or standard zero Numeric.
-
#-(subtrahend) ⇒ Money
Performs subtraction with another Money instance or standard zero Numeric.
-
#-@ ⇒ Money
Unary negation operator.
-
#/(divisor) ⇒ Money, Numeric
Performs division of the monetary value by a scalar Numeric or identical currency Money.
- #<=>(other) ⇒ Object
-
#==(other) ⇒ Object
True if both are zero, or both have same amount and same currency.
-
#abs ⇒ Money
Returns the absolute value of the monetary amount as a new Money instance.
-
#allocate(proportions) ⇒ Array<Money>
Proportionally allocates the monetary amount among a list of ratios.
-
#coerce(other) ⇒ Array(CoercedNumber, Money)
Implements the standard Ruby coercion protocol.
-
#currency_code ⇒ String
Returns the ISO 3-letter currency code string.
- #eql?(other) ⇒ Boolean
-
#hash ⇒ Integer
Generates a stable hash key for Money instances.
-
#initialize(amount, currency) ⇒ Money
constructor
Creates a new Money immutable object with the specified amount and currency.
-
#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
Returns true if the monetary amount is less than zero.
- #nonzero? ⇒ Boolean
-
#positive? ⇒ Boolean
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(quantity) ⇒ Array<Money>
Splits the monetary amount into a given quantity of equal parts.
-
#succ ⇒ Money
Returns the successor of the Money instance by adding the minimum possible subunit amount.
-
#to_d ⇒ BigDecimal
Converts the monetary amount to a BigDecimal object.
-
#to_f ⇒ Float
Converts the monetary amount to a standard float.
-
#to_html(format = DEFAULT_FORMAT) ⇒ String
Renders a safe HTML5 ‘<data>` element containing the formatted currency.
-
#to_i ⇒ Integer
Truncates and converts the monetary amount to an Integer.
-
#to_json(*_args) ⇒ String
Serializes the money instance to a standard JSON object containing the amount and currency.
-
#to_r ⇒ Rational
Returns the exact internal Rational representation of the monetary amount.
-
#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
13 14 15 16 17 18 19 20 |
# File 'lib/minting/money/money.rb', line 13 def initialize(amount, currency) raise ArgumentError, 'amount must be Numeric' unless amount.is_a?(Numeric) raise ArgumentError, 'currency must be a Currency object' unless currency.is_a?(Currency) @amount = amount.to_r.round(currency.subunit) @currency = currency freeze end |
Instance Attribute Details
#amount ⇒ Object (readonly)
Returns the value of attribute amount.
7 8 9 |
# File 'lib/minting/money/money.rb', line 7 def amount @amount end |
#currency ⇒ Object (readonly)
Returns the value of attribute currency.
7 8 9 |
# File 'lib/minting/money/money.rb', line 7 def currency @currency end |
Class Method Details
.parse(input, currency = nil) ⇒ Money
Parses a human-readable money string into a Mint::Money object.
19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/minting/money/parse.rb', line 19 def self.parse(input, currency = nil) raise ArgumentError, 'input must be a String' unless input.is_a?(String) input = input.strip raise ArgumentError, 'input cannot be empty' if input.empty? currency = currency ? Mint.currency(currency) : parse_currency(input) raise ArgumentError, "Currency [#{currency}] not registered" unless currency amount = parse_amount(input) new(amount, currency) end |
.zero ⇒ Object
Returns default zero no currency money
53 |
# File 'lib/minting/money/money.rb', line 53 def self.zero = @zero ||= new(0, Mint.currencies('XXX')) |
Instance Method Details
#*(multiplicand) ⇒ Money
Performs multiplication of the monetary value by a standard scalar Numeric.
62 63 64 65 66 |
# File 'lib/minting/money/arithmetics.rb', line 62 def *(multiplicand) return mint(amount * multiplicand.to_r) if multiplicand.is_a?(Numeric) raise TypeError, "#{self} can't be multiplied by #{multiplicand}" end |
#+(addend) ⇒ Money
Performs addition with another Mint::Money instance or standard zero Numeric.
29 30 31 32 33 34 |
# File 'lib/minting/money/arithmetics.rb', line 29 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) ⇒ Money
Performs subtraction with another Mint::Money instance or standard zero Numeric.
41 42 43 44 45 46 47 48 |
# File 'lib/minting/money/arithmetics.rb', line 41 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 |
#-@ ⇒ Money
Unary negation operator. Returns a new Mint::Money instance with the inverted sign.
53 54 55 |
# File 'lib/minting/money/arithmetics.rb', line 53 def -@ mint(-amount) end |
#/(divisor) ⇒ Money, Numeric
Performs division of the monetary value by a scalar Numeric or identical currency Mint::Money.
74 75 76 77 78 79 |
# File 'lib/minting/money/arithmetics.rb', line 74 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
29 30 31 32 33 34 35 36 37 |
# File 'lib/minting/money/comparable.rb', line 29 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 |
# File 'lib/minting/money/comparable.rb', line 8 def ==(other) return true if zero? && other.respond_to?(:zero?) && other.zero? eql?(other) end |
#abs ⇒ Money
Returns the absolute value of the monetary amount as a new Mint::Money instance.
6 |
# File 'lib/minting/money/arithmetics.rb', line 6 def abs = mint(amount.abs) |
#allocate(proportions) ⇒ Array<Money>
Proportionally allocates the monetary amount among a list of ratios. Disperses any subunit rounding amounts across the initial slots
12 13 14 15 16 17 18 19 |
# File 'lib/minting/money/allocation.rb', line 12 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? 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) ⇒ Array(CoercedNumber, Money)
Implements the standard Ruby coercion protocol. Allows Mint::Money to interact seamlessly as the right-hand operand in Numeric arithmetic.
8 9 10 |
# File 'lib/minting/money/coercion.rb', line 8 def coerce(other) [CoercedNumber.new(other), self] end |
#currency_code ⇒ String
Returns the ISO 3-letter currency code string.
25 |
# File 'lib/minting/money/money.rb', line 25 def currency_code = currency.code |
#eql?(other) ⇒ Boolean
14 15 16 17 18 |
# File 'lib/minting/money/comparable.rb', line 14 def eql?(other) other.is_a?(Mint::Money) && amount == other.amount && currency == other.currency end |
#hash ⇒ Integer
Generates a stable hash key for Money instances.
30 |
# File 'lib/minting/money/money.rb', line 30 def hash = [amount, currency_code].hash |
#inspect ⇒ String
Returns a standard developer-oriented string inspection of the Money object.
42 43 44 |
# File 'lib/minting/money/money.rb', line 42 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
35 36 37 |
# File 'lib/minting/money/money.rb', line 35 def mint(new_amount) new_amount.to_r == amount ? self : Money.new(new_amount, currency) end |
#negative? ⇒ Boolean
Returns true if the monetary amount is less than zero.
11 |
# File 'lib/minting/money/arithmetics.rb', line 11 def negative? = amount.negative? |
#nonzero? ⇒ Boolean
39 |
# File 'lib/minting/money/comparable.rb', line 39 def nonzero? = amount.nonzero? |
#positive? ⇒ Boolean
Returns true if the monetary amount is greater than zero.
16 |
# File 'lib/minting/money/arithmetics.rb', line 16 def positive? = amount.positive? |
#same_currency?(other) ⇒ Boolean
Helper method to verify if another object has the identical currency.
50 |
# File 'lib/minting/money/money.rb', line 50 def same_currency?(other) = other.respond_to?(:currency) && other.currency == currency |
#split(quantity) ⇒ Array<Money>
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.
32 33 34 35 36 37 38 39 40 41 |
# File 'lib/minting/money/allocation.rb', line 32 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 ⇒ Money
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`).
22 |
# File 'lib/minting/money/arithmetics.rb', line 22 def succ = mint(amount + currency.minimum_amount) |
#to_d ⇒ BigDecimal
Converts the monetary amount to a BigDecimal object.
10 11 12 13 14 |
# File 'lib/minting/money/conversion.rb', line 10 def to_d raise NoMethodError, 'decimal gem required' unless defined?(BigDecimal) amount.to_d 0 end |
#to_f ⇒ Float
Converts the monetary amount to a standard float. Note: Using float conversion loses precision guarantees.
20 21 22 |
# File 'lib/minting/money/conversion.rb', line 20 def to_f amount.to_f end |
#to_html(format = DEFAULT_FORMAT) ⇒ String
Renders a safe HTML5 ‘<data>` element containing the formatted currency. Embeds the ISO currency description and raw value as the metadata `title` attribute.
29 30 31 32 33 |
# File 'lib/minting/money/conversion.rb', line 29 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='#{ERB::Util.html_escape(title)}'>#{ERB::Util.html_escape(body)}</data>) end |
#to_i ⇒ Integer
Truncates and converts the monetary amount to an Integer.
38 39 40 |
# File 'lib/minting/money/conversion.rb', line 38 def to_i amount.to_i end |
#to_json(*_args) ⇒ String
Serializes the money instance to a standard JSON object containing the amount and currency. Highly optimized to run without external dependencies.
46 47 48 49 50 51 52 |
# File 'lib/minting/money/conversion.rb', line 46 def to_json(*_args) subunit = currency.subunit Kernel.format( %({"currency": "#{currency_code}", "amount": "%0.#{subunit}f"}), amount ) end |
#to_r ⇒ Rational
Returns the exact internal Rational representation of the monetary amount.
57 58 59 |
# File 'lib/minting/money/conversion.rb', line 57 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
41 |
# File 'lib/minting/money/comparable.rb', line 41 def zero? = amount.zero? |