Class: Mint::Currency

Inherits:
Object
  • Object
show all
Defined in:
lib/minting/currency/currency.rb,
lib/minting/currency/rounding.rb

Overview

:nodoc:

Constant Summary collapse

VALID_ROUNDING_MODES =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

%i[up down even].freeze
ROUNDING_THREAD_KEY =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

:minting_rounding_mode

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(code:, symbol:, subunit: 0, priority: 0, country: nil, name: nil, disambiguate_symbol: nil) ⇒ Currency

Returns a new instance of Currency.

Parameters:

  • code (String)

    ISO 4217 currency code

  • symbol (String)

    Display symbol

  • subunit (Integer) (defaults to: 0)

    Number of decimal places (default 0)

  • priority (Integer) (defaults to: 0)

    Parser precedence for symbol detection (default 0)

  • country (String, nil) (defaults to: nil)

    Associated country code (default nil)

  • name (String, nil) (defaults to: nil)

    Currency name (default nil)



49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/minting/currency/currency.rb', line 49

def initialize(code:, symbol:, subunit: 0, priority: 0, country: nil, name: nil,
               disambiguate_symbol: nil)
  @code = code
  @country = country
  @name = name
  @priority = priority.to_i
  @subunit = subunit.to_i
  @symbol = symbol.nil? || symbol.empty? ? nil : symbol

  @fractional_multiplier = 10**@subunit
  @minimum_amount = Rational(1, @fractional_multiplier)
  @disambiguate_symbol = [code, @symbol].include?(disambiguate_symbol) ? nil : disambiguate_symbol
  freeze
end

Instance Attribute Details

#codeString (readonly)

Returns ISO 4217 currency code (e.g., "USD", "EUR").

Returns:

  • (String)

    ISO 4217 currency code (e.g., "USD", "EUR")



16
17
18
# File 'lib/minting/currency/currency.rb', line 16

def code
  @code
end

#countryString? (readonly)

Returns Associated country code.

Returns:

  • (String, nil)

    Associated country code



19
20
21
# File 'lib/minting/currency/currency.rb', line 19

def country
  @country
end

#disambiguate_symbolString? (readonly)

Returns A longer, code-prefixed variant to distinguish currencies that share the same primary symbol (e.g. "US$" for USD, "C$" for CAD).

Returns:

  • (String, nil)

    A longer, code-prefixed variant to distinguish currencies that share the same primary symbol (e.g. "US$" for USD, "C$" for CAD).



23
24
25
# File 'lib/minting/currency/currency.rb', line 23

def disambiguate_symbol
  @disambiguate_symbol
end

#fractional_multiplierInteger (readonly)

Returns 10^subunit, used for fractional conversions.

Returns:

  • (Integer)

    10^subunit, used for fractional conversions



26
27
28
# File 'lib/minting/currency/currency.rb', line 26

def fractional_multiplier
  @fractional_multiplier
end

#minimum_amountRational (readonly)

Returns Smallest representable amount (1/fractional_multiplier).

Returns:

  • (Rational)

    Smallest representable amount (1/fractional_multiplier)



29
30
31
# File 'lib/minting/currency/currency.rb', line 29

def minimum_amount
  @minimum_amount
end

#nameString? (readonly)

Returns Currency name.

Returns:

  • (String, nil)

    Currency name



32
33
34
# File 'lib/minting/currency/currency.rb', line 32

def name
  @name
end

#priorityInteger (readonly)

Returns Parser precedence for symbol detection.

Returns:

  • (Integer)

    Parser precedence for symbol detection



35
36
37
# File 'lib/minting/currency/currency.rb', line 35

def priority
  @priority
end

#subunitInteger (readonly)

Returns Number of decimal places (0 for JPY, 2 for USD, 3 for IQD).

Returns:

  • (Integer)

    Number of decimal places (0 for JPY, 2 for USD, 3 for IQD)



38
39
40
# File 'lib/minting/currency/currency.rb', line 38

def subunit
  @subunit
end

#symbolString? (readonly)

Returns Display symbol (e.g., "$", "€", "R$").

Returns:

  • (String, nil)

    Display symbol (e.g., "$", "€", "R$")



41
42
43
# File 'lib/minting/currency/currency.rb', line 41

def symbol
  @symbol
end

Class Method Details

.activate_custom_rounding!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.

Activates the custom rounding dispatch path in #normalize_amount. Once called, this cannot be reversed for the lifetime of the process.



20
# File 'lib/minting/currency/rounding.rb', line 20

def self.activate_custom_rounding! = @custom_rounding_active = true

.crypto_currenciesArray<Currency>

Returns the list of built-in crypto currency definitions.

These are not registered by default — call register_crypto to opt in.

Returns:

  • (Array<Currency>)

    frozen array of crypto currency definitions



15
# File 'lib/minting/currency/registry.rb', line 15

