minting 1.7.2 → 1.7.3

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +16 -1
  3. data/doc/Mint/Currency.html +285 -31
  4. data/doc/Mint/Money.html +79 -56
  5. data/doc/Mint/RangeStepPatch.html +1 -1
  6. data/doc/Mint/Registry.html +842 -0
  7. data/doc/Mint/UnknownCurrency.html +1 -1
  8. data/doc/Mint.html +345 -51
  9. data/doc/Minting.html +2 -2
  10. data/doc/_index.html +8 -8
  11. data/doc/agents/api_review-2026-06-15.md +342 -0
  12. data/doc/class_list.html +1 -1
  13. data/doc/file.README.html +13 -2
  14. data/doc/index.html +13 -2
  15. data/doc/method_list.html +100 -36
  16. data/doc/top-level-namespace.html +1 -1
  17. data/lib/minting/currency/currency.rb +31 -0
  18. data/lib/minting/mint/locale_backend.rb +29 -0
  19. data/lib/minting/mint/mint.rb +23 -21
  20. data/lib/minting/mint/parser/parser.rb +3 -7
  21. data/lib/minting/mint/registry/registration.rb +33 -0
  22. data/lib/minting/mint/registry/registry.rb +38 -0
  23. data/lib/minting/mint/registry/symbols.rb +49 -0
  24. data/lib/minting/mint/registry/zeros.rb +18 -0
  25. data/lib/minting/mint.rb +13 -25
  26. data/lib/minting/money/constructors.rb +6 -11
  27. data/lib/minting/money/format/formatting.rb +16 -0
  28. data/lib/minting/money/format/to_s.rb +13 -4
  29. data/lib/minting/money/money.rb +12 -0
  30. data/lib/minting/version.rb +1 -1
  31. metadata +13 -8
  32. data/lib/minting/currency/currency_registry.rb +0 -67
  33. data/lib/minting/currency/world_currencies.rb +0 -16
  34. /data/doc/agents/{AGENTS.md → expired/AGENTS.md} +0 -0
  35. /data/doc/agents/{copilot-instructions.md → expired/copilot-instructions.md} +0 -0
  36. /data/doc/agents/{gemini_gem_evaluation.md → expired/gemini_gem_evaluation.md} +0 -0
  37. /data/doc/agents/{recommendations.md → expired/recommendations.md} +0 -0
  38. /data/doc/agents/{rubocop-issues.md → expired/rubocop-issues.md} +0 -0
data/doc/method_list.html CHANGED
@@ -177,23 +177,23 @@
177
177
 
178
178
  <li class="odd ">
179
179
  <div class="item">
180
- <span class='object_link'><a href="Mint/CurrencyRegistry.html#currencies-instance_method" title="Mint::CurrencyRegistry#currencies (method)">#currencies</a></span>
181
- <small>Mint::CurrencyRegistry</small>
180
+ <span class='object_link'><a href="Mint/Registry.html#currencies-class_method" title="Mint::Registry.currencies (method)">currencies</a></span>
181
+ <small>Mint::Registry</small>
182
182
  </div>
183
183
  </li>
184
184
 
185
185
 
186
186
  <li class="even ">
187
187
  <div class="item">
188
- <span class='object_link'><a href="Mint.html#currency-class_method" title="Mint.currency (method)">currency</a></span>
189
- <small>Mint</small>
188
+ <span class='object_link'><a href="Mint/Money.html#currency-instance_method" title="Mint::Money#currency (method)">#currency</a></span>
189
+ <small>Mint::Money</small>
190
190
  </div>
191
191
  </li>
192
192
 
193
193
 
194
194
  <li class="odd ">
195
195
  <div class="item">
196
- <span class='object_link'><a href="Mint/Money.html#currency-instance_method" title="Mint::Money#currency (method)">#currency</a></span>
196
+ <span class='object_link'><a href="Mint/Money.html#currency_code-instance_method" title="Mint::Money#currency_code (method)">#currency_code</a></span>
197
197
  <small>Mint::Money</small>
