Minting
Fast, precise, and developer-friendly money handling for Ruby.
Quick start
require 'minting'
price = Mint.money(19.99, 'USD') #=> [USD 19.99]
tax = price * 0.08 #=> [USD 1.60]
total = price + tax #=> [USD 21.59]
total.to_s #=> "$21.59"
total.currency_code #=> "USD"
Why Minting?
| Minting | |
|---|---|
| Precision | Rational-based, zero floating-point error |
| Performance | 2Γ faster (10Γ+ formatting) |
| Ruby support | 3.3+ (including Ruby 4.0) |
| Rails | Dedicated companion gem |
| Code quality | 100% coverage, 93/100 RubyCritic |
π― Exact precision
Amounts are stored as Rational and rounded to the currency subunit. No floating-point surprises, ever.
β‘ Blazing performance
Minting is 2Γ faster than the Money gem for everyday operations and over 10Γ faster for formatting. See full benchmarks in the Performance Guide.
π§Ό Clean, modern API
Intuitive interface, descriptive error messages, and sensible defaults. Works the way you expect.
π Rails-ready
Use with the minting-rails companion gem for drop-in ActiveRecord type casting, validators, and form helpers.
π Quality you can trust
- 100% test coverage β every line exercised
- 93/100 RubyCritic score β clean, maintainable code
- CI-tested on Ruby 3.3 and 4.0
Installation
bundle add minting
Or add to your Gemfile:
gem 'minting'
Usage
require 'minting'
# Create money
ten = Mint.money(10, 'USD') #=> [USD 10.00]
# Create money using Numeric refinements
using Mint
1.dollar == Mint.money(1, 'USD') #=> true
ten = 10.dollars #=> [USD 10.00]
4.to_money('USD') #=> [USD 4.00]
# Comparisons
ten == 10.dollars #=> true
ten == Mint.money(10, 'EUR') #=> false
ten > Mint.money(9.99, 'USD') #=> true
# Zero equality semantics
# Any zero amount is treated as equal, regardless of currency
Mint.money(0, 'USD') == Mint.money(0, 'EUR') #=> true
Mint.money(0, 'USD') == 0 #=> true
Mint.money(0, 'USD') == 0.0 #=> true
# Non-zero numerics are not equal to Money objects
Mint.money(10, 'USD') == 10 #=> false
# Format (uses Kernel.format syntax)
price = Mint.money(9.99, 'USD')
price.to_s #=> "$9.99",
price.to_s(format: '%<amount>d') #=> "9",
price.to_s(format: '%<symbol>s%<amount>f') #=> "$9.99",
price.to_s(format: '%<symbol>s%<amount>+f') #=> "$+9.99",
(-price).to_s(format: '%<amount>f') #=> "-9.99",
# Format with padding
price_in_euros = Mint.money(12.34, 'EUR')
price.to_s(format: '--%<amount>7d') #=> "-- 9"
price.to_s(format: ' %<amount>10f %<currency>s') #=> " 9.99 USD"
(-price).to_s(format: ' %<amount>10f') #=> " -9.99"
price_in_euros.to_s(format: '%<symbol>2s%<amount>+10f') #=> " β¬ +12.34"
# Per-sign Hash format (e.g. accounting parentheses for losses)
loss = Mint.money(-1234.56, 'USD')
loss.to_s(format: { negative: '(%<symbol>s%<amount>f)' }) #=> "($1,234.56)"
Mint.money(0, 'BRL').to_s(format: { zero: '--' }) #=> "--"
# All three keys at once:
fmt = { positive: '%<symbol>s%<amount>f', negative: '(%<symbol>s%<amount>f)', zero: '--' }
Mint.money(1234.56, 'USD').to_s(format: fmt) #=> "$1,234.56"
# Json serialization
price.to_json # => "{\"currency\": \"USD\", \"amount\": \"9.99\"}"
# Hash conversion
price.to_hash #=> {currency: "USD", amount: "9.99"}
# Fractional units (inverse of #fractional) - exact integer arithmetic
price.fractional #=> 999
Mint::Money.from_fractional(999, 'USD') #=> [USD 9.99]
Mint::Money.from_fractional(1234, 'JPY') #=> [JPY 1234] # subunit 0 -> no scaling
# Proportional allocation and split
ten.split(3) #=> [[USD 3.34], [USD 3.33], [USD 3.33]]
ten.allocate([1, 2, 3]) #=> [[USD 1.67], [USD 3.33], [USD 5.00]]
# Clamping to a range
price = Mint.money(50, 'USD')
min_price = Mint.money(75, 'USD')
price.clamp(0, 100) #=> [USD 50.00] (returns self, no new object)
price.clamp(0, 25) #=> [USD 25.00] (clamped to max)
price.clamp(min_price, 100) #=> [USD 75.00] (clamped to min)
# Clamp accepts Money bounds or Numeric amounts
price.clamp(min_price, 100) #=> [USD 75.00]
# Ranges and enumeration are supported
1.dollar..10.dollars #=> [USD 1.00]..[USD 10.00]
(1.dollar..3.dollars).step(1.dollar).to_a #=> [[USD 1.00], [USD 2.00], [USD 3.00]]
Parsing strings
Mint.parse('$19.99') #=> [USD 19.99]
Mint.parse('19,99 β¬') #=> [EUR 19.99]
Mint.parse('1.234,56', 'EUR') #=> [EUR 1234.56]
Mint.parse('USD 1,234.56') #=> [USD 1234.56]
Notes:
- Pass a currency code when the string has no symbol or code.
1,234means 1234, not 1.234 and1,23means 1.23, not 1231,234.00is unambiguous (thousands + decimal).- Accounting negatives like
($1.23)are unsupported for now. - Ambiguous symbols like
$resolve by currency priority (currently USD). - The parser scans all uppercase words for registered codes, so spurious non-currency words before the real code are correctly ignored:
Mint.parse("MAX 10.00 USD")yields[USD 10.00].
API notes
Exact amounts β Amounts are stored as Rational and rounded to the currency subunit.
Refinements β 10.dollars and similar helpers require using Mint in the current scope (see Usage above).
Division β money / 5 returns new Money; money / other_money returns a numeric ratio, not money.
Zero equality β Any zero amount is considered equal across currencies and to numeric zero (Mint.money(0, 'USD') == Mint.money(0, 'EUR') is intentionally true). Non-zero amounts must match currency and value.
Registered currencies β Mint.register_currency. Only registered currency codes and symbols are recognized by the parser.
Built-in currencies β 150+ ISO-4217 world currencies ship in lib/minting/data/currencies.yaml and load when the registry is first accessed.
Optional top-level Money and Currency
By default, Minting keeps everything namespaced under Mint to coexist nicely with other gems. If you prefer shorter constants, opt in:
require "minting"
require "minting/dsl" # optβin topβlevel Money / Currency
Or at runtime:
Minting.use_top_level_constants!
After opting in:
price = Money.create(10, "USD") # equivalent to Mint::Money.create
tax = Money.money(2.50, "USD")
cur = Currency.new(code: "EUR", symbol: "β¬", subunit: 2, priority: 0)
Good fit: Application code, especially Rails apps.
Not recommended: Reusable gems/libraries β stick to Mint::Money to avoid conflicts.
Roadmap
- Localization (I18n-aware formatting)
- Exchange-rate conversion infrastructure
License
MIT