def Currency.crypto_currencies = Registry.crypto_currencies

.current_rounding_modeSymbol

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 currently active rounding mode, falling back to :up.

Returns:

  • (Symbol)

    one of :up, :down, :even



25
26
27
# File 'lib/minting/currency/rounding.rb', line 25

def self.current_rounding_mode
  Thread.current[ROUNDING_THREAD_KEY] || :up
end

.custom_rounding_active?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 whether a custom rounding mode has been activated.

Returns:

  • (Boolean)

    whether a custom rounding mode has been activated



15
# File 'lib/minting/currency/rounding.rb', line 15

def self.custom_rounding_active? = @custom_rounding_active

.for_code(code) ⇒ Currency?

Looks up a registered currency by its alpha code.

Parameters:

  • code (String)

    the currency code

Returns:

  • (Currency, nil)

    the registered Currency, or nil if not found



21
# File 'lib/minting/currency/registry.rb', line 21

def Currency.for_code(code) = Registry.currencies[code]

.for_symbol(symbol) ⇒ Currency?

Looks up a currency by its display symbol.

Parameters:

  • symbol (String)

    the display symbol (e.g. "$", "R$")

Returns:

  • (Currency, nil)

    the highest-priority currency for the symbol



27
# File 'lib/minting/currency/registry.rb', line 27

def Currency.for_symbol(symbol) = Registry.currency_for_symbol(symbol)

.register(code:, subunit: 0, symbol: '', priority: 0) ⇒ Currency

Registers a new currency, raising a KeyError if already registered.

Parameters:

  • code (String)

    the unique currency code

  • subunit (Integer) (defaults to: 0)

    the decimal subunit precision, defaults to 0

  • symbol (String) (defaults to: '')

    the display symbol

  • priority (Integer) (defaults to: 0)

    parser precedence priority

Returns:

  • (Currency)

    the newly registered Currency instance

Raises:

  • (ArgumentError)

    if the code contains invalid characters

  • (KeyError)

    if the currency code is already registered



38
39
40
# File 'lib/minting/currency/registry.rb', line 38

def Currency.register(code:, subunit: 0, symbol: '', priority: 0)
  Registry.register(code:, subunit:, symbol:, priority:)
end

.register_all_cryptoArray<Currency>

Registers all built-in crypto currencies at once.

Raises on the first duplicate — call rescue if idempotency is needed.

Returns:

  • (Array<Currency>)

    the newly registered Currency objects

Raises:

  • (KeyError)

    if any currency code is already registered



48
# File 'lib/minting/currency/registry.rb', line 48

def Currency.register_all_crypto = Registry.register_all_crypto

.register_cryptoArray<Currency>

Registers one or more crypto currencies into the shared currency registry.

Raises on duplicate registration or unknown code — use rescue if idempotent bulk registration is needed.

Parameters:

  • codes (Array<String>)

    one or more crypto currency codes

Returns:

  • (Array<Currency>)

    the newly registered Currency objects

Raises:

  • (ArgumentError)

    if a code is not a known crypto currency

  • (KeyError)

    if the currency code is already registered



59
# File 'lib/minting/currency/registry.rb', line 59

def Currency.register_crypto(...) = Registry.register_crypto(...)

.registered_currenciesHash{String => Currency}

Returns all registered currencies as a frozen hash keyed by ISO code.

Examples:

Iterate over registered currencies

Currency.registered_currencies.each { |code, currency| puts "#{code}: #{currency.name}" }

Count of registered currencies

Currency.registered_currencies.size  #=> 154

Returns:

  • (Hash{String => Currency})

    frozen hash of all registered currencies



68
# File 'lib/minting/currency/registry.rb', line 68

def Currency.registered_currencies = Registry.currencies

.resolve(object) ⇒ Currency?

Resolves an object into a Mint::Currency, returning nil when it can't.

Accepts nil, String, Mint::Currency, Money, or any object implementing #to_currency (must return Mint::Currency) or #currency_code (must return String).

