minting 2.1.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +142 -40
- data/Rakefile +2 -7
- data/doc/agents/AGENTS.md +382 -0
- data/doc/api_review-2026-08-13.md +144 -0
- data/doc/security-report.md +137 -0
- data/lib/minting/aliases.rb +8 -8
- data/lib/minting/currency/currency.rb +1 -7
- data/lib/minting/currency/registry.rb +6 -5
- data/lib/minting/currency/rounding.rb +14 -1
- data/lib/minting/mint/dsl/numeric.rb +7 -2
- data/lib/minting/mint/dsl/string.rb +2 -2
- data/lib/minting/mint/mint.rb +6 -1
- data/lib/minting/mint/registry/crypto.rb +7 -3
- data/lib/minting/mint/registry/registration.rb +6 -2
- data/lib/minting/mint/registry/registry.rb +1 -1
- data/lib/minting/money/allocation/allocation.rb +6 -1
- data/lib/minting/money/clamp.rb +6 -16
- data/lib/minting/money/constructors.rb +1 -1
- data/lib/minting/money/conversion.rb +3 -0
- data/lib/minting/money/format/format.rb +10 -1
- data/lib/minting/money/format/formatter.rb +50 -22
- data/lib/minting/money/format/to_s.rb +4 -3
- data/lib/minting/money/money.rb +2 -1
- data/lib/minting/money/parse/separator_parser.rb +60 -0
- data/lib/minting/money/parse.rb +74 -14
- data/lib/minting/money/rounding.rb +4 -5
- data/lib/minting/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3af2c270bbab98102901e0f9c1aefb22966d916805cd04a7eab8e867fe9f59be
|
|
4
|
+
data.tar.gz: 109d647aceff1385f35135c91b1728237965e549de4744717ff28956f4e33659
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3ac0ee4402ad94a39aa256fdcefaa24333f89333de1ee216b7ca698e493fb2410d771115c8a7e694d7acec8228bb5301db8ba6643e1e277bf87aa2aaf132b65d
|
|
7
|
+
data.tar.gz: 41e8ff87efe32f54185a96fc7e9781c02304a9a24b3090bb50bd5f8532488ab581c08ccbf2be47d469d18d63fed9fda2cf74776bf066dc8aac48aca982bb90e7
|
data/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
> **Status:** Minting 2.
|
|
15
|
+
> **Status:** Minting 2.3 is released. The core API (`Money`, `Currency`, formatting, parsing) is stable.
|
|
16
16
|
|
|
17
17
|
```ruby
|
|
18
18
|
price = Money.from(19.99, 'USD') #=> [USD 19.99]
|
|
@@ -57,25 +57,10 @@ shares = total.allocate([1, 2, 3]) # => [16.67, 33.33, 50.00]
|
|
|
57
57
|
parts = total.split(3) # => [33.34, 33.33, 33.33]
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
## What's
|
|
61
|
-
|
|
62
|
-
### Breaking Changes
|
|
63
|
-
- Rounding mode symbols renamed to match `Rational#round` `half:` parameter: `:half_up` → `:up`, `:half_down` → `:down`, `:half_even` → `:even`
|
|
64
|
-
|
|
65
|
-
## What's New in 2.0
|
|
66
|
-
|
|
67
|
-
### Breaking Changes
|
|
68
|
-
- `Mint.parse` and `Mint.parse!` removed — use `Money.parse` and `Money.parse!`
|
|
69
|
-
- `Mint.with_rounding` removed — use `Money.with_rounding`
|
|
70
|
-
- `Mint.world_currencies` removed — use `Currency.world_currencies`
|
|
71
|
-
- `Money#mint` removed — use `Money#copy_with`
|
|
72
|
-
- `Money::Currency` is the canonical name to access the `Currency` class
|
|
73
|
-
- `minting/mint/aliases` abbreviated to `minting/aliases`
|
|
74
|
-
- `Money#format` `formatter_class:` kwarg removed — `Formatter` is now the sole formatter implementation
|
|
75
|
-
- `Money#to_json` and `Money.from_json` — moved to `attribute-money` companion gem
|
|
60
|
+
## What's new since 2.0
|
|
76
61
|
|
|
77
62
|
### New Features
|
|
78
|
-
- **Crypto currency support**: Opt-in YAML-backed definitions for ~25 popular coins (BTC, ETH, SOL, ...). Use `Currency.register_crypto('BTC', 'ETH')` to register, or `Currency.crypto_currencies` to inspect available definitions.
|
|
63
|
+
- **Crypto currency support**: Opt-in YAML-backed definitions for ~25 popular coins (BTC, ETH, SOL, ...). Use `Money::Currency.register_crypto('BTC', 'ETH')` to register, or `Money::Currency.crypto_currencies` to inspect available definitions.
|
|
79
64
|
- `Currency.registered_currencies` — public access to all registered currencies (frozen hash)
|
|
80
65
|
- `Money.from_hash(hash)` — deserializer symmetric with `to_hash`, accepts `{ currency:, amount: }`
|
|
81
66
|
- `Money#integral` — returns the whole-unit part of the amount (complement to `#fractional`). `#to_i` is now an alias of `#integral`.
|
|
@@ -88,6 +73,17 @@ parts = total.split(3) # => [33.34, 33.33, 33.33]
|
|
|
88
73
|
- `Money#fractional` now returns a signed value matching the amount's sign (previously always positive for negative amounts). The invariant `integral * multiplier + fractional == subunits` now holds for all amounts.
|
|
89
74
|
- `Money#initialize` now calls `.to_r` on the amount, guaranteeing `@amount` is always a `Rational`. Fixes a hash/`eql?` contract violation for zero-subunit currencies and an `ArgumentError` in `Integer#to_d`.
|
|
90
75
|
|
|
76
|
+
### Breaking Changes
|
|
77
|
+
- `Money#clamp` no longer accepts `Numeric` bounds — only `Money` or `nil`. Previously `price.clamp(0, 100)` was silently accepted; now it raises `ArgumentError`. Use `price.clamp(0.dollars, 100.dollars)` instead. This prevents accidental misuse where a bare number is passed as a variable.
|
|
78
|
+
- Rounding mode symbols renamed to match `Rational#round` `half:` parameter: `:half_up` → `:up`, `:half_down` → `:down`, `:half_even` → `:even`
|
|
79
|
+
- `Mint.parse` and `Mint.parse!` removed — use `Money.parse` and `Money.parse!`
|
|
80
|
+
- `Mint.with_rounding` removed — use `Money.with_rounding`
|
|
81
|
+
- `Mint.world_currencies` removed — use `Currency.world_currencies`
|
|
82
|
+
- `Money#mint` removed — use `Money#copy_with`
|
|
83
|
+
- `Money::Currency` is the canonical name to access the `Currency` class
|
|
84
|
+
- `Money#format` `formatter_class:` kwarg removed — `Formatter` is now the sole formatter implementation
|
|
85
|
+
- `Money#to_json` and `Money.from_json` — moved to `attribute-money` companion gem
|
|
86
|
+
|
|
91
87
|
### Removed
|
|
92
88
|
- `Money::Formatter2` — removed; `Formatter` is the sole implementation
|
|
93
89
|
- `Money#to_json` and `Money.from_json` — moved to `attribute-money` companion gem
|
|
@@ -98,8 +94,7 @@ Amounts are stored as `Rational`, so there's no floating-point drift — `0.1 +
|
|
|
98
94
|
|
|
99
95
|
- [Quickstart](#quickstart)
|
|
100
96
|
- [Why Minting](#why-minting)
|
|
101
|
-
- [What's
|
|
102
|
-
- [What's New in 2.0](#whats-new-in-20)
|
|
97
|
+
- [What's new since 2.0](#whats-new-since-20)
|
|
103
98
|
- [How it compares](#how-it-compares)
|
|
104
99
|
- [Installation](#installation)
|
|
105
100
|
- [Usage](#usage)
|
|
@@ -124,7 +119,7 @@ What it's become along the way:
|
|
|
124
119
|
|
|
125
120
|
- **Exact by construction** — amounts are `Rational` internally, rounded to the currency's subunit only when needed. No silent precision loss from repeated arithmetic.
|
|
126
121
|
- **No Rails dependency** — Minting is a plain Ruby gem. Use it in a script, a Sinatra app, a background job runner, or a Rails app — your choice, not the gem's.
|
|
127
|
-
- **Formatting that doesn't fight you** — `Kernel.format`-style templates,
|
|
122
|
+
- **Formatting that doesn't fight you** — `Kernel.format`-style templates, per-sign formats (parentheses for negatives), explicit sign and magnitude placeholders, and a pluggable locale hook.
|
|
128
123
|
- **Built for real-world currency handling** — 150+ ISO-4217 currencies, correct subunit handling (JPY has none, KWD has three), proportional allocation/split that doesn't lose cents to rounding.
|
|
129
124
|
- **Measured, not assumed, performance** — see the [Performance Guide](bench/BENCHMARKS.md) for actual benchmarks rather than claims.
|
|
130
125
|
- **Rails-ready without being Rails-only** — pair with the companion [MoneyAttribute](https://github.com/gferraz/money-attribute) gem for `ActiveRecord` type casting, validators, and form helpers.
|
|
@@ -156,6 +151,14 @@ gem 'minting'
|
|
|
156
151
|
|
|
157
152
|
### Creating & comparing money
|
|
158
153
|
|
|
154
|
+
`Money.from` is the canonical constructor. The older `Mint.money` factory and
|
|
155
|
+
`Numeric#mint` shortcut are deprecated; use `Money.from` and `Numeric#to_money`
|
|
156
|
+
instead. Named shortcuts such as `dollars`, `euros`, and `reais` remain
|
|
157
|
+
available.
|
|
158
|
+
|
|
159
|
+
Currency arguments accept currency code strings or `Currency` objects. Symbols
|
|
160
|
+
such as `:USD` are not supported.
|
|
161
|
+
|
|
159
162
|
```ruby
|
|
160
163
|
require 'minting'
|
|
161
164
|
|
|
@@ -187,9 +190,10 @@ Money.from(10, 'USD') == 10 #=> false
|
|
|
187
190
|
price = Money.from(50, 'USD')
|
|
188
191
|
min_price = Money.from(75, 'USD')
|
|
189
192
|
|
|
190
|
-
price.clamp(
|
|
191
|
-
price.clamp(0, 25) #=> [USD 25.00] (clamped to max)
|
|
192
|
-
price.clamp(min_price, 100)
|
|
193
|
+
price.clamp(price, Money.from(100, 'USD')) #=> [USD 50.00] (returns self, no new object)
|
|
194
|
+
price.clamp(0.dollars, 25.dollars) #=> [USD 25.00] (clamped to max)
|
|
195
|
+
price.clamp(min_price, 100.dollars) #=> [USD 75.00] (clamped to min)
|
|
196
|
+
|
|
193
197
|
```
|
|
194
198
|
|
|
195
199
|
### Formatting
|
|
@@ -203,6 +207,7 @@ price.format('%<amount>d') #=> "9"
|
|
|
203
207
|
price.format('%<symbol>s%<amount>f') #=> "$9.99"
|
|
204
208
|
price.format('%<symbol>s%<amount>+f') #=> "$+9.99"
|
|
205
209
|
(-price).format('%<amount>f') #=> "-9.99"
|
|
210
|
+
(-price).format('%<sign>s%<symbol>s%<magnitude>f') #=> "-$9.99"
|
|
206
211
|
|
|
207
212
|
# Format with padding
|
|
208
213
|
price_in_euros = Money.from(12.34, 'EUR')
|
|
@@ -216,8 +221,11 @@ price_in_euros.format('%<symbol>2s%<amount>+10f') #=> " € +12.34"
|
|
|
216
221
|
price.format('%<integral>d %<fractional>d/100') #=> "9 99/100"
|
|
217
222
|
Money.from(0.99, 'USD').format('%<integral>d dollars and %<fractional>02d cents')
|
|
218
223
|
#=> "0 dollars and 99 cents"
|
|
224
|
+
(-Money.from(0.99, 'USD')).format('%<sign>s%<integral>d.%<fractional>02d')
|
|
225
|
+
#=> "-0.99"
|
|
219
226
|
|
|
220
227
|
# Per-sign Hash format (e.g. accounting parentheses for losses)
|
|
228
|
+
loss = Money.from(-1234.56, 'USD')
|
|
221
229
|
loss.format( { negative: '(%<symbol>s%<amount>f)' }) #=> "($1,234.56)"
|
|
222
230
|
Money.from(0, 'BRL').format( { zero: '--' }) #=> "--"
|
|
223
231
|
fmt = { positive: '%<symbol>s%<amount>f', negative: '(%<symbol>s%<amount>f)', zero: '--' }
|
|
@@ -264,8 +272,24 @@ Money.parse('USD 1,234.56') #=> [USD 1234.56]
|
|
|
264
272
|
|
|
265
273
|
Notes:
|
|
266
274
|
- Pass a currency code when the string has no symbol or code.
|
|
275
|
+
- The optional `default_currency:` argument is a default, not an override: a code or symbol embedded in the string takes precedence (`Money.parse('10 EUR', default_currency: 'USD')` returns EUR). The positional currency argument is deprecated.
|
|
267
276
|
- `1,234` means 1234, not 1.234, and `1,23` means 1.23, not 123.
|
|
268
277
|
- `1,234.00` is unambiguous (thousands + decimal).
|
|
278
|
+
- Parsing is separator-positional rather than locale-aware. For currencies
|
|
279
|
+
with three decimal places, a single comma followed by three digits is read
|
|
280
|
+
as a thousands separator: `Money.parse('KWD 861,949')` means 861949 KWD.
|
|
281
|
+
Use a period decimal (`KWD 861.949`) or both separators (`KWD 1.234,567`)
|
|
282
|
+
when the intended value has three fractional digits.
|
|
283
|
+
- Pass the input source's decimal separator explicitly when a single separator
|
|
284
|
+
followed by three digits is ambiguous: `Money.parse('123,456', 'EUR',
|
|
285
|
+
decimal: ',')` means `123.456`, while `decimal: '.'` means `123456`. The
|
|
286
|
+
default currency can be passed positionally or as `default_currency: 'EUR'`. When
|
|
287
|
+
the input is an integer with grouping, pass `thousand:` explicitly instead:
|
|
288
|
+
`Money.parse('123,456', 'USD', thousand: ',')` means `123456`. When both
|
|
289
|
+
options are supplied, they define the roles independently, so a decimal
|
|
290
|
+
separator that is absent from the input is also valid:
|
|
291
|
+
`Money.parse('123,456', 'USD', decimal: ':', thousand: ',')` means `123456`.
|
|
292
|
+
When both options are omitted, the positional rules above remain in effect.
|
|
269
293
|
- Accounting negatives like `($1.23)` or `(USD 10.00)` are supported — the parser detects parentheses and negates the amount.
|
|
270
294
|
- Ambiguous symbols like `$` resolve by currency priority (currently USD).
|
|
271
295
|
- The parser scans all uppercase words for registered codes, so spurious non-currency words before the real code are correctly ignored: `Money.parse("MAX 10.00 USD")` yields `[USD 10.00]`.
|
|
@@ -273,12 +297,12 @@ Notes:
|
|
|
273
297
|
### Currency lookup
|
|
274
298
|
|
|
275
299
|
```ruby
|
|
276
|
-
# All registered currencies (
|
|
300
|
+
# All registered currencies (currently 164 built-in ISO 4217 + custom)
|
|
277
301
|
Money::Currency.registered_currencies.size #=> 164
|
|
278
302
|
Money::Currency.registered_currencies.each { |code, c| puts "#{code}: #{c.name}" }
|
|
279
303
|
|
|
280
304
|
# Built-in ISO 4217 currencies (before custom registrations)
|
|
281
|
-
Money::Currency.world_currencies.size #=>
|
|
305
|
+
Money::Currency.world_currencies.size #=> 164
|
|
282
306
|
|
|
283
307
|
# By ISO code (direct hash lookup, string only)
|
|
284
308
|
Money::Currency.for_code('USD') #=> #<Currency code="USD" ...>
|
|
@@ -307,16 +331,16 @@ Money::Currency.resolve!(Product.new) # raises Mint::UnknownCurrency if code is
|
|
|
307
331
|
Minting ships with opt-in definitions for ~25 popular crypto currencies (BTC, ETH, SOL, ...). They are not registered by default — use `register_crypto` to enable them:
|
|
308
332
|
|
|
309
333
|
```ruby
|
|
310
|
-
Currency.register_crypto('BTC', 'ETH', 'SOL')
|
|
334
|
+
Money::Currency.register_crypto('BTC', 'ETH', 'SOL')
|
|
311
335
|
|
|
312
336
|
Money.parse("0.01 BTC") #=> [BTC 0.01000000]
|
|
313
|
-
|
|
337
|
+
Money.from(1, 'ETH') #=> [ETH 1.000000000000000000]
|
|
314
338
|
```
|
|
315
339
|
|
|
316
|
-
`Currency.crypto_currencies` lists all available definitions without registering:
|
|
340
|
+
`Money::Currency.crypto_currencies` lists all available definitions without registering:
|
|
317
341
|
|
|
318
342
|
```ruby
|
|
319
|
-
Currency.crypto_currencies.each { |c| puts "#{c.code}: #{c.name}" }
|
|
343
|
+
Money::Currency.crypto_currencies.each { |c| puts "#{c.code}: #{c.name}" }
|
|
320
344
|
# BTC: Bitcoin
|
|
321
345
|
# ETH: Ethereum
|
|
322
346
|
# SOL: Solana
|
|
@@ -346,11 +370,11 @@ LOCALE_DATA = {
|
|
|
346
370
|
|
|
347
371
|
Mint.locale_backend = ->(locale) { LOCALE_DATA[locale.to_s] || {} }
|
|
348
372
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
373
|
+
Money.from(1234.56, 'USD').format(locale: :en) #=> "$1,234.56"
|
|
374
|
+
Money.from(9.99, 'BRL').format(locale: 'pt') #=> "R$9,99"
|
|
375
|
+
Money.from(9.99, 'EUR').format(locale: :de) #=> "9,99 EUR"
|
|
376
|
+
Money.from(9.99, 'EUR').format(locale: 'fr') #=> "9,99 €"
|
|
377
|
+
Money.from(9.99, 'USD').format(locale: :ja) #=> "$9.99"
|
|
354
378
|
```
|
|
355
379
|
|
|
356
380
|
Pass `locale:` as a keyword to `format` / `to_fs`. Accepts both symbols (`:en`, `:'pt-BR'`) and strings (`'pt-BR'`, `'en-US'`) — passed through as-is, matching Rails' `I18n.locale` convention. The backend returns a hash with `:decimal`, `:thousand`, and optionally `:format` (defaults to `'%<symbol>s%<amount>f'`). String and symbol keys are interchangeable. Return `{}` or `nil` for unknown locales — defaults apply.
|
|
@@ -371,13 +395,25 @@ Arity-0 callables (`-> { ... }`) are called without arguments and work unchanged
|
|
|
371
395
|
|
|
372
396
|
```ruby
|
|
373
397
|
Mint.locale_backend = -> { { decimal: ',', thousand: '.' } }
|
|
374
|
-
|
|
398
|
+
Money.from(9.99, 'BRL').format #=> "R$9,99"
|
|
375
399
|
```
|
|
376
400
|
|
|
377
401
|
## API notes
|
|
378
402
|
|
|
379
403
|
**Exact amounts** — Amounts are stored as `Rational` and rounded to the currency subunit.
|
|
380
404
|
|
|
405
|
+
**Sign and magnitude placeholders** — `%<amount>` retains its sign.
|
|
406
|
+
`%<magnitude>` always renders the absolute value, while `%<sign>` renders `+`
|
|
407
|
+
for positive values, `-` for negative values, and an empty string for zero.
|
|
408
|
+
Use a width of `1` to reserve the sign column for zero values:
|
|
409
|
+
|
|
410
|
+
```ruby
|
|
411
|
+
format = '%<sign>1s%<symbol>s%<magnitude>f'
|
|
412
|
+
Money.from(12.34, 'BRL').format(format, decimal: ',') #=> "+R$12,34"
|
|
413
|
+
Money.from(-12.34, 'BRL').format(format, decimal: ',') #=> "-R$12,34"
|
|
414
|
+
Money.from(0, 'BRL').format(format, decimal: ',') #=> " R$0,00"
|
|
415
|
+
```
|
|
416
|
+
|
|
381
417
|
**Rounding modes** — Wrap operations in `Money.with_rounding(mode)` to change how amounts are rounded to the subunit:
|
|
382
418
|
|
|
383
419
|
```ruby
|
|
@@ -386,21 +422,87 @@ Money.with_rounding(:even) { Money.from(1.015, 'USD') } #=> [USD 1.02]
|
|
|
386
422
|
Money.with_rounding(:up) { Money.from(1.005, 'USD') } #=> [USD 1.01]
|
|
387
423
|
```
|
|
388
424
|
|
|
389
|
-
Modes: `:up` (default), `:down`, `:even`. Applies to construction, parsing,
|
|
425
|
+
Modes: `:up` (default), `:down`, `:even`. Applies to construction, parsing,
|
|
426
|
+
`copy_with`, `split`, and `allocate`. Restores the previous mode when the block
|
|
427
|
+
exits, even on exception.
|
|
390
428
|
|
|
391
429
|
> **Performance note:** Rounding-mode support is not loaded by default — `require 'minting'` uses the fastest possible rounding (equivalent to `:up`) with zero dispatch overhead. The first call to `Money.with_rounding` activates the rounding dispatch in `Currency#normalize_amount`, adding ~10–35 ns per money creation or mutation. If your application never uses custom rounding modes, there is **no performance cost**.
|
|
392
430
|
|
|
393
|
-
|
|
431
|
+
### Common API workflows
|
|
432
|
+
|
|
433
|
+
**Parsing failures** — `Money.parse` returns `nil` for invalid input or an
|
|
434
|
+
unresolved currency. `Money.parse!` raises `ArgumentError` instead:
|
|
435
|
+
|
|
436
|
+
```ruby
|
|
437
|
+
Money.parse('bad', 'USD') #=> nil
|
|
438
|
+
Money.parse!('bad', 'USD') #=> raises ArgumentError
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
The optional currency argument is a default. Embedded currency information wins:
|
|
442
|
+
|
|
443
|
+
```ruby
|
|
444
|
+
Money.parse('10', 'USD') #=> [USD 10.00]
|
|
445
|
+
Money.parse('10 EUR', 'USD') #=> [EUR 10.00]
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
**Arithmetic and division** — Money can be added or subtracted only with the
|
|
449
|
+
same currency. Multiplication and scalar division return Money; dividing by
|
|
450
|
+
same-currency Money returns a numeric ratio. Non-zero cross-currency operations
|
|
451
|
+
and reverse numeric division raise `TypeError`.
|
|
452
|
+
|
|
453
|
+
```ruby
|
|
454
|
+
price = Money.from(10, 'USD')
|
|
455
|
+
price + Money.from(2, 'USD') #=> [USD 12.00]
|
|
456
|
+
price * 1.5 #=> [USD 15.00]
|
|
457
|
+
price / 2 #=> [USD 5.00]
|
|
458
|
+
price / Money.from(5, 'USD') #=> (2/1)
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
**Clamping** — Bounds must be Money objects in the same currency or `nil`.
|
|
462
|
+
An inclusive Range can be passed as the only argument:
|
|
463
|
+
|
|
464
|
+
```ruby
|
|
465
|
+
price.clamp(Money.from(0, 'USD'), Money.from(8, 'USD')) #=> [USD 8.00]
|
|
466
|
+
price.clamp(Money.from(8, 'USD')..Money.from(12, 'USD')) #=> [USD 10.00]
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
**Allocation** — `split(n)` requires a positive integer. `allocate(ratios)`
|
|
470
|
+
requires a non-empty list whose total is non-zero. Both preserve the original
|
|
471
|
+
amount after subunit rounding; any leftover smallest units go to the first
|
|
472
|
+
slots. Ratios may be negative and produce signed allocations, but use them only
|
|
473
|
+
when that distribution is intentional:
|
|
474
|
+
|
|
475
|
+
```ruby
|
|
476
|
+
Money.from(10, 'USD').split(3) #=> [[USD 3.34], [USD 3.33], [USD 3.33]]
|
|
477
|
+
Money.from(10, 'USD').allocate([1, 2, 3]) #=> [[USD 1.67], [USD 3.33], [USD 5.00]]
|
|
478
|
+
Money.from(10, 'USD').allocate([-1, 2]) #=> [[USD -10.00], [USD 20.00]]
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
**Conversions and serialization** — `to_r` is exact, `to_d` returns a
|
|
482
|
+
BigDecimal, and `to_f` can lose precision. `to_hash` and `from_hash` provide a
|
|
483
|
+
string-safe round trip. JSON and Rails `as_json` integration belong to the
|
|
484
|
+
`money_attribute` companion gem, not the core Minting gem:
|
|
485
|
+
|
|
486
|
+
```ruby
|
|
487
|
+
money = Money.from(9.99, 'USD')
|
|
488
|
+
money.to_r #=> (999/100)
|
|
489
|
+
money.to_f #=> 9.99
|
|
490
|
+
Money.from_hash(money.to_hash) #=> [USD 9.99]
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
**Money ranges** — Money ranges use `succ` and a Money step. Ruby 4.0 supports
|
|
494
|
+
non-numeric steps natively; Minting patches `Range#step` for Money only on
|
|
495
|
+
older supported Rubies.
|
|
394
496
|
|
|
395
497
|
**Zero equality** — Any zero amount is considered equal across currencies and to numeric zero (`Money.from(0, 'USD') == Money.from(0, 'EUR')` is intentionally `true`). Non-zero amounts must match currency and value.
|
|
396
498
|
|
|
397
499
|
|
|
398
500
|
|
|
399
|
-
**Registered currencies** — `Currency.register(code:, subunit:, symbol:, priority:)` adds custom currencies. Only registered codes and symbols are recognized by the parser or searches. You don't need to register a currency to use it with most features.
|
|
501
|
+
**Registered currencies** — `Money::Currency.register(code:, subunit:, symbol:, priority:, country:, name:, disambiguate_symbol:)` adds custom currencies. `country`, `name`, and `disambiguate_symbol` are optional metadata fields; `disambiguate_symbol` is also used by `%<dsymbol>s` and symbol parsing. Only registered codes and symbols are recognized by the parser or searches. You don't need to register a currency to use it with most features.
|
|
400
502
|
|
|
401
503
|
**Built-in currencies** — 150+ ISO-4217 world currencies ship in `lib/minting/data/world-currencies.yaml` and are preloaded at gem initialization.
|
|
402
504
|
|
|
403
|
-
##
|
|
505
|
+
## Top-level aliases
|
|
404
506
|
|
|
405
507
|
By default, `require "minting"` exposes `Mint::Money` as the top-level `Money` constant, so you can write `Money.from(10, "USD")` directly:
|
|
406
508
|
```ruby
|
data/Rakefile
CHANGED
|
@@ -13,12 +13,7 @@ Rake::TestTask.new(:test) do |t|
|
|
|
13
13
|
t.ruby_opts << '-rtest_helper.rb'
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
Rake::TestTask.new('bench:
|
|
17
|
-
t.libs = %w[lib bench]
|
|
18
|
-
t.pattern = 'bench/{core,memory,regression}/*_benchmark.rb'
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
Rake::TestTask.new('bench:core') do |t|
|
|
16
|
+
Rake::TestTask.new('bench:parse') do |t|
|
|
22
17
|
t.libs = %w[lib bench]
|
|
23
18
|
t.pattern = 'bench/core/parse_benchmark.rb'
|
|
24
19
|
end
|
|
@@ -49,7 +44,7 @@ desc 'Run core benchmarks and update the baseline'
|
|
|
49
44
|
task 'bench:baseline' do
|
|
50
45
|
platform = RUBY_PLATFORM
|
|
51
46
|
baseline = "bench/check/results/baseline-#{platform}.json"
|
|
52
|
-
|
|
47
|
+
ruby "bench/check/runner.rb #{baseline}"
|
|
53
48
|
puts "Baseline updated for #{platform}."
|
|
54
49
|
end
|
|
55
50
|
|