198
198
  </div>
199
199
  </li>
@@ -201,16 +201,32 @@
201
201
 
202
202
  <li class="even ">
203
203
  <div class="item">
204
- <span class='object_link'><a href="Mint/Money.html#currency_code-instance_method" title="Mint::Money#currency_code (method)">#currency_code</a></span>
205
- <small>Mint::Money</small>
204
+ <span class='object_link'><a href="Mint.html#currency_for_code-class_method" title="Mint.currency_for_code (method)">currency_for_code</a></span>
205
+ <small>Mint</small>
206
+ </div>
207
+ </li>
208
+
209
+
210
+ <li class="odd ">
211
+ <div class="item">
212
+ <span class='object_link'><a href="Mint/Registry.html#currency_for_symbol-instance_method" title="Mint::Registry#currency_for_symbol (method)">#currency_for_symbol</a></span>
213
+ <small>Mint::Registry</small>
214
+ </div>
215
+ </li>
216
+
217
+
218
+ <li class="even ">
219
+ <div class="item">
220
+ <span class='object_link'><a href="Mint.html#currency_for_symbol-class_method" title="Mint.currency_for_symbol (method)">currency_for_symbol</a></span>
221
+ <small>Mint</small>
206
222
  </div>
207
223
  </li>
208
224
 
209
225
 
210
226
  <li class="odd ">
211
227
  <div class="item">
212
- <span class='object_link'><a href="Mint/CurrencyRegistry.html#currency_symbols-instance_method" title="Mint::CurrencyRegistry#currency_symbols (method)">#currency_symbols</a></span>
213
- <small>Mint::CurrencyRegistry</small>
228
+ <span class='object_link'><a href="Mint/Registry.html#detect_currency-instance_method" title="Mint::Registry#detect_currency (method)">#detect_currency</a></span>
229
+ <small>Mint::Registry</small>
214
230
  </div>
215
231
  </li>
216
232
 
@@ -280,6 +296,14 @@
280
296
 
281
297
 
282
298
  <li class="even ">
299
+ <div class="item">
300
+ <span class='object_link'><a href="Mint.html#locale_backend-class_method" title="Mint.locale_backend (method)">locale_backend</a></span>
301
+ <small>Mint</small>
302
+ </div>
303
+ </li>
304
+
305
+
306
+ <li class="odd ">
283
307
  <div class="item">
284
308
  <span class='object_link'><a href="Mint/Currency.html#minimum_amount-instance_method" title="Mint::Currency#minimum_amount (method)">#minimum_amount</a></span>
285
309
  <small>Mint::Currency</small>
@@ -287,7 +311,7 @@
287
311
  </li>
288
312
 
289
313
 
290
- <li class="odd ">
314
+ <li class="even ">
291
315
  <div class="item">
292
316
  <span class='object_link'><a href="Mint/Money.html#mint-instance_method" title="Mint::Money#mint (method)">#mint</a></span>
293
317
  <small>Mint::Money</small>
@@ -295,7 +319,7 @@
295
319
  </li>
296
320
 
297
321
 
298
- <li class="even ">
322
+ <li class="odd ">
299
323
  <div class="item">
300
324
  <span class='object_link'><a href="Mint.html#money-class_method" title="Mint.money (method)">money</a></span>
301
325
  <small>Mint</small>
@@ -303,7 +327,7 @@
303
327
  </li>
304
328
 
305
329
 
306
- <li class="odd ">
330
+ <li class="even ">
307
331
  <div class="item">
308
332
  <span class='object_link'><a href="Mint/Currency.html#name-instance_method" title="Mint::Currency#name (method)">#name</a></span>
309
333
  <small>Mint::Currency</small>
@@ -311,7 +335,7 @@
311
335
  </li>
312
336
 
313
337
 
314
- <li class="even ">
338
+ <li class="odd ">
315
339
  <div class="item">