Parameters:

  • object (String, Currency, Money, nil, #to_currency, #currency_code)

    a currency code, object, or nil

Returns:

  • (Currency, nil)

    the resolved Currency, or nil if object is nil or the code is not registered

Raises:

  • (ArgumentError)

    if object is an unsupported type, or if the method used to resolve it returns a value of the wrong type



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/minting/currency/registry.rb', line 81

def Currency.resolve(object)
  case object
  when NilClass then nil
  when Currency then object
  when Money    then object.currency
  when String   then Currency.for_code object
  else
    if object.respond_to?(:to_currency)
      result = object.to_currency
      unless result.is_a?(Currency)
        raise ArgumentError, "#to_currency must return a [Money::Currency], got #{result.class}"
      end

      result
    elsif object.respond_to?(:currency_code)
      result = object.currency_code
      raise ArgumentError, "#currency_code must return a [String], got #{result.class}" unless result.is_a?(String)

      Currency.for_code result
    else
      raise ArgumentError, "currency must be [Money::Currency], [Money], [String] or nil (#{object})"
    end
  end
end

.resolve!(object) ⇒ Currency

Resolves an object into a Mint::Currency, raising on failure.

Like resolve but raises when the result would be nil.

Parameters:

Returns:

Raises:

  • (Mint::UnknownCurrency)

    if object cannot be resolved into a registered currency. Mint::UnknownCurrency inherits from ArgumentError, so existing rescue ArgumentError handlers continue to work.



115
116
117
# File 'lib/minting/currency/registry.rb', line 115

def Currency.resolve!(object)
  resolve(object) or raise Mint::UnknownCurrency, "Could not resolve (#{object}) into a currency"
end

.rounding_mode(mode) { ... } ⇒ 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.

Sets a rounding mode for the duration of a block, restoring the previous mode on exit (even on exception).

Parameters:

  • mode (Symbol)

    one of :up, :down, :even

Yields:

  • block to execute with the mode active

Raises:

  • (ArgumentError)

    on unknown mode



35
36
37
38
39
40
41
42
43
44
45
# File 'lib/minting/currency/rounding.rb', line 35

def self.rounding_mode(mode)
  unless VALID_ROUNDING_MODES.include?(mode)
    raise ArgumentError, "Unknown rounding mode: #{mode} (expected :up, :down, or :even)"
  end

  prev = Thread.current[ROUNDING_THREAD_KEY]
  Thread.current[ROUNDING_THREAD_KEY] = mode
  yield
ensure
  Thread.current[ROUNDING_THREAD_KEY] = prev
end

.world_currenciesHash{String => Currency}

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 frozen hash of all built-in ISO 4217 world currencies.

Returns:

  • (Hash{String => Currency})

    ISO-4217 world currencies mapped by code



133
# File 'lib/minting/currency/registry.rb', line 133

def Currency.world_currencies = Registry.world_currencies

.zero(currency) ⇒ Money

Returns a zero Money in the given currency, useful as a default value for discounts, totals, or placeholders.

Parameters:

Returns:

  • (Money)

    a frozen zero-Money

Raises:



125
# File 'lib/minting/currency/registry.rb', line 125

def Currency.zero(currency) = Registry.zero_for(Currency.resolve!(currency))

Instance Method Details

#==(other) ⇒ Object

Two Currency objects are equal if they share the same ISO code.



65
# File 'lib/minting/currency/currency.rb', line 65

def ==(other) = other.is_a?(self.class) && code == other.code

#dsymbolString?

Returns disambiguate_symbol or code/symbol fallback.

Returns:

  • (String, nil)

    disambiguate_symbol or code/symbol fallback



68
# File 'lib/minting/currency/currency.rb', line 68

def dsymbol = disambiguate_symbol || (Registry.symbol_shared?(symbol) ? code : symbol)

#eql?(other) ⇒ Boolean

Currency identity is by code — two objects with the same code are eql? regardless of other attributes. This makes Currency usable as a Hash key where lookup is by currency identity (ISO code).

Returns:

  • (Boolean)


73
# File 'lib/minting/currency/currency.rb', line 73

def eql?(other) = other.is_a?(Currency) && code == other.code

#hashInteger

Returns stable hash based on currency code.

Returns:

  • (Integer)

    stable hash based on currency code



76
# File 'lib/minting/currency/currency.rb', line 76

def hash = code.hash

#inspectString

Returns debug representation.

Returns:

  • (String)

    debug representation



79
# File 'lib/minting/currency/currency.rb', line 79

def inspect = "<Currency:(#{code} #{symbol} #{subunit} #{name})>"

#normalize_amount(amount) ⇒ Rational

Normalizes a numeric amount for this currency.

Examples:

usd = Money::Currency.for_code('USD')
usd.normalize_amount(10.567)  #=> (10567/1000)
usd.normalize_amount("5.25")  #=> (21/4)

Parameters:

  • amount (Numeric)

    the monetary amount to normalize

Returns:

  • (Rational)

    the amount converted to Rational and rounded to the currency's subunit precision (up by default)

See Also:



92
93
94
95
96
97
98
# File 'lib/minting/currency/currency.rb', line 92

def normalize_amount(amount)
  if Currency.custom_rounding_active?
    amount.to_r.round(subunit, half: Thread.current[Currency::ROUNDING_THREAD_KEY])
  else
    amount.to_r.round(subunit)
  end
end

#zeroMoney

Returns the cached frozen zero-Money for this currency.

Examples:

Money::Currency.for_code('USD').zero  #=> [USD 0.00]

Returns:

  • (Money)

    a frozen zero-Money instance



105
# File 'lib/minting/currency/currency.rb', line 105

def zero = Registry.zero_for(self)