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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 293a64ebe3fd00ed8f6d062b596234550f85ead517b7f8094648dd9502be2f86
4
- data.tar.gz: 3a98bb94ce601340b9cced8b275ee0be3e578d7e3cf781245cdd0fe5ee8038f1
3
+ metadata.gz: 0af1a3786bbef7fd2c9d3614ccd5076e0ad6f77fb35bd0d34e636355bafd4714
4
+ data.tar.gz: 520dfe15e4a3ddcd180a898769266d27502af54e3ad70503ed734ad351564639
5
5
  SHA512:
6
- metadata.gz: fe587b6b5180c831b1e32092dfe9857ae37516f7fd274215be27e21af1263e99a93725f019e3be96962c5f20c13519326fc2b18b81b88e22aab43dc0d93a9b49
7
- data.tar.gz: b0b8ca0b0dd832668e7b37dfbcca6e3606c673496c8149de90e9d2ffd21a166205e37ae9b455e36b1a71b07729a1d8c5bac074022991d45906ec7c410ec73740
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](#performance) section for full benchmarks.
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: `BENCH=true rake bench:performance`
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/*_benchmark.rb'
17
+ t.pattern = 'test/performance/**/*_benchmark.rb'
18
18
  end
19
19
 
20
- Rake::TestTask.new('bench:parse') do |t|
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:edge') do |t|
25
+ Rake::TestTask.new('bench:memory') do |t|
27
26
  t.libs = %w[lib test]
28
- t.pattern = 'test/performance/algorithm_benchmark.rb'
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/regression_benchmark.rb'
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/competitive_performance_benchmark.rb'
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 (use BENCH=true)
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 (set BENCH=true as in README):
25
- - BENCH=true rake bench:performance
26
- - BENCH=true rake bench:competitive
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 BENCH=true to enable benchmark-heavy tasks; benchmarks use Minitest::Benchmark patterns.
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
@@ -87,7 +87,7 @@ Qwen run on 2026-05-30 and aren't reproducible from CI.
87
87
  **Concrete action:** in CI, run
88
88
 
89
89
  ```sh
90
- BENCH=true rake bench:regression
90
+ ake bench:regression
91
91
  ```
92
92
 
93
93
  and fail the build if any benchmark regresses by more than (e.g.)
@@ -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 Registry
10
- module_function
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('../data', __dir__), 'currencies.yaml')
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
@@ -3,7 +3,7 @@
3
3
  # Mint currency registration and factory (public API)
4
4
  module Mint
5
5
  # Unknown currency excpetion
6
- class UnknownCurreny < StandardError
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 Registry.currencies[currency]
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
@@ -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
- Mint.currency_symbols.each do |symbol, currency|
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/currency_store'
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
- # @param new_amount [Numeric] The new amount
49
- # @return [Money] A new Money object or self
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.gsub(/%<amount>(\+?\d*)f/,
96
- "%<amount>\\1.#{currency.subunit}f")
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,
@@ -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.
@@ -3,5 +3,5 @@
3
3
  # Root namespace for the Minting library.
4
4
  module Minting
5
5
  # Current version of the Minting gem.
6
- VERSION = '1.6.0'
6
+ VERSION = '1.6.2'
7
7
  end
data/lib/minting.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'minting/mint'
4
- require 'minting/money'
5
4
  require 'minting/version'
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.0
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/currency_store.rb
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