316
340
  <span class='object_link'><a href="Mint/Money.html#negative%3F-instance_method" title="Mint::Money#negative? (method)">#negative?</a></span>
317
341
  <small>Mint::Money</small>
@@ -319,7 +343,7 @@
319
343
  </li>
320
344
 
321
345
 
322
- <li class="odd ">
346
+ <li class="even ">
323
347
  <div class="item">
324
348
  <span class='object_link'><a href="Mint/Money.html#nonzero%3F-instance_method" title="Mint::Money#nonzero? (method)">#nonzero?</a></span>
325
349
  <small>Mint::Money</small>
@@ -327,7 +351,7 @@
327
351
  </li>
328
352
 
329
353
 
330
- <li class="even ">
354
+ <li class="odd ">
331
355
  <div class="item">
332
356
  <span class='object_link'><a href="Mint/Currency.html#normalize_amount-instance_method" title="Mint::Currency#normalize_amount (method)">#normalize_amount</a></span>
333
357
  <small>Mint::Currency</small>
@@ -335,7 +359,7 @@
335
359
  </li>
336
360
 
337
361
 
338
- <li class="odd ">
362
+ <li class="even ">
339
363
  <div class="item">
340
364
  <span class='object_link'><a href="Mint.html#parse-instance_method" title="Mint#parse (method)">#parse</a></span>
341
365
  <small>Mint</small>
@@ -343,7 +367,7 @@
343
367
  </li>
344
368
 
345
369
 
346
- <li class="even ">
370
+ <li class="odd ">
347
371
  <div class="item">
348
372
  <span class='object_link'><a href="Mint/Money.html#positive%3F-instance_method" title="Mint::Money#positive? (method)">#positive?</a></span>
349
373
  <small>Mint::Money</small>
@@ -351,7 +375,7 @@
351
375
  </li>
352
376
 
353
377
 
354
- <li class="odd ">
378
+ <li class="even ">
355
379
  <div class="item">
356
380
  <span class='object_link'><a href="Mint/Currency.html#priority-instance_method" title="Mint::Currency#priority (method)">#priority</a></span>
357
381
  <small>Mint::Currency</small>
@@ -359,15 +383,15 @@
359
383
  </li>
360
384
 
361
385
 
362
- <li class="even ">
386
+ <li class="odd ">
363
387
  <div class="item">
364
- <span class='object_link'><a href="Mint/CurrencyRegistry.html#register-instance_method" title="Mint::CurrencyRegistry#register (method)">#register</a></span>
365
- <small>Mint::CurrencyRegistry</small>
388
+ <span class='object_link'><a href="Mint/Registry.html#register-class_method" title="Mint::Registry.register (method)">register</a></span>
389
+ <small>Mint::Registry</small>
366
390
  </div>
367
391
  </li>
368
392
 
369
393
 
370
- <li class="odd ">
394
+ <li class="even ">
371
395
  <div class="item">
372
396
  <span class='object_link'><a href="Mint.html#register_currency-class_method" title="Mint.register_currency (method)">register_currency</a></span>
373
397
  <small>Mint</small>
@@ -375,7 +399,23 @@
375
399
  </li>
376
400
 
377
401
 
402
+ <li class="odd ">
403
+ <div class="item">
404
+ <span class='object_link'><a href="Mint/Currency.html#resolve-class_method" title="Mint::Currency.resolve (method)">resolve</a></span>
405
+ <small>Mint::Currency</small>
406
+ </div>
407
+ </li>
408
+
409
+
378
410
  <li class="even ">
411
+ <div class="item">
412
+ <span class='object_link'><a href="Mint/Currency.html#resolve!-class_method" title="Mint::Currency.resolve! (method)">resolve!</a></span>
413
+ <small>Mint::Currency</small>
414
+ </div>
415
+ </li>
416
+
417
+
418
+ <li class="odd ">
379
419
  <div class="item">
