minting 1.6.0 → 1.6.2
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 +2 -67
- data/Rakefile +8 -12
- data/bin/check-currencies +29 -0
- data/doc/agents/AGENTS.md +1 -1
- data/doc/agents/copilot-instructions.md +4 -4
- data/doc/agents/recommendations.md +1 -1
- data/lib/minting/data/{currencies.yaml → world-currencies.yaml} +278 -1
- data/lib/minting/mint/currency/currency.rb +36 -0
- data/lib/minting/mint/{currency_store.rb → currency/currency_registry.rb} +28 -2
- data/lib/minting/mint/{world_currencies.rb → currency/world_currencies.rb} +3 -4
- data/lib/minting/mint/mint.rb +15 -2
- data/lib/minting/mint/parser.rb +2 -1
- data/lib/minting/mint.rb +3 -4
- data/lib/minting/money/coercion.rb +4 -0
- data/lib/minting/money/constructors.rb +10 -3
- data/lib/minting/money/conversion.rb +11 -0
- data/lib/minting/money/formatting.rb +3 -3
- data/lib/minting/money/money.rb +11 -1
- data/lib/minting/version.rb +1 -1
- data/lib/minting.rb +0 -1
- metadata +6 -6
- data/lib/minting/mint/currency.rb +0 -25
- data/lib/minting/mint/registry.rb +0 -37
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0af1a3786bbef7fd2c9d3614ccd5076e0ad6f77fb35bd0d34e636355bafd4714
|
|
4
|
+
data.tar.gz: 520dfe15e4a3ddcd180a898769266d27502af54e3ad70503ed734ad351564639
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bd835b1ff970861a98dff3f056feb8903e2cb5145f0b60e52372d754daa65d60461e5200cc07de954963c0f31d954f8a8f54faf0a4f16746119e1195dc3c206e
|
|
7
|
+
data.tar.gz: 5f4e58dea85c7129e00aeef897810053136ba8d30cd49a4f199376b0e4f45885680bf1225b518dd479174e473da6e538efe30831e834e2ce41b3fdaf0a81235c
|
data/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Fast, precise, and developer-friendly money handling for Ruby.
|
|
|
8
8
|
|
|
9
9
|
**Tired of floating-point errors in financial calculations?** Minting uses Rational numbers for perfect precision.
|
|
10
10
|
|
|
11
|
-
**Need performance?** Minting is 2× faster than alternatives for high-volume operations (often 10×+ for formatting). See the [Performance](
|
|
11
|
+
**Need performance?** Minting is 2× faster than alternatives for high-volume operations (often 10×+ for formatting). See the [Performance](https://github.com/gferraz/minting/blob/master/test/performance/README.md) section for full benchmarks.
|
|
12
12
|
|
|
13
13
|
**Want a clean API?** Minting provides an intuitive interface with helpful error messages.
|
|
14
14
|
|
|
@@ -212,75 +212,10 @@ Bug reports and pull requests are welcome on GitHub at <https://github.com/gferr
|
|
|
212
212
|
|
|
213
213
|
1. Fork and create a feature branch
|
|
214
214
|
2. Run the test suite: `rake`
|
|
215
|
-
3. Run performance suites as needed: `
|
|
215
|
+
3. Run performance suites as needed: `rake bench:performance`
|
|
216
216
|
4. Open a PR with a clear description and benchmarks if relevant
|
|
217
217
|
|
|
218
218
|
|
|
219
|
-
## Performance
|
|
220
|
-
|
|
221
|
-
This gem includes a performance suite under `test/performance`:
|
|
222
|
-
|
|
223
|
-
- Core operations (creation, arithmetic, comparisons)
|
|
224
|
-
- Algorithm benchmarks (split, allocate)
|
|
225
|
-
- Memory and GC pressure tests
|
|
226
|
-
- Competitive benchmarks vs `money` gem
|
|
227
|
-
|
|
228
|
-
Run locally:
|
|
229
|
-
|
|
230
|
-
```bash
|
|
231
|
-
# All performance suites
|
|
232
|
-
BENCH=true rake bench:performance
|
|
233
|
-
|
|
234
|
-
# Competitive vs money gem
|
|
235
|
-
BENCH=true rake bench:competitive
|
|
236
|
-
|
|
237
|
-
# Regression checks
|
|
238
|
-
rake bench:regression
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
## Benchmark Summary: Minting vs Money Gem
|
|
242
|
-
|
|
243
|
-
Generated by Qwen from the latest benchmark run on Ruby 4.0.1. - 2026-05-30
|
|
244
|
-
|
|
245
|
-
### Key Takeaways
|
|
246
|
-
|
|
247
|
-
- **Mint is consistently faster** than the Money gem across all measured operations.
|
|
248
|
-
- **Mint is 2.28x faster** in the 50,000-transaction simulation.
|
|
249
|
-
- **Mint object creation is 2.76x faster** than `Money.from_amount`.
|
|
250
|
-
- In formatting and conversion, Mint is often **10+x **.
|
|
251
|
-
- Mint’s performance advantage is especially strong for numeric conversion, string formatting, comparisons, and high-volume transaction loops.
|
|
252
|
-
|
|
253
|
-
### Performance Highlights
|
|
254
|
-
|
|
255
|
-
| Category | Mint | Money | Approx. Ratio |
|
|
256
|
-
| --- | --- | --- | --- |
|
|
257
|
-
| High-volume transactions | 195,412 ops/sec | 85,882 ops/sec | 2.28x faster |
|
|
258
|
-
| `Mint.money` creation | 1.14M ops/sec | — | 2.76x faster than `Money.from_amount` |
|
|
259
|
-
| `some.dollars` creation | 990k ops/sec | — | 1.15x faster than `Mint.money` |
|
|
260
|
-
| `Money.new` creation | — | 715k ops/sec | Mint 1.59x faster |
|
|
261
|
-
| `to_f` formatting | 8.8M–9.3M ops/sec | 0.7M ops/sec | ~12x faster |
|
|
262
|
-
| `to_d` conversion | 2.1M–2.3M ops/sec | 0.73M–0.79M ops/sec | ~3x faster |
|
|
263
|
-
| `to_s` formatting | 300k–420k ops/sec | 109k–132k ops/sec | ~3x faster |
|
|
264
|
-
| `inspect` formatting | ~2.6–2.9M ops/sec | ~1.1–1.16M ops/sec | ~2.5x faster |
|
|
265
|
-
| `to_json` formatting | ~2.0–2.2M ops/sec | ~110k–126k ops/sec | ~17x faster |
|
|
266
|
-
| Currency lookup `Mint.currency('USD')` | 3.82M ops/sec | — | 1.60x faster than `Money::Currency.new` |
|
|
267
|
-
| Currency lookup `Money::Currency.find('USD')` | 3.63M ops/sec | 1.67M ops/sec | 2.29x faster |
|
|
268
|
-
| Addition | 1.11M ops/sec | 0.37M ops/sec | 3.0x faster |
|
|
269
|
-
| Subtraction | 1.11M ops/sec | 0.36M ops/sec | 3.0x faster |
|
|
270
|
-
| Multiplication | 1.28M ops/sec | 0.51M ops/sec | 2.5x faster |
|
|
271
|
-
| Division | 1.04M ops/sec | 0.37M ops/sec | 2.8x faster |
|
|
272
|
-
| Ratio division | 2.94M ops/sec | 0.39M ops/sec | 7.6x faster |
|
|
273
|
-
| Comparison (`==`, `<`, `>`) | 2.5M–4.1M ops/sec | 0.35M–0.38M ops/sec | 7x–10x faster |
|
|
274
|
-
| Allocation (`Mint.allocate`) | 279k ops/sec | 146k ops/sec | 1.9x faster |
|
|
275
|
-
| Split (`Mint.split`) | 215k ops/sec | 85k ops/sec | 3.3x faster |
|
|
276
|
-
|
|
277
|
-
### Commands Used
|
|
278
|
-
|
|
279
|
-
```sh
|
|
280
|
-
BENCH=true bundle exec ruby -Ilib:test -r ./test/test_helper.rb test/performance/competitive_performance_benchmark.rb
|
|
281
|
-
BENCH=true bundle exec ruby -Ilib:test -r ./test/test_helper.rb test/performance/competitive_memory_benchmark.rb
|
|
282
|
-
```
|
|
283
|
-
|
|
284
219
|
## License
|
|
285
220
|
|
|
286
221
|
MIT
|
data/Rakefile
CHANGED
|
@@ -12,33 +12,29 @@ Rake::TestTask.new(:test) do |t|
|
|
|
12
12
|
t.ruby_opts << '-rtest_helper.rb'
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
Rake::TestTask.new('bench') do |t|
|
|
15
|
+
Rake::TestTask.new('bench:all') do |t|
|
|
16
16
|
t.libs = %w[lib test]
|
|
17
|
-
t.pattern = 'test/performance
|
|
17
|
+
t.pattern = 'test/performance/**/*_benchmark.rb'
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
Rake::TestTask.new('bench:
|
|
20
|
+
Rake::TestTask.new('bench:core') do |t|
|
|
21
21
|
t.libs = %w[lib test]
|
|
22
|
-
t.pattern = 'test/performance/parse_benchmark.rb'
|
|
23
|
-
t.ruby_opts << '-r test_helper.rb'
|
|
22
|
+
t.pattern = 'test/performance/core/parse_benchmark.rb'
|
|
24
23
|
end
|
|
25
24
|
|
|
26
|
-
Rake::TestTask.new('bench:
|
|
25
|
+
Rake::TestTask.new('bench:memory') do |t|
|
|
27
26
|
t.libs = %w[lib test]
|
|
28
|
-
t.pattern = 'test/performance/
|
|
29
|
-
t.ruby_opts << '-r test_helper.rb'
|
|
27
|
+
t.pattern = 'test/performance/memory/*_benchmark.rb'
|
|
30
28
|
end
|
|
31
29
|
|
|
32
30
|
Rake::TestTask.new('bench:regression') do |t|
|
|
33
31
|
t.libs = %w[lib test]
|
|
34
|
-
t.pattern = 'test/performance/
|
|
35
|
-
t.ruby_opts << '-r test_helper.rb'
|
|
32
|
+
t.pattern = 'test/performance/regression/*_benchmark.rb'
|
|
36
33
|
end
|
|
37
34
|
|
|
38
35
|
Rake::TestTask.new('bench:competitive') do |t|
|
|
39
36
|
t.libs = %w[lib test]
|
|
40
|
-
t.pattern = 'test/performance/
|
|
41
|
-
t.ruby_opts << '-r test_helper.rb'
|
|
37
|
+
t.pattern = 'test/performance/competitive/**/*_benchmark.rb'
|
|
42
38
|
end
|
|
43
39
|
|
|
44
40
|
RuboCop::RakeTask.new(:cop)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
require "set"
|
|
5
|
+
|
|
6
|
+
yaml = YAML.load_file(
|
|
7
|
+
File.expand_path("~/code/minting/lib/minting/data/world-currencies.yaml")
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
minting_codes = yaml.map { |c| c["code"] }.to_set
|
|
11
|
+
|
|
12
|
+
iso_codes = %w[
|
|
13
|
+
AED AFN ALL AMD ANG AOA ARS AUD AWG AZN BAM BBD BDT BGN BHD BIF BMD BND BOB
|
|
14
|
+
BRL BSD BTN BWP BYN BZD CAD CDF CHF CLP CNY COP CRC CUP CVE CZK DJF DKK DOP
|
|
15
|
+
DZD EGP ERN ETB EUR FJD FKP FOK GBP GEL GGP GHS GIP GMD GNF GTQ GYD HKD HNL
|
|
16
|
+
HRK HTG HUF IDR ILS IMP INR IQD IRR ISK JEP JMD JOD JPY KES KGS KHR KID KMF
|
|
17
|
+
KRW KWD KYD KZT LAK LBP LKR LRD LSL LYD MAD MDL MGA MKD MMK MNT MOP MRU MUR
|
|
18
|
+
MVR MWK MXN MYR MZN NAD NGN NIO NOK NPR NZD OMR PAB PEN PGK PHP PKR PLN PYG
|
|
19
|
+
QAR RON RSD RUB RWF SAR SBD SCR SDG SEK SGD SHP SLE SLL SOS SRD SSP STN SVC
|
|
20
|
+
SYP SZL THB TJS TMT TND TOP TRY TTD TVD TWD TZS UAH UGX USD UYU UZS VES VND
|
|
21
|
+
VUV WST XAF XCD XDR XOF XPF YER ZAR ZMW ZWL
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
missing = iso_codes.reject { |c| minting_codes.include?(c) }
|
|
25
|
+
if missing.empty?
|
|
26
|
+
puts "Currencies: #{minting_codes.size}. No currency missing!"
|
|
27
|
+
else
|
|
28
|
+
puts "Missing: #{missing.sort.join(", ")}"
|
|
29
|
+
end
|
data/doc/agents/AGENTS.md
CHANGED
|
@@ -20,6 +20,6 @@ Project highlights & conventions:
|
|
|
20
20
|
- Amounts stored as Rational; prefer rationals or decimal strings for precision
|
|
21
21
|
- Zero-equality: zeros equal across currencies; non-zero comparisons require same currency
|
|
22
22
|
- Currency codes must match /^[A-Z_]+$/
|
|
23
|
-
- Tests: Minitest; performance benches under test/performance
|
|
23
|
+
- Tests: Minitest; performance benches under test/performance
|
|
24
24
|
|
|
25
25
|
Edit guidance: keep this file minimal and link to existing docs; add repo-specific agent tips here.
|
|
@@ -21,9 +21,9 @@ Build, test and lint commands
|
|
|
21
21
|
- Run a single test method by name (Minitest -n regexp):
|
|
22
22
|
- ruby -Ilib:test -r ./test/test_helper.rb test/money/money_test.rb -n /test_creation/
|
|
23
23
|
|
|
24
|
-
- Performance suites
|
|
25
|
-
-
|
|
26
|
-
-
|
|
24
|
+
- Performance suites:
|
|
25
|
+
- rake bench:performance
|
|
26
|
+
- rake bench:competitive
|
|
27
27
|
- rake bench:regression
|
|
28
28
|
|
|
29
29
|
- Linting:
|
|
@@ -56,7 +56,7 @@ Key conventions and repo-specific rules
|
|
|
56
56
|
- Currency registration: use Mint.register_currency for idempotent registration; Mint.register_currency! raises on duplicates. Codes must match /^[A-Z_]+$/.
|
|
57
57
|
- Symbol parsing: parser resolves symbols by longest match then currency priority (see Mint.currency_symbols sorting).
|
|
58
58
|
- Tests: test_helper.rb configures coverage (SimpleCov) and loads minitest; when running tests outside rake, require test_helper (-r ./test/test_helper.rb).
|
|
59
|
-
- Benchmarks: set
|
|
59
|
+
- Benchmarks: set to enable benchmark-heavy tasks; benchmarks use Minitest::Benchmark patterns.
|
|
60
60
|
- Formatting: Money.to_s uses Kernel.format patterns; take care with %<amount>f vs %<amount>d depending on desired rounding/formatting.
|
|
61
61
|
|
|
62
62
|
Files and places to check first during edits
|
|
@@ -133,6 +133,12 @@
|
|
|
133
133
|
symbol: د.إ
|
|
134
134
|
name: United Arab Emirates Dirham
|
|
135
135
|
priority: 600
|
|
136
|
+
- code: YER
|
|
137
|
+
country: YEM
|
|
138
|
+
subunit: 2
|
|
139
|
+
symbol: "﷼"
|
|
140
|
+
name: Yemeni Rial
|
|
141
|
+
priority: 114
|
|
136
142
|
- code: IDR
|
|
137
143
|
country: IDN
|
|
138
144
|
subunit: 2
|
|
@@ -151,6 +157,12 @@
|
|
|
151
157
|
symbol: "₦"
|
|
152
158
|
name: Nigerian Naira
|
|
153
159
|
priority: 111
|
|
160
|
+
- code: SOS
|
|
161
|
+
country: SOM
|
|
162
|
+
subunit: 2
|
|
163
|
+
symbol: Sh
|
|
164
|
+
name: Somali Shilling
|
|
165
|
+
priority: 110
|
|
154
166
|
- code: BDT
|
|
155
167
|
country: BGD
|
|
156
168
|
subunit: 2
|
|
@@ -169,12 +181,24 @@
|
|
|
169
181
|
symbol: "₽"
|
|
170
182
|
name: Russian Ruble
|
|
171
183
|
priority: 107
|
|
184
|
+
- code: SYP
|
|
185
|
+
country: SYR
|
|
186
|
+
subunit: 2
|
|
187
|
+
symbol: "£"
|
|
188
|
+
name: Syrian Pound
|
|
189
|
+
priority: 106
|
|
172
190
|
- code: ETB
|
|
173
191
|
country: ETH
|
|
174
192
|
subunit: 2
|
|
175
193
|
symbol: Br
|
|
176
194
|
name: Ethiopian Birr
|
|
177
195
|
priority: 105
|
|
196
|
+
- code: ZWL
|
|
197
|
+
country: ZWE
|
|
198
|
+
subunit: 2
|
|
199
|
+
symbol: Z$
|
|
200
|
+
name: Zimbabwean Dollar
|
|
201
|
+
priority: 104
|
|
178
202
|
- code: PHP
|
|
179
203
|
country: PHL
|
|
180
204
|
subunit: 2
|
|
@@ -211,6 +235,12 @@
|
|
|
211
235
|
symbol: "฿"
|
|
212
236
|
name: Thai Baht
|
|
213
237
|
priority: 98
|
|
238
|
+
- code: TMT
|
|
239
|
+
country: TKM
|
|
240
|
+
subunit: 2
|
|
241
|
+
symbol: m
|
|
242
|
+
name: Turkmenistani Manat
|
|
243
|
+
priority: 97
|
|
214
244
|
- code: TZS
|
|
215
245
|
country: TZA
|
|
216
246
|
subunit: 2
|
|
@@ -223,6 +253,12 @@
|
|
|
223
253
|
symbol: FCFA
|
|
224
254
|
name: Central African CFA franc
|
|
225
255
|
priority: 95
|
|
256
|
+
- code: DJF
|
|
257
|
+
country: DJI
|
|
258
|
+
subunit: 0
|
|
259
|
+
symbol: Fdj
|
|
260
|
+
name: Djiboutian Franc
|
|
261
|
+
priority: 94
|
|
226
262
|
- code: KES
|
|
227
263
|
country: KEN
|
|
228
264
|
subunit: 2
|
|
@@ -241,6 +277,12 @@
|
|
|
241
277
|
symbol: "$"
|
|
242
278
|
name: Colombian Peso
|
|
243
279
|
priority: 91
|
|
280
|
+
- code: ERN
|
|
281
|
+
country: ERI
|
|
282
|
+
subunit: 2
|
|
283
|
+
symbol: Nfk
|
|
284
|
+
name: Eritrean Nakfa
|
|
285
|
+
priority: 90
|
|
244
286
|
- code: UGX
|
|
245
287
|
country: UGA
|
|
246
288
|
subunit: 0
|
|
@@ -271,6 +313,12 @@
|
|
|
271
313
|
symbol: "؋"
|
|
272
314
|
name: Afghan Afghani
|
|
273
315
|
priority: 85
|
|
316
|
+
- code: LBP
|
|
317
|
+
country: LBN
|
|
318
|
+
subunit: 2
|
|
319
|
+
symbol: "£"
|
|
320
|
+
name: Lebanese Pound
|
|
321
|
+
priority: 84
|
|
274
322
|
- code: MAD
|
|
275
323
|
country: MAR
|
|
276
324
|
subunit: 2
|
|
@@ -283,6 +331,12 @@
|
|
|
283
331
|
symbol: zł
|
|
284
332
|
name: Polish Zloty
|
|
285
333
|
priority: 82
|
|
334
|
+
- code: SVC
|
|
335
|
+
country: SLV
|
|
336
|
+
subunit: 2
|
|
337
|
+
symbol: "$"
|
|
338
|
+
name: El Salvadoran Colón
|
|
339
|
+
priority: 81
|
|
286
340
|
- code: UAH
|
|
287
341
|
country: UKR
|
|
288
342
|
subunit: 2
|
|
@@ -337,6 +391,12 @@
|
|
|
337
391
|
symbol: Bs.
|
|
338
392
|
name: Venezuelan Bolívar
|
|
339
393
|
priority: 72
|
|
394
|
+
- code: SSP
|
|
395
|
+
country: SSD
|
|
396
|
+
subunit: 2
|
|
397
|
+
symbol: "£"
|
|
398
|
+
name: South Sudanese Pound
|
|
399
|
+
priority: 71
|
|
340
400
|
- code: TWD
|
|
341
401
|
country: TWN
|
|
342
402
|
subunit: 2
|
|
@@ -445,6 +505,18 @@
|
|
|
445
505
|
symbol: د.ا
|
|
446
506
|
name: Jordanian Dinar
|
|
447
507
|
priority: 53
|
|
508
|
+
- code: ALL
|
|
509
|
+
country: ALB
|
|
510
|
+
subunit: 2
|
|
511
|
+
symbol: L
|
|
512
|
+
name: Albanian Lek
|
|
513
|
+
priority: 52
|
|
514
|
+
- code: MKD
|
|
515
|
+
country: MKD
|
|
516
|
+
subunit: 2
|
|
517
|
+
symbol: ден
|
|
518
|
+
name: North Macedonian Denar
|
|
519
|
+
priority: 51
|
|
448
520
|
- code: AZN
|
|
449
521
|
country: AZE
|
|
450
522
|
subunit: 2
|
|
@@ -481,6 +553,18 @@
|
|
|
481
553
|
symbol: Br
|
|
482
554
|
name: Belarusian Ruble
|
|
483
555
|
priority: 45
|
|
556
|
+
- code: BAM
|
|
557
|
+
country: BIH
|
|
558
|
+
subunit: 2
|
|
559
|
+
symbol: KM
|
|
560
|
+
name: Bosnia and Herzegovina Convertible Mark
|
|
561
|
+
priority: 44
|
|
562
|
+
- code: MOP
|
|
563
|
+
country: MAC
|
|
564
|
+
subunit: 2
|
|
565
|
+
symbol: P
|
|
566
|
+
name: Macanese Pataca
|
|
567
|
+
priority: 43
|
|
484
568
|
- code: LAK
|
|
485
569
|
country: LAO
|
|
486
570
|
subunit: 2
|
|
@@ -493,6 +577,12 @@
|
|
|
493
577
|
symbol: лв
|
|
494
578
|
name: Kyrgyzstani Som
|
|
495
579
|
priority: 41
|
|
580
|
+
- code: MVR
|
|
581
|
+
country: MDV
|
|
582
|
+
subunit: 2
|
|
583
|
+
symbol: Rf
|
|
584
|
+
name: Maldivian Rufiyaa
|
|
585
|
+
priority: 40
|
|
496
586
|
- code: LYD
|
|
497
587
|
country: LBY
|
|
498
588
|
subunit: 3
|
|
@@ -523,6 +613,30 @@
|
|
|
523
613
|
symbol: лв
|
|
524
614
|
name: Bulgarian Lev
|
|
525
615
|
priority: 36
|
|
616
|
+
- code: GNF
|
|
617
|
+
country: GIN
|
|
618
|
+
subunit: 0
|
|
619
|
+
symbol: Fr
|
|
620
|
+
name: Guinean Franc
|
|
621
|
+
priority: 35
|
|
622
|
+
- code: GMD
|
|
623
|
+
country: GMB
|
|
624
|
+
subunit: 2
|
|
625
|
+
symbol: D
|
|
626
|
+
name: Gambian Dalasi
|
|
627
|
+
priority: 34
|
|
628
|
+
- code: SLL
|
|
629
|
+
country: SLE
|
|
630
|
+
subunit: 2
|
|
631
|
+
symbol: Le
|
|
632
|
+
name: Sierra Leonean Leone
|
|
633
|
+
priority: 33
|
|
634
|
+
- code: STN
|
|
635
|
+
country: STP
|
|
636
|
+
subunit: 2
|
|
637
|
+
symbol: Db
|
|
638
|
+
name: São Tomé and Príncipe Dobra
|
|
639
|
+
priority: 31
|
|
526
640
|
- code: CRC
|
|
527
641
|
country: CRI
|
|
528
642
|
subunit: 2
|
|
@@ -535,6 +649,12 @@
|
|
|
535
649
|
symbol: د.ك
|
|
536
650
|
name: Kuwaiti Dinar
|
|
537
651
|
priority: 30
|
|
652
|
+
- code: BTN
|
|
653
|
+
country: BTN
|
|
654
|
+
subunit: 2
|
|
655
|
+
symbol: Nu.
|
|
656
|
+
name: Bhutanese Ngultrum
|
|
657
|
+
priority: 30
|
|
538
658
|
- code: OMR
|
|
539
659
|
country: OMN
|
|
540
660
|
subunit: 3
|
|
@@ -553,6 +673,12 @@
|
|
|
553
673
|
symbol: kn
|
|
554
674
|
name: Croatian Kuna
|
|
555
675
|
priority: 27
|
|
676
|
+
- code: KMF
|
|
677
|
+
country: COM
|
|
678
|
+
subunit: 0
|
|
679
|
+
symbol: CF
|
|
680
|
+
name: Comorian Franc
|
|
681
|
+
priority: 26
|
|
556
682
|
- code: GEL
|
|
557
683
|
country: GEO
|
|
558
684
|
subunit: 2
|
|
@@ -565,6 +691,12 @@
|
|
|
565
691
|
symbol: "$U"
|
|
566
692
|
name: Uruguayan Peso
|
|
567
693
|
priority: 25
|
|
694
|
+
- code: SCR
|
|
695
|
+
country: SYC
|
|
696
|
+
subunit: 2
|
|
697
|
+
symbol: "₨"
|
|
698
|
+
name: Seychellois Rupee
|
|
699
|
+
priority: 24
|
|
568
700
|
- code: AMD
|
|
569
701
|
country: ARM
|
|
570
702
|
subunit: 2
|
|
@@ -583,6 +715,12 @@
|
|
|
583
715
|
symbol: "﷼"
|
|
584
716
|
name: Qatari Riyal
|
|
585
717
|
priority: 22
|
|
718
|
+
- code: MUR
|
|
719
|
+
country: MUS
|
|
720
|
+
subunit: 2
|
|
721
|
+
symbol: "₨"
|
|
722
|
+
name: Mauritian Rupee
|
|
723
|
+
priority: 22
|
|
586
724
|
- code: BWP
|
|
587
725
|
country: BWA
|
|
588
726
|
subunit: 2
|
|
@@ -595,24 +733,48 @@
|
|
|
595
733
|
symbol: L
|
|
596
734
|
name: Moldovan Leu
|
|
597
735
|
priority: 20
|
|
736
|
+
- code: ANG
|
|
737
|
+
country: null
|
|
738
|
+
subunit: 2
|
|
739
|
+
symbol: ƒ
|
|
740
|
+
name: Netherlands Antillean Guilder
|
|
741
|
+
priority: 19
|
|
598
742
|
- code: NAD
|
|
599
743
|
country: NAM
|
|
600
744
|
subunit: 2
|
|
601
745
|
symbol: N$
|
|
602
746
|
name: Namibian Dollar
|
|
603
747
|
priority: 19
|
|
748
|
+
- code: CVE
|
|
749
|
+
country: CPV
|
|
750
|
+
subunit: 2
|
|
751
|
+
symbol: $
|
|
752
|
+
name: Cape Verdean Escudo
|
|
753
|
+
priority: 18
|
|
604
754
|
- code: LSL
|
|
605
755
|
country: LSO
|
|
606
756
|
subunit: 2
|
|
607
757
|
symbol: L
|
|
608
758
|
name: Lesotho Loti
|
|
609
759
|
priority: 18
|
|
760
|
+
- code: GIP
|
|
761
|
+
country: GIB
|
|
762
|
+
subunit: 2
|
|
763
|
+
symbol: £
|
|
764
|
+
name: Gibraltar Pound
|
|
765
|
+
priority: 17
|
|
610
766
|
- code: BHD
|
|
611
767
|
country: BHR
|
|
612
768
|
subunit: 3
|
|
613
769
|
symbol: ".د.ب"
|
|
614
770
|
name: Bahraini Dinar
|
|
615
771
|
priority: 17
|
|
772
|
+
- code: FKP
|
|
773
|
+
country: FLK
|
|
774
|
+
subunit: 2
|
|
775
|
+
symbol: £
|
|
776
|
+
name: Falkland Islands Pound
|
|
777
|
+
priority: 16
|
|
616
778
|
- code: TTD
|
|
617
779
|
country: TTO
|
|
618
780
|
subunit: 2
|
|
@@ -710,8 +872,123 @@
|
|
|
710
872
|
name: Tongan Paʻanga
|
|
711
873
|
priority: 1
|
|
712
874
|
- code: XXX
|
|
713
|
-
country:
|
|
875
|
+
country: null
|
|
714
876
|
subunit: 0
|
|
715
877
|
symbol: ¤
|
|
716
878
|
name: No Currency
|
|
717
879
|
priority: 0
|
|
880
|
+
- code: AWG
|
|
881
|
+
country: ABW
|
|
882
|
+
subunit: 2
|
|
883
|
+
symbol: ƒ
|
|
884
|
+
name: Aruban Florin
|
|
885
|
+
priority: 0
|
|
886
|
+
- code: BMD
|
|
887
|
+
country: BMU
|
|
888
|
+
subunit: 2
|
|
889
|
+
symbol: "$"
|
|
890
|
+
name: Bermudian Dollar
|
|
891
|
+
priority: 0
|
|
892
|
+
- code: CDF
|
|
893
|
+
country: COD
|
|
894
|
+
subunit: 2
|
|
895
|
+
symbol: Fr
|
|
896
|
+
name: Congolese Franc
|
|
897
|
+
priority: 0
|
|
898
|
+
- code: CUP
|
|
899
|
+
country: CUB
|
|
900
|
+
subunit: 2
|
|
901
|
+
symbol: "$"
|
|
902
|
+
name: Cuban Peso
|
|
903
|
+
priority: 0
|
|
904
|
+
- code: FOK
|
|
905
|
+
country: FRO
|
|
906
|
+
subunit: 2
|
|
907
|
+
symbol: kr
|
|
908
|
+
name: Faroese Króna
|
|
909
|
+
priority: 0
|
|
910
|
+
- code: GGP
|
|
911
|
+
country: GGY
|
|
912
|
+
subunit: 2
|
|
913
|
+
symbol: £
|
|
914
|
+
name: Guernsey Pound
|
|
915
|
+
priority: 0
|
|
916
|
+
- code: IMP
|
|
917
|
+
country: IMN
|
|
918
|
+
subunit: 2
|
|
919
|
+
symbol: £
|
|
920
|
+
name: Manx Pound
|
|
921
|
+
priority: 0
|
|
922
|
+
- code: JEP
|
|
923
|
+
country: JEY
|
|
924
|
+
subunit: 2
|
|
925
|
+
symbol: £
|
|
926
|
+
name: Jersey Pound
|
|
927
|
+
priority: 0
|
|
928
|
+
- code: KID
|
|
929
|
+
country: KIR
|
|
930
|
+
subunit: 2
|
|
931
|
+
symbol: "$"
|
|
932
|
+
name: Kiribati Dollar
|
|
933
|
+
priority: 0
|
|
934
|
+
- code: KYD
|
|
935
|
+
country: CYM
|
|
936
|
+
subunit: 2
|
|
937
|
+
symbol: "$"
|
|
938
|
+
name: Cayman Islands Dollar
|
|
939
|
+
priority: 0
|
|
940
|
+
- code: LRD
|
|
941
|
+
country: LBR
|
|
942
|
+
subunit: 2
|
|
943
|
+
symbol: "$"
|
|
944
|
+
name: Liberian Dollar
|
|
945
|
+
priority: 0
|
|
946
|
+
- code: MGA
|
|
947
|
+
country: MDG
|
|
948
|
+
subunit: 2
|
|
949
|
+
symbol: Ar
|
|
950
|
+
name: Malagasy Ariary
|
|
951
|
+
priority: 0
|
|
952
|
+
- code: MNT
|
|
953
|
+
country: MNG
|
|
954
|
+
subunit: 2
|
|
955
|
+
symbol: "₮"
|
|
956
|
+
name: Mongolian Tögrög
|
|
957
|
+
priority: 0
|
|
958
|
+
- code: MRU
|
|
959
|
+
country: MRT
|
|
960
|
+
subunit: 2
|
|
961
|
+
symbol: UM
|
|
962
|
+
name: Mauritanian Ouguiya
|
|
963
|
+
priority: 0
|
|
964
|
+
- code: SDG
|
|
965
|
+
country: SDN
|
|
966
|
+
subunit: 2
|
|
967
|
+
symbol: "£"
|
|
968
|
+
name: Sudanese Pound
|
|
969
|
+
priority: 0
|
|
970
|
+
- code: SHP
|
|
971
|
+
country: SHN
|
|
972
|
+
subunit: 2
|
|
973
|
+
symbol: "£"
|
|
974
|
+
name: Saint Helena Pound
|
|
975
|
+
priority: 0
|
|
976
|
+
- code: SLE
|
|
977
|
+
country: SLE
|
|
978
|
+
subunit: 2
|
|
979
|
+
symbol: Le
|
|
980
|
+
name: Sierra Leonean Leone (re-denominated)
|
|
981
|
+
priority: 0
|
|
982
|
+
- code: TVD
|
|
983
|
+
country: TUV
|
|
984
|
+
subunit: 2
|
|
985
|
+
symbol: "$"
|
|
986
|
+
name: Tuvaluan Dollar
|
|
987
|
+
priority: 0
|
|
988
|
+
- code: XDR
|
|
989
|
+
country: null
|
|
990
|
+
subunit: 0
|
|
991
|
+
symbol: SDR
|
|
992
|
+
name: Special Drawing Rights
|
|
993
|
+
priority: 0
|
|
994
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mint
|
|
4
|
+
# Represents a specific currency unit, identified by ISO 4217 alphabetic code.
|
|
5
|
+
# Currency objects are immutable and define the properties of a monetary unit
|
|
6
|
+
# including its subunit precision, display symbol, and formatting rules.
|
|
7
|
+
#
|
|
8
|
+
# @see https://www.iso.org/iso-4217-currency-codes.html
|
|
9
|
+
# @attr_reader code [String] ISO 4217 currency code (e.g., "USD", "EUR")
|
|
10
|
+
# @attr_reader subunit [Integer] Number of decimal places (0 for JPY, 2 for USD, 3 for IQD)
|
|
11
|
+
# @attr_reader symbol [String] Display symbol (e.g., "$", "€", "R$")
|
|
12
|
+
# @attr_reader priority [Integer] Parser precedence for symbol detection
|
|
13
|
+
# @attr_reader country [String, nil] Associated country code
|
|
14
|
+
# @attr_reader name [String, nil] Currency name
|
|
15
|
+
# @attr_reader fractional_multiplier [Integer] 10^subunit, used for fractional conversions
|
|
16
|
+
# @attr_reader minimum_amount [Rational] Smallest representable amount (1/fractional_multiplier)
|
|
17
|
+
Currency = Data.define(:code, :subunit, :symbol, :priority, :country, :name,
|
|
18
|
+
:fractional_multiplier) do
|
|
19
|
+
def initialize(code:, symbol:, subunit: 0, priority: 0, country: nil, name: nil)
|
|
20
|
+
subunit = subunit.to_i
|
|
21
|
+
priority = priority.to_i
|
|
22
|
+
fractional_multiplier = 10**subunit
|
|
23
|
+
super(code:, subunit:, symbol:, priority:, country:, name:,
|
|
24
|
+
fractional_multiplier:)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def inspect = "<Currency:(#{code} #{symbol} #{subunit} #{name})>"
|
|
28
|
+
|
|
29
|
+
def minimum_amount = Rational(1, fractional_multiplier)
|
|
30
|
+
|
|
31
|
+
# Normalizes numeric amounts for this currency
|
|
32
|
+
# 1. Converts to Rational
|
|
33
|
+
# 2. Rounds to respect currency subunit
|
|
34
|
+
def normalize_amount(amount) = amount.to_r.round(subunit)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -6,8 +6,8 @@ require 'yaml'
|
|
|
6
6
|
module Mint
|
|
7
7
|
# Internal currency registry
|
|
8
8
|
# Manages the registry cache and currency symbol lookups.
|
|
9
|
-
module
|
|
10
|
-
|
|
9
|
+
module CurrencyRegistry
|
|
10
|
+
extend self
|
|
11
11
|
|
|
12
12
|
# Returns the hash of all registered currencies.
|
|
13
13
|
#
|
|
@@ -30,6 +30,32 @@ module Mint
|
|
|
30
30
|
end.freeze
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
# Registers a new currency, raising a KeyError if already registered.
|
|
34
|
+
#
|
|
35
|
+
# @param code [String] the unique currency code
|
|
36
|
+
# @param subunit [Integer] the decimal subunit precision, defaults to 0
|
|
37
|
+
# @param symbol [String] the display symbol
|
|
38
|
+
# @param priority [Integer] parser precedence priority
|
|
39
|
+
# @return [Currency] the newly registered Currency instance
|
|
40
|
+
# @raise [ArgumentError] if the code contains invalid characters
|
|
41
|
+
# @raise [KeyError] if the currency code is already registered
|
|
42
|
+
def register(code:, subunit: 0, symbol: '', priority: 0)
|
|
43
|
+
raise ArgumentError, 'Currency code must be String' unless code.is_a? String
|
|
44
|
+
unless code.match?(/^[A-Z_]+$/)
|
|
45
|
+
raise ArgumentError,
|
|
46
|
+
"Currency code must have only letters or '_' ('USD',, 'MY_COIN')"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
currencies = CurrencyRegistry.currencies
|
|
50
|
+
raise KeyError, "Currency: #{code} already registered" if currencies[code]
|
|
51
|
+
|
|
52
|
+
currency = currencies[code] = Currency.new(code:, subunit:, symbol:, priority:)
|
|
53
|
+
invalidate_symbols_cache
|
|
54
|
+
currency
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
33
59
|
# Clears and refreshes the currency symbol cache.
|
|
34
60
|
# Called when currencies are registered.
|
|
35
61
|
#
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# Mint list of world currencies
|
|
3
4
|
module Mint
|
|
4
|
-
module_function
|
|
5
|
-
|
|
6
5
|
# Loads ISO world currencies from YAML file into the registry.
|
|
7
6
|
#
|
|
8
7
|
# @return [Hash{String => Currency}] ISO-4217 world currencies mapped by code
|
|
9
8
|
# @api private
|
|
10
|
-
def world_currencies
|
|
9
|
+
def self.world_currencies
|
|
11
10
|
@world_currencies ||= begin
|
|
12
|
-
path = File.join(File.expand_path('
|
|
11
|
+
path = File.join(File.expand_path('../../data', __dir__), 'world-currencies.yaml')
|
|
13
12
|
|
|
14
13
|
YAML.load_file(path).to_h { |entry| [entry['code'], Currency.new(**entry.transform_keys(&:to_sym))] }
|
|
15
14
|
end.freeze
|
data/lib/minting/mint/mint.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Mint currency registration and factory (public API)
|
|
4
4
|
module Mint
|
|
5
5
|
# Unknown currency excpetion
|
|
6
|
-
class
|
|
6
|
+
class UnknownCurrency < StandardError
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
# Creates a new {Money} instance with the given amount and currency code.
|
|
@@ -23,8 +23,21 @@ module Mint
|
|
|
23
23
|
case currency
|
|
24
24
|
when nil then nil
|
|
25
25
|
when Currency then currency
|
|
26
|
-
when String then
|
|
26
|
+
when String then CurrencyRegistry.currencies[currency]
|
|
27
27
|
else raise ArgumentError, "currency must be [Currency] ot [String] (#{currency})"
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
|
+
|
|
31
|
+
# Registers a new currency, raising a KeyError if already registered.
|
|
32
|
+
#
|
|
33
|
+
# @param code [String] the unique currency code
|
|
34
|
+
# @param subunit [Integer] the decimal subunit precision, defaults to 0
|
|
35
|
+
# @param symbol [String] the display symbol
|
|
36
|
+
# @param priority [Integer] parser precedence priority
|
|
37
|
+
# @return [Currency] the newly registered Currency instance
|
|
38
|
+
# @raise [ArgumentError] if the code contains invalid characters
|
|
39
|
+
# @raise [KeyError] if the currency code is already registered
|
|
40
|
+
def self.register_currency(code:, subunit: 0, symbol: '', priority: 0)
|
|
41
|
+
CurrencyRegistry.register(code:, subunit:, symbol:, priority:)
|
|
42
|
+
end
|
|
30
43
|
end
|
data/lib/minting/mint/parser.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# Mint Money parsing
|
|
3
4
|
module Mint
|
|
4
5
|
extend self
|
|
5
6
|
|
|
@@ -68,7 +69,7 @@ module Mint
|
|
|
68
69
|
return currency if currency
|
|
69
70
|
|
|
70
71
|
# Fall back to registered symbols, longest first (HK$ before $).
|
|
71
|
-
|
|
72
|
+
CurrencyRegistry.currency_symbols.each do |symbol, currency|
|
|
72
73
|
return currency if input.include?(symbol)
|
|
73
74
|
end
|
|
74
75
|
end
|
data/lib/minting/mint.rb
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'minting/mint/currency'
|
|
4
|
-
require 'minting/mint/
|
|
3
|
+
require 'minting/mint/currency/currency'
|
|
4
|
+
require 'minting/mint/currency/currency_registry'
|
|
5
|
+
require 'minting/mint/currency/world_currencies'
|
|
5
6
|
require 'minting/mint/mint'
|
|
6
7
|
require 'minting/mint/parser'
|
|
7
|
-
require 'minting/mint/registry'
|
|
8
8
|
require 'minting/mint/refinements'
|
|
9
|
-
require 'minting/mint/world_currencies'
|
|
10
9
|
require 'minting/money/allocation'
|
|
11
10
|
require 'minting/money/arithmetics'
|
|
12
11
|
require 'minting/money/coercion'
|
|
@@ -4,9 +4,13 @@ module Mint
|
|
|
4
4
|
# Implements the standard Ruby coercion protocol.
|
|
5
5
|
class Money
|
|
6
6
|
# Allows {Money} to interact seamlessly as the right-hand operand in Numeric arithmetic.
|
|
7
|
+
# This enables expressions like `5 + money` where `5` is a Numeric and `money` is a Money object.
|
|
7
8
|
#
|
|
8
9
|
# @param other [Numeric] the left-hand operand to coerce
|
|
9
10
|
# @return [Array(CoercedNumber, Money)] coerced operand array
|
|
11
|
+
# @example
|
|
12
|
+
# price = Mint.money(10, 'USD')
|
|
13
|
+
# 5 + price #=> [USD 15.00] (via coercion)
|
|
10
14
|
def coerce(other)
|
|
11
15
|
[CoercedNumber.new(other), self]
|
|
12
16
|
end
|
|
@@ -44,9 +44,16 @@ module Mint
|
|
|
44
44
|
new(amount, checked_currency)
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
# Returns a new Money object with the specified amount, or self if unchanged
|
|
48
|
-
#
|
|
49
|
-
#
|
|
47
|
+
# Returns a new Money object with the specified amount, or self if unchanged.
|
|
48
|
+
# This is the primary method for creating a modified copy of a Money instance
|
|
49
|
+
# while preserving immutability.
|
|
50
|
+
#
|
|
51
|
+
# @param new_amount [Numeric] The new monetary amount
|
|
52
|
+
# @return [Money] A new Money object with the new amount, or self if the amount is unchanged
|
|
53
|
+
# @example
|
|
54
|
+
# price = Mint.money(10.00, 'USD')
|
|
55
|
+
# price.mint(15.00) #=> [USD 15.00]
|
|
56
|
+
# price.mint(10.00) #=> [USD 10.00] (returns self)
|
|
50
57
|
def mint(new_amount)
|
|
51
58
|
new_amount = currency.normalize_amount(new_amount)
|
|
52
59
|
new_amount == amount ? self : Money.new(new_amount, currency)
|
|
@@ -9,6 +9,8 @@ module Mint
|
|
|
9
9
|
# Converts the monetary amount to a BigDecimal object.
|
|
10
10
|
#
|
|
11
11
|
# @return [BigDecimal] the decimal representation of the money amount
|
|
12
|
+
# @example
|
|
13
|
+
# Mint.money(9.99, 'USD').to_d #=> 0.999e1
|
|
12
14
|
def to_d = amount.to_d 0
|
|
13
15
|
|
|
14
16
|
# Converts the monetary amount to a standard float.
|
|
@@ -31,8 +33,17 @@ module Mint
|
|
|
31
33
|
# Truncates and converts the monetary amount to an Integer.
|
|
32
34
|
#
|
|
33
35
|
# @return [Integer] the integer representation of the money amount
|
|
36
|
+
# @example
|
|
37
|
+
# Mint.money(9.99, 'USD').to_i #=> 9
|
|
38
|
+
# Mint.money(-9.99, 'USD').to_i #=> -9
|
|
34
39
|
def to_i = amount.to_i
|
|
35
40
|
|
|
41
|
+
# Returns a Hash representation of the money instance.
|
|
42
|
+
#
|
|
43
|
+
# @return [Hash] hash with :currency (String) and :amount (String) keys
|
|
44
|
+
# @example
|
|
45
|
+
# Mint.money(134120, 'BRL').to_hash
|
|
46
|
+
# #=> { currency: "BRL", amount: "134120.00" }
|
|
36
47
|
def to_hash
|
|
37
48
|
{ currency: currency_code, amount: Kernel.format("%0.#{currency.subunit}f", amount) }
|
|
38
49
|
end
|
|
@@ -91,9 +91,9 @@ module Mint
|
|
|
91
91
|
end
|
|
92
92
|
format ||= '%<symbol>s%<amount>f'
|
|
93
93
|
|
|
94
|
-
# Automatically adjust decimal places based on currency subunit
|
|
95
|
-
adjusted_format = format
|
|
96
|
-
|
|
94
|
+
# Automatically adjust decimal places based on currency subunit if missing
|
|
95
|
+
adjusted_format = format
|
|
96
|
+
.gsub(/%<amount>(\s*\+?\d*)f/, "%<amount>\\1.#{currency.subunit}f")
|
|
97
97
|
|
|
98
98
|
Kernel.format(adjusted_format,
|
|
99
99
|
amount: value,
|
data/lib/minting/money/money.rb
CHANGED
|
@@ -11,9 +11,19 @@ module Mint
|
|
|
11
11
|
|
|
12
12
|
# Returns the ISO 3-letter currency code string.
|
|
13
13
|
#
|
|
14
|
-
# @return [String] the ISO currency code
|
|
14
|
+
# @return [String] the ISO currency code (e.g., "USD", "EUR", "BRL")
|
|
15
|
+
# @example
|
|
16
|
+
# Mint.money(100, 'USD').currency_code #=> "USD"
|
|
15
17
|
def currency_code = currency.code
|
|
16
18
|
|
|
19
|
+
# Returns the monetary amount expressed in the currency's smallest unit (fractional units).
|
|
20
|
+
# For example, cents for USD (subunit 2), yen for JPY (subunit 0), fils for IQD (subunit 3).
|
|
21
|
+
#
|
|
22
|
+
# @return [Integer] the amount in fractional units
|
|
23
|
+
# @example
|
|
24
|
+
# Mint.money(1234.56, 'USD').fractional #=> 123456
|
|
25
|
+
# Mint.money(1000, 'JPY').fractional #=> 1000
|
|
26
|
+
# Mint.money(123.456, 'IQD').fractional #=> 123456
|
|
17
27
|
def fractional = (amount * currency.fractional_multiplier).to_i
|
|
18
28
|
|
|
19
29
|
# Generates a stable hash key for Money instances.
|
data/lib/minting/version.rb
CHANGED
data/lib/minting.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: minting
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.6.
|
|
4
|
+
version: 1.6.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gilson Ferraz
|
|
@@ -32,6 +32,7 @@ files:
|
|
|
32
32
|
- LICENSE
|
|
33
33
|
- README.md
|
|
34
34
|
- Rakefile
|
|
35
|
+
- bin/check-currencies
|
|
35
36
|
- bin/console
|
|
36
37
|
- bin/setup
|
|
37
38
|
- doc/agents/AGENTS.md
|
|
@@ -40,15 +41,14 @@ files:
|
|
|
40
41
|
- doc/agents/recommendations.md
|
|
41
42
|
- doc/agents/rubocop-issues.md
|
|
42
43
|
- lib/minting.rb
|
|
43
|
-
- lib/minting/data/currencies.yaml
|
|
44
|
+
- lib/minting/data/world-currencies.yaml
|
|
44
45
|
- lib/minting/mint.rb
|
|
45
|
-
- lib/minting/mint/currency.rb
|
|
46
|
-
- lib/minting/mint/
|
|
46
|
+
- lib/minting/mint/currency/currency.rb
|
|
47
|
+
- lib/minting/mint/currency/currency_registry.rb
|
|
48
|
+
- lib/minting/mint/currency/world_currencies.rb
|
|
47
49
|
- lib/minting/mint/mint.rb
|
|
48
50
|
- lib/minting/mint/parser.rb
|
|
49
51
|
- lib/minting/mint/refinements.rb
|
|
50
|
-
- lib/minting/mint/registry.rb
|
|
51
|
-
- lib/minting/mint/world_currencies.rb
|
|
52
52
|
- lib/minting/money/allocation.rb
|
|
53
53
|
- lib/minting/money/arithmetics.rb
|
|
54
54
|
- lib/minting/money/coercion.rb
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Mint
|
|
4
|
-
# Represents a specific currency unit, identified by ISO 4217 alphabetic code
|
|
5
|
-
#
|
|
6
|
-
# @see https://www.iso.org/iso-4217-currency-codes.html
|
|
7
|
-
Currency = Data.define(:code, :subunit, :symbol, :priority, :country, :name,
|
|
8
|
-
:fractional_multiplier, :minimum_amount) do
|
|
9
|
-
def initialize(code:, symbol:, subunit: 0, priority: 0, country: nil, name: nil)
|
|
10
|
-
subunit = subunit.to_i
|
|
11
|
-
priority = priority.to_i
|
|
12
|
-
fractional_multiplier = 10**subunit
|
|
13
|
-
minimum_amount = Rational(1, fractional_multiplier)
|
|
14
|
-
super(code:, subunit:, symbol:, priority:, country:, name:,
|
|
15
|
-
fractional_multiplier:, minimum_amount:)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def inspect = "<Currency:(#{code} #{symbol} #{subunit} #{name})>"
|
|
19
|
-
|
|
20
|
-
# Normalizes numeric amounts for this currency
|
|
21
|
-
# 1. Converts to Rational
|
|
22
|
-
# 2. Rounds to respect currency subunit
|
|
23
|
-
def normalize_amount(amount) = amount.to_r.round(subunit)
|
|
24
|
-
end
|
|
25
|
-
end
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Mint currency registration and factory (public API)
|
|
4
|
-
module Mint
|
|
5
|
-
# Registers a new currency, raising a KeyError if already registered.
|
|
6
|
-
#
|
|
7
|
-
# @param code [String] the unique currency code
|
|
8
|
-
# @param subunit [Integer] the decimal subunit precision, defaults to 0
|
|
9
|
-
# @param symbol [String] the display symbol
|
|
10
|
-
# @param priority [Integer] parser precedence priority
|
|
11
|
-
# @return [Currency] the newly registered Currency instance
|
|
12
|
-
# @raise [ArgumentError] if the code contains invalid characters
|
|
13
|
-
# @raise [KeyError] if the currency code is already registered
|
|
14
|
-
def self.register_currency(code:, subunit: 0, symbol: '', priority: 0)
|
|
15
|
-
raise ArgumentError, 'Currency code must be String' unless code.is_a? String
|
|
16
|
-
unless code.match?(/^[A-Z_]+$/)
|
|
17
|
-
raise ArgumentError,
|
|
18
|
-
"Currency code must have only letters or '_' ('USD',, 'MY_COIN')"
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
currencies = Registry.currencies
|
|
22
|
-
raise KeyError, "Currency: #{code} already registered" if currencies[code]
|
|
23
|
-
|
|
24
|
-
currency = currencies[code] = Currency.new(code:, subunit:, symbol:, priority:)
|
|
25
|
-
Registry.invalidate_symbols_cache
|
|
26
|
-
currency
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Registered symbols sorted for detection: longest match wins, then parser priority.
|
|
30
|
-
# Internal API - used by Money parser.
|
|
31
|
-
#
|
|
32
|
-
# @return [Array<Array<String, Currency>>] sorted symbol-to-currency mappings
|
|
33
|
-
# @api private
|
|
34
|
-
def self.currency_symbols
|
|
35
|
-
Registry.currency_symbols
|
|
36
|
-
end
|
|
37
|
-
end
|