380
420
  <span class='object_link'><a href="Mint/Money.html#same_currency%3F-instance_method" title="Mint::Money#same_currency? (method)">#same_currency?</a></span>
381
421
  <small>Mint::Money</small>
@@ -383,7 +423,7 @@
383
423
  </li>
384
424
 
385
425
 
386
- <li class="odd ">
426
+ <li class="even ">
387
427
  <div class="item">
388
428
  <span class='object_link'><a href="Mint/Money.html#split-instance_method" title="Mint::Money#split (method)">#split</a></span>
389
429
  <small>Mint::Money</small>
@@ -391,7 +431,7 @@
391
431
  </li>
392
432
 
393
433
 
394
- <li class="even ">
434
+ <li class="odd ">
395
435
  <div class="item">
396
436
  <span class='object_link'><a href="Mint/RangeStepPatch.html#step-instance_method" title="Mint::RangeStepPatch#step (method)">#step</a></span>
397
437
  <small>Mint::RangeStepPatch</small>
@@ -399,7 +439,7 @@
399
439
  </li>
400
440
 
401
441
 
402
- <li class="odd ">
442
+ <li class="even ">
403
443
  <div class="item">
404
444
  <span class='object_link'><a href="Mint/Currency.html#subunit-instance_method" title="Mint::Currency#subunit (method)">#subunit</a></span>
405
445
  <small>Mint::Currency</small>
@@ -407,7 +447,7 @@
407
447
  </li>
408
448
 
409
449
 
410
- <li class="even ">
450
+ <li class="odd ">
411
451
  <div class="item">
412
452
  <span class='object_link'><a href="Mint/Money.html#succ-instance_method" title="Mint::Money#succ (method)">#succ</a></span>
413
453
  <small>Mint::Money</small>
@@ -415,7 +455,7 @@
415
455
  </li>
416
456
 
417
457
 
418
- <li class="odd ">
458
+ <li class="even ">
419
459
  <div class="item">
420
460
  <span class='object_link'><a href="Mint/Currency.html#symbol-instance_method" title="Mint::Currency#symbol (method)">#symbol</a></span>
421
461
  <small>Mint::Currency</small>
@@ -423,7 +463,7 @@
423
463
  </li>
424
464
 
425
465
 
426
- <li class="even ">
466
+ <li class="odd ">
427
467
  <div class="item">
428
468
  <span class='object_link'><a href="Mint/Money.html#to_d-instance_method" title="Mint::Money#to_d (method)">#to_d</a></span>
429
469
  <small>Mint::Money</small>
@@ -431,7 +471,7 @@
431
471
  </li>
432
472
 
433
473
 
434
- <li class="odd ">
474
+ <li class="even ">
435
475
  <div class="item">
436
476
  <span class='object_link'><a href="Mint/Money.html#to_f-instance_method" title="Mint::Money#to_f (method)">#to_f</a></span>
437
477
  <small>Mint::Money</small>
@@ -439,7 +479,7 @@
439
479
  </li>
440
480
 
441
481
 
442
- <li class="even ">
482
+ <li class="odd ">
443
483
  <div class="item">
444
484
  <span class='object_link'><a href="Mint/Money.html#to_hash-instance_method" title="Mint::Money#to_hash (method)">#to_hash</a></span>
445
485
  <small>Mint::Money</small>
@@ -447,7 +487,7 @@
447
487
  </li>
448
488
 
449
489
 
450
- <li class="odd ">
490
+ <li class="even ">
451
491
  <div class="item">
452
492
  <span class='object_link'><a href="Mint/Money.html#to_html-instance_method" title="Mint::Money#to_html (method)">#to_html</a></span>
453
493
  <small>Mint::Money</small>
@@ -455,7 +495,7 @@
455
495
  </li>
456
496
 
457
497
 
458
- <li class="even ">
498
+ <li class="odd ">
459
499
  <div class="item">
460
500
  <span class='object_link'><a href="Mint/Money.html#to_i-instance_method" title="Mint::Money#to_i (method)">#to_i</a></span>
461
501
  <small>Mint::Money</small>
@@ -463,7 +503,7 @@
463
503
  </li>
464
504
 
465
505
 
466
- <li class="odd ">
506
+ <li class="even ">
467
507
  <div class="item">
468
508
  <span class='object_link'><a href="Mint/Money.html#to_json-instance_method" title="Mint::Money#to_json (method)">#to_json</a></span>
469
509
  <small>Mint::Money</small>
@@ -471,7 +511,7 @@
471
511
  </li>
472
512
 
473
513
 
474
- <li class="even ">
514
+ <li class="odd ">
475
515
  <div class="item">
476
516
  <span class='object_link'><a href="Mint/Money.html#to_r-instance_method" title="Mint::Money#to_r (method)">#to_r</a></span>
477
517
  <small>Mint::Money</small>
@@ -479,7 +519,7 @@
479
519
  </li>
480
520
 
481
521
 
482
- <li class="odd ">
522
+ <li class="even ">
483
523
  <div class="item">
484
524
  <span class='object_link'><a href="Mint/Money.html#to_s-instance_method" title="Mint::Money#to_s (method)">#to_s</a></span>
485
525
  <small>Mint::Money</small>
@@ -487,7 +527,7 @@
487
527
  </li>
488
528
 
489
529
 
490
- <li class="even ">
530
+ <li class="odd ">
491
531
  <div class="item">
492
532
  <span class='object_link'><a href="Mint.html#use_top_level_constants!-class_method" title="Mint.use_top_level_constants! (method)">use_top_level_constants!</a></span>
493
533
  <small>Mint</small>
@@ -495,6 +535,14 @@
495
535
  </li>
496
536
 
497
537
 
538
+ <li class="even ">
539
+ <div class="item">
540
+ <span class='object_link'><a href="Mint/Registry.html#world_currencies-class_method" title="Mint::Registry.world_currencies (method)">world_currencies</a></span>
541
+ <small>Mint::Registry</small>
542
+ </div>
543
+ </li>
544
+
545
+
498
546
  <li class="odd ">
499
547
  <div class="item">
500
548
  <span class='object_link'><a href="Mint.html#world_currencies-class_method" title="Mint.world_currencies (method)">world_currencies</a></span>
@@ -504,6 +552,14 @@
504
552
 
505
553
 
506
554
  <li class="even ">
555
+ <div class="item">
556
+ <span class='object_link'><a href="Mint.html#zero-class_method" title="Mint.zero (method)">zero</a></span>
557
+ <small>Mint</small>
558
+ </div>
559
+ </li>
560
+
561
+
562
+ <li class="odd ">
507
563
  <div class="item">
508
564
  <span class='object_link'><a href="Mint/Money.html#zero%3F-instance_method" title="Mint::Money#zero? (method)">#zero?</a></span>
509
565
  <small>Mint::Money</small>
@@ -511,6 +567,14 @@
511
567
  </li>
512
568
 
513
569
 
570
+ <li class="even ">
571
+ <div class="item">
572
+ <span class='object_link'><a href="Mint/Registry.html#zero_for-class_method" title="Mint::Registry.zero_for (method)">zero_for</a></span>
573
+ <small>Mint::Registry</small>
574
+ </div>
575
+ </li>
576
+
577
+
514
578
 
515
579
  </ul>
516
580
  </div>
@@ -141,7 +141,7 @@
141
141
  </div>
142
142
 
143
143
  <div id="footer">
144
- Generated on Sun Jun 14 21:57:00 2026 by
144
+ Generated on Mon Jun 15 19:57:57 2026 by
145
145
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
146
146
  0.9.44 (ruby-4.0.5).
147
147
  </div>
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :nodoc:
3
4
  module Mint
4
5
  # Represents a specific currency unit, identified by ISO 4217 alphabetic code.
5
6
  # Currency objects are immutable and define the properties of a monetary unit
@@ -41,4 +42,34 @@ module Mint
41
42
  # 2. Rounds to respect currency subunit
42
43
  def normalize_amount(amount) = amount.to_r.round(subunit)
43
44
  end
45
+
46
+ # Resolves an object into a {Currency}, returning +nil+ when it can't.
47
+ #
48
+ # Accepts +nil+, +String+, {Currency}, or {Money}.
49
+ # Passing a {Money} extracts its currency
50
+ #
51
+ # @param object [String, Currency, Money, nil] a currency code, object, or +nil+
52
+ # @return [Currency, nil] the resolved Currency, or +nil+ if +object+ is +nil+
53
+ # or the code is not registered
54
+ # @raise [ArgumentError] if +object+ is an unsupported type (e.g. +Integer+)
55
+ def Currency.resolve(object)
56
+ case object
57
+ when NilClass then nil
58
+ when Currency then object
59
+ when Money then object.currency
60
+ when String then Mint.currency_for_code object
61
+ else raise ArgumentError, "currency must be [Currency], [Money], [String] or nil (#{object})"
62
+ end
63
+ end
64
+
65
+ # Resolves an object into a {Currency}, raising on failure.
66
+ #
67
+ # Like {.resolve} but raises when the result would be +nil+.
68
+ #
69
+ # @param object [String, Currency, Money, nil] a currency code, object, or +nil+
70
+ # @return [Currency] the resolved Currency
71
+ # @raise [ArgumentError] if +object+ cannot be resolved into a registered currency
72
+ def Currency.resolve!(object)
73
+ resolve(object) or raise ArgumentError, "Could not resolve (#{object}) into a currency"
74
+ end
44
75
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ module Mint
5
+ class << self
6
+ # Optional callable that returns a Hash with locale-aware formatting defaults.
7
+ #
8
+ # The callable receives no arguments and returns a Hash with these keys:
9
+ # [+:decimal+] Decimal separator (e.g. +","+)
10
+ # [+:thousand+] Thousands delimiter (e.g. +"."+)
11
+ # [+:format+] Format template string (e.g. +"%<amount>f %<symbol>s"+)
12
+ #
13
+ # When set, +#to_s+ and +#format+ use these values as fallbacks when the
14
+ # corresponding parameter is not explicitly provided.
15
+ #
16
+ # @example Rails I18n integration (in minting-rails railtie)
17
+ # Mint.locale_backend = -> {
18
+ # fmt = I18n.t('number.currency.format')
19
+ # {
20
+ # decimal: fmt[:separator],
21
+ # thousand: fmt[:delimiter],
22
+ # format: fmt[:format] == '%n %u' ? '%<amount>f %<symbol>s' : '%<symbol>s%<amount>f'
23
+ # }
24
+ # }
25
+ #
26
+ # @return [Proc, #call, nil]
27
+ attr_accessor :locale_backend
28
+ end
29
+ end
@@ -14,18 +14,26 @@ module Mint
14
14
  # @raise [ArgumentError] if the currency code is not registered
15
15
  def self.money(amount, currency_code) = Money.create(amount, currency_code)
16
16
 
17
- # Finds a registered currency by its code, symbol,
18
- # or retrieves it directly if already a Currency object.
17
+ # @return [Hash{String => Currency}] the frozen world-currencies hash
18
+ # @api private
19
+ def self.world_currencies = Registry.world_currencies
20
+
21
+ # Looks up a registered currency by its alpha code.
22
+ #
23
+ # Unlike {.currency}, this performs a direct hash lookup and only accepts strings.
19
24
  #
20
- # @param currency [String, Currency] the currency identifier or object
21
- # @return [Currency, nil] the registered Currency instance or nil if not found
22
- def self.currency(currency)
23
- case currency
24
- when NilClass then nil
25
- when Currency then currency
26
- when String then CurrencyRegistry.currencies[currency]
27
- else raise ArgumentError, "currency must be [Currency], [String] or nil (#{currency})"
28
- end
25
+ # @param code [String] the currency code
26
+ # @return [Currency, nil] the registered Currency, or +nil+ if not found
27
+ def self.currency_for_code(code)
28
+ Registry.currencies[code]
29
+ end
30
+
31
+ # Looks up a currency by its display symbol.
32
+ #
33
+ # @param symbol [String] the display symbol (e.g. "$", "R$")
34
+ # @return [Currency, nil] the highest-priority currency for the symbol
35
+ def self.currency_for_symbol(symbol)
36
+ Registry.currency_for_symbol(symbol)
29
37
  end
30
38
 
31
39
  # Returns a zero {Money} in the given currency, useful as a default value
@@ -33,16 +41,10 @@ module Mint
33
41
  #
34
42
  # @param currency [String, Currency] a currency code or object
35
43
  # @return [Money] a frozen zero-Money
36
- # @raise [ArgumentError] if the currency is not registered
37
- def self.zero(currency)
38
- checked = Mint.currency(currency)
39
- raise ArgumentError, "Invalid Currency: [#{currency}]" unless checked
40
-
41
- @zeros ||= CurrencyRegistry.currencies.values.to_h { |currency| [currency, Mint::Money.send(:new, 0, currency)] }
42
- @zeros[currency] ||= Money.send(:new, 0, currency)
43
- @zeros[currency]
44
- end
44
+ # @raise [ArgumentError] if the currency can't be resolved
45
+ def self.zero(currency) = Registry.zero_for(Currency.resolve!(currency))
45
46
 
47
+ # Registers a new currency, raising a KeyError if already registered.
46
48
  #
47
49
  # @param code [String] the unique currency code
48
50
  # @param subunit [Integer] the decimal subunit precision, defaults to 0
@@ -52,6 +54,6 @@ module Mint
52
54
  # @raise [ArgumentError] if the code contains invalid characters
53
55
  # @raise [KeyError] if the currency code is already registered
54
56
  def self.register_currency(code:, subunit: 0, symbol: '', priority: 0)
55
- CurrencyRegistry.register(code:, subunit:, symbol:, priority:)
57
+ Registry.register(code:, subunit:, symbol:, priority:)
56
58
  end
57
59
  end
@@ -25,7 +25,7 @@ module Mint
25
25
  input = input.strip
26
26
  raise ArgumentError, 'input cannot be empty' if input.empty?
27
27
 
28
- currency = Mint.currency(currency) || parse_currency(input)
28
+ currency = Currency.resolve(currency) || parse_currency(input)
29
29
  raise ArgumentError, "Currency [#{currency}] not registered" unless currency
30
30
 
31
31
  amount = currency.normalize_amount(parse_amount(input))
@@ -53,14 +53,10 @@ module Mint
53
53
  # @private
54
54
  def parse_currency(input)
55
55
  input.scan(/\b([A-Z_]+)\b/) do |(code)|
56
- currency = Mint.currency(code)
56
+ currency = Mint.currency_for_code(code)
57
57
  return currency if currency
58
58
  end
59
59
 
60
- CurrencyRegistry.currency_symbols.each do |symbol, currency|
61
- return currency if input.include?(symbol)
62
- end
63
-
64
- raise ArgumentError, 'Currency could not be detected'
60
+ Registry.detect_currency(input) or raise ArgumentError, 'Currency could not be detected'
65
61
  end
66
62
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mint
4
+ # :nodoc:
5
+ module Registry
6
+ # Registers a new currency, raising a KeyError if already registered.
7
+ #
8
+ # @param code [String] the unique currency code
9
+ # @param subunit [Integer] the decimal subunit precision, defaults to 0
10
+ # @param symbol [String] the display symbol
11
+ # @param priority [Integer] parser precedence priority
12
+ # @return [Currency] the newly registered Currency instance
13
+ # @raise [ArgumentError] if the code contains invalid characters
14
+ # @raise [KeyError] if the currency code is already registered
15
+ def self.register(code:, subunit: 0, symbol: '', priority: 0)
16
+ raise ArgumentError, 'Currency code must be String' unless code.is_a? String
17
+ unless code.match?(/^[A-Z_]+$/)
18
+ raise ArgumentError,
19
+ "Currency code must have only letters or '_' ('USD',, 'MY_COIN')"
20
+ end
21
+
22
+ MUTEX.synchronize do
23
+ raise KeyError, "Currency: #{code} already registered" if currencies[code]
24
+
25
+ currency = Currency.new(code:, subunit:, symbol:, priority:)
26
+ @currencies = @currencies.merge(code => currency).freeze
27
+ @currency_symbols = nil
28
+ @currency_symbol_map = nil
29
+ currency
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require_relative 'symbols'
5
+ require_relative 'registration'
6
+ require_relative 'zeros'
7
+
8
+ # Mint registry: manages all cached state
9
+ module Mint
10
+ # Internal registry for currencies, symbols, and zero-money cache.
11
+ # All mutable shared state lives here.
12
+ module Registry
13
+ MUTEX = Monitor.new
14
+
15
+ private_constant :MUTEX
16
+
17
+ # Loads ISO world currencies from YAML file.
18
+ #
19
+ # @return [Hash{String => Currency}] ISO-4217 world currencies mapped by code
20
+ # @api private
21
+ def self.world_currencies
22
+ @world_currencies || MUTEX.synchronize do
23
+ @world_currencies = begin
24
+ path = File.join(File.expand_path('../../data', __dir__), 'world-currencies.yaml')
25
+ YAML.load_file(path).to_h { |entry| [entry['code'], Currency.new(**entry.transform_keys(&:to_sym))] }
26
+ end.freeze
27
+ end
28
+ end
29
+
30
+ # Returns the frozen hash of all registered currencies (world + custom).
31
+ #
32
+ # @return [Hash{String => Currency}] registered currencies mapped by code
33
+ # @api private
34
+ def self.currencies
35
+ @currencies || MUTEX.synchronize { @currencies = world_currencies.dup.freeze }
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mint
4
+ # :nodoc:
5
+ module Registry
6
+ extend self
7
+
8
+ # Looks up a currency by its display symbol.
9
+ #
10
+ # @param symbol [String] the display symbol (e.g. "$", "R$")
11
+ # @return [Currency, nil] the highest-priority currency for the symbol
12
+ # @api private
13
+ def currency_for_symbol(symbol)
14
+ @currency_symbol_map || MUTEX.synchronize { @currency_symbol_map = currency_symbols.to_h.freeze }
15
+ @currency_symbol_map[symbol]
16
+ end
17
+
18
+ # Scans +input+ for registered currency symbols and returns the first match.
19
+ #
20
+ # @param input [String] the string to scan
21
+ # @return [Currency, nil]
22
+ # @api private
23
+ def detect_currency(input)
24
+ currency_symbols.each do |symbol, currency|
25
+ return currency if input.include?(symbol)
26
+ end
27
+ nil
28
+ end
29
+
30
+ private
31
+
32
+ # Registered symbols sorted for detection: longest match wins, then parser priority.
33
+ # Duplicate symbols are deduplicated — the highest-priority currency wins.
34
+ #
35
+ # @return [Array<Array<String, Currency>>] sorted symbol-to-currency mappings
36
+ # @api private
37
+ def currency_symbols
38
+ @currency_symbols || MUTEX.synchronize do
39
+ @currency_symbols =
40
+ currencies.values
41
+ .reject { |currency| currency.symbol.empty? }
42
+ .map { |currency| [currency.symbol, currency] }
43
+ .sort_by { |symbol, currency| [-symbol.length, -currency.priority] }
44
+ .uniq { |symbol, _| symbol }
45
+ .freeze
46
+ end
47
+ end
48
+ end
49
+ end