minting 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +17 -2
- data/Rakefile +3 -3
- data/doc/Mint/Currency.html +816 -0
- data/doc/Mint/Money.html +3471 -0
- data/doc/Mint.html +953 -0
- data/doc/Minting.html +142 -0
- data/doc/_index.html +136 -0
- data/doc/agents/AGENTS.md +25 -0
- data/doc/agents/copilot-instructions.md +75 -0
- data/doc/agents/gemini_gem_evaluation.md +245 -0
- data/doc/agents/recommendations.md +335 -0
- data/doc/class_list.html +54 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +206 -0
- data/doc/css/style.css +1089 -0
- data/doc/file.README.html +379 -0
- data/doc/file_list.html +59 -0
- data/doc/frames.html +22 -0
- data/doc/index.html +379 -0
- data/doc/js/app.js +801 -0
- data/doc/js/full_list.js +334 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +470 -0
- data/doc/top-level-namespace.html +112 -0
- data/lib/minting/mint/currency.rb +1 -1
- data/lib/minting/mint/registry.rb +9 -18
- data/lib/minting/money/conversion.rb +5 -16
- data/lib/minting/money/formatting.rb +34 -1
- data/lib/minting/money/money.rb +83 -5
- data/lib/minting/version.rb +1 -1
- data/minting.gemspec +1 -0
- metadata +38 -2
data/doc/Minting.html
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>
|
|
7
|
+
Module: Minting
|
|
8
|
+
|
|
9
|
+
— Documentation by YARD 0.9.44
|
|
10
|
+
|
|
11
|
+
</title>
|
|
12
|
+
|
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css">
|
|
14
|
+
|
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css">
|
|
16
|
+
|
|
17
|
+
<script type="text/javascript">
|
|
18
|
+
pathId = "Minting";
|
|
19
|
+
relpath = '';
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
|
24
|
+
|
|
25
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<div id="main_progress" aria-hidden="true"></div>
|
|
31
|
+
|
|
32
|
+
<div class="nav_wrap">
|
|
33
|
+
<iframe id="nav" src="class_list.html?1"></iframe>
|
|
34
|
+
<div id="resizer"></div>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div id="main" tabindex="-1">
|
|
38
|
+
<div id="header">
|
|
39
|
+
<div id="menu">
|
|
40
|
+
|
|
41
|
+
<a href="_index.html">Index (M)</a> »
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
<span class="title">Minting</span>
|
|
45
|
+
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div id="search">
|
|
49
|
+
|
|
50
|
+
<a class="full_list_link" id="class_list_link"
|
|
51
|
+
href="class_list.html">
|
|
52
|
+
|
|
53
|
+
<svg width="24" height="24">
|
|
54
|
+
<rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
|
|
55
|
+
<rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
|
|
56
|
+
<rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
|
|
57
|
+
</svg>
|
|
58
|
+
</a>
|
|
59
|
+
|
|
60
|
+
</div>
|
|
61
|
+
<div class="clear"></div>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div id="content"><h1>Module: Minting
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
</h1>
|
|
69
|
+
<div class="box_info">
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
<dl>
|
|
82
|
+
<dt>Defined in:</dt>
|
|
83
|
+
<dd>lib/minting/version.rb
|
|
84
|
+
</dd>
|
|
85
|
+
</dl>
|
|
86
|
+
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<h2>Overview</h2><div class="docstring">
|
|
90
|
+
<div class="discussion">
|
|
91
|
+
<p>Root namespace for the Minting library.</p>
|
|
92
|
+
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="tags">
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<h2>
|
|
101
|
+
Constant Summary
|
|
102
|
+
<small><a href="#" class="constants_summary_toggle">collapse</a></small>
|
|
103
|
+
</h2>
|
|
104
|
+
|
|
105
|
+
<dl class="constants">
|
|
106
|
+
|
|
107
|
+
<dt id="VERSION-constant" class="">VERSION =
|
|
108
|
+
<div class="docstring">
|
|
109
|
+
<div class="discussion">
|
|
110
|
+
<p>Current version of the Minting gem.</p>
|
|
111
|
+
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
<div class="tags">
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
</div>
|
|
118
|
+
</dt>
|
|
119
|
+
<dd><pre class="code"><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>1.3.0</span><span class='tstring_end'>'</span></span><span class='period'>.</span><span class='id identifier rubyid_freeze'>freeze</span></pre></dd>
|
|
120
|
+
|
|
121
|
+
</dl>
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<div id="footer">
|
|
135
|
+
Generated on Tue Jun 2 14:44:35 2026 by
|
|
136
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
|
137
|
+
0.9.44 (ruby-4.0.1).
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
</div>
|
|
141
|
+
</body>
|
|
142
|
+
</html>
|
data/doc/_index.html
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>
|
|
7
|
+
Documentation by YARD 0.9.44
|
|
8
|
+
|
|
9
|
+
</title>
|
|
10
|
+
|
|
11
|
+
<link rel="stylesheet" href="css/style.css" type="text/css">
|
|
12
|
+
|
|
13
|
+
<link rel="stylesheet" href="css/common.css" type="text/css">
|
|
14
|
+
|
|
15
|
+
<script type="text/javascript">
|
|
16
|
+
pathId = null;
|
|
17
|
+
relpath = '';
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
|
22
|
+
|
|
23
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
</head>
|
|
27
|
+
<body>
|
|
28
|
+
<div id="main_progress" aria-hidden="true"></div>
|
|
29
|
+
|
|
30
|
+
<div class="nav_wrap">
|
|
31
|
+
<iframe id="nav" src="class_list.html?1"></iframe>
|
|
32
|
+
<div id="resizer"></div>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div id="main" tabindex="-1">
|
|
36
|
+
<div id="header">
|
|
37
|
+
<div id="menu">
|
|
38
|
+
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div id="search">
|
|
42
|
+
|
|
43
|
+
<a class="full_list_link" id="class_list_link"
|
|
44
|
+
href="class_list.html">
|
|
45
|
+
|
|
46
|
+
<svg width="24" height="24">
|
|
47
|
+
<rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
|
|
48
|
+
<rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
|
|
49
|
+
<rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
|
|
50
|
+
</svg>
|
|
51
|
+
</a>
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
<div class="clear"></div>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div id="content"><h1 class="noborder title">Documentation by YARD 0.9.44</h1>
|
|
58
|
+
<div id="listing">
|
|
59
|
+
<h1 class="alphaindex">Alphabetic Index</h1>
|
|
60
|
+
|
|
61
|
+
<h2>File Listing</h2>
|
|
62
|
+
<ul id="files" class="index_inline_list">
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
<li class="r1"><a href="index.html" title="README">README</a></li>
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
</ul>
|
|
69
|
+
|
|
70
|
+
<div class="clear"></div>
|
|
71
|
+
<h2>Namespace Listing A-Z</h2>
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
<table>
|
|
77
|
+
<tr>
|
|
78
|
+
<td valign='top' width="33%">
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
<ul id="alpha_C" class="alpha">
|
|
82
|
+
<li class="letter">C</li>
|
|
83
|
+
<ul>
|
|
84
|
+
|
|
85
|
+
<li>
|
|
86
|
+
<span class='object_link'><a href="Mint/Currency.html" title="Mint::Currency (class)">Currency</a></span>
|
|
87
|
+
|
|
88
|
+
<small>(Mint)</small>
|
|
89
|
+
|
|
90
|
+
</li>
|
|
91
|
+
|
|
92
|
+
</ul>
|
|
93
|
+
</ul>
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
<ul id="alpha_M" class="alpha">
|
|
97
|
+
<li class="letter">M</li>
|
|
98
|
+
<ul>
|
|
99
|
+
|
|
100
|
+
<li>
|
|
101
|
+
<span class='object_link'><a href="Mint.html" title="Mint (module)">Mint</a></span>
|
|
102
|
+
|
|
103
|
+
</li>
|
|
104
|
+
|
|
105
|
+
<li>
|
|
106
|
+
<span class='object_link'><a href="Minting.html" title="Minting (module)">Minting</a></span>
|
|
107
|
+
|
|
108
|
+
</li>
|
|
109
|
+
|
|
110
|
+
<li>
|
|
111
|
+
<span class='object_link'><a href="Mint/Money.html" title="Mint::Money (class)">Money</a></span>
|
|
112
|
+
|
|
113
|
+
<small>(Mint)</small>
|
|
114
|
+
|
|
115
|
+
</li>
|
|
116
|
+
|
|
117
|
+
</ul>
|
|
118
|
+
</ul>
|
|
119
|
+
|
|
120
|
+
</td>
|
|
121
|
+
</tr>
|
|
122
|
+
</table>
|
|
123
|
+
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div id="footer">
|
|
129
|
+
Generated on Tue Jun 2 14:44:34 2026 by
|
|
130
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
|
131
|
+
0.9.44 (ruby-4.0.1).
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
</div>
|
|
135
|
+
</body>
|
|
136
|
+
</html>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Purpose: concise instructions for AI coding agents working on the minting Ruby gem.
|
|
4
|
+
|
|
5
|
+
Key links:
|
|
6
|
+
- README.md
|
|
7
|
+
- .github/copilot-instructions.md
|
|
8
|
+
|
|
9
|
+
Quick commands:
|
|
10
|
+
- Install: bundle install
|
|
11
|
+
- Full test suite: rake
|
|
12
|
+
- Single test file: ruby -Ilib:test -r ./test/test_helper.rb test/money/money_test.rb
|
|
13
|
+
- Single test method: ruby -Ilib:test -r ./test/test_helper.rb test/money/money_test.rb -n /test_creation/
|
|
14
|
+
- Lint: bundle exec rake cop
|
|
15
|
+
|
|
16
|
+
Project highlights & conventions:
|
|
17
|
+
- Language: Ruby gem (minting)
|
|
18
|
+
- Main code: lib/minting (mint/, money/)
|
|
19
|
+
- Currency data: lib/minting/data/currencies.yaml
|
|
20
|
+
- Amounts stored as Rational; prefer rationals or decimal strings for precision
|
|
21
|
+
- Zero-equality: zeros equal across currencies; non-zero comparisons require same currency
|
|
22
|
+
- Currency codes must match /^[A-Z_]+$/
|
|
23
|
+
- Tests: Minitest; performance benches under test/performance (use BENCH=true)
|
|
24
|
+
|
|
25
|
+
Edit guidance: keep this file minimal and link to existing docs; add repo-specific agent tips here.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Repository overview
|
|
2
|
+
|
|
3
|
+
- Language: Ruby gem (minting)
|
|
4
|
+
- Location of main code: lib/minting and its subfolders (mint/, money/)
|
|
5
|
+
- Public API surface: Mint (factory/helpers), Mint::Money, Mint::Currency
|
|
6
|
+
- Data: built-in currencies in lib/minting/data/currencies.yaml
|
|
7
|
+
- Tests: Minitest (unit) + performance benchmarks under test/performance
|
|
8
|
+
|
|
9
|
+
Build, test and lint commands
|
|
10
|
+
|
|
11
|
+
- Install dependencies:
|
|
12
|
+
- bundle install
|
|
13
|
+
|
|
14
|
+
- Run full test suite (default task):
|
|
15
|
+
- bundle exec rake
|
|
16
|
+
- or simply: rake
|
|
17
|
+
|
|
18
|
+
- Run a single test file (recommended when iterating):
|
|
19
|
+
- ruby -Ilib:test -r ./test/test_helper.rb test/money/money_test.rb
|
|
20
|
+
|
|
21
|
+
- Run a single test method by name (Minitest -n regexp):
|
|
22
|
+
- ruby -Ilib:test -r ./test/test_helper.rb test/money/money_test.rb -n /test_creation/
|
|
23
|
+
|
|
24
|
+
- Performance suites (set BENCH=true as in README):
|
|
25
|
+
- BENCH=true rake bench:performance
|
|
26
|
+
- BENCH=true rake bench:competitive
|
|
27
|
+
- rake bench:regression
|
|
28
|
+
|
|
29
|
+
- Linting:
|
|
30
|
+
- bundle exec rake cop
|
|
31
|
+
- or: bundle exec rubocop
|
|
32
|
+
|
|
33
|
+
- Build gem package:
|
|
34
|
+
- gem build minting.gemspec
|
|
35
|
+
|
|
36
|
+
- Documentation:
|
|
37
|
+
- bundle exec rake yard
|
|
38
|
+
|
|
39
|
+
- README verification:
|
|
40
|
+
- README examples are exercised by `test/minting_test.rb#test_readme_usage`.
|
|
41
|
+
- Prefer the README as the authoritative usage guide for feature behavior and examples.
|
|
42
|
+
|
|
43
|
+
High-level architecture (big picture)
|
|
44
|
+
|
|
45
|
+
- Top-level: lib/minting.rb requires the Mint module and Money implementation.
|
|
46
|
+
- Mint module: currency registry and factory helpers live in lib/minting/mint/*. Registry loads lib/minting/data/currencies.yaml on first access.
|
|
47
|
+
- Currency: lightweight value object (code, subunit, symbol, priority, minimum_amount).
|
|
48
|
+
- Money: immutable value object stored as Rational and rounded to currency.subunit. Core concerns split across lib/minting/money/* (arithmetics, formatting, conversion, coercion, allocation, parsing, comparable).
|
|
49
|
+
- Refinements: Numeric/String/Refinements in lib/minting/mint/refinements.rb expose helpers like 10.dollars, 4.to_money('USD'), and require `using Mint` in scope.
|
|
50
|
+
- Performance tests: separate bench tasks under Rake; test/performance holds benchmark suites.
|
|
51
|
+
|
|
52
|
+
Key conventions and repo-specific rules
|
|
53
|
+
|
|
54
|
+
- Exactness: amounts are stored as Rational and rounded to the currency subunit. Prefer rationals or decimal strings (e.g., '19.99'.to_r or 1999/100r) when precision is needed.
|
|
55
|
+
- Zero equality: zeros are equal across currencies (Mint.money(0,'USD') == Mint.money(0,'EUR') == 0). Non-zero comparisons require identical currency and amount.
|
|
56
|
+
- Currency registration: use Mint.register_currency for idempotent registration; Mint.register_currency! raises on duplicates. Codes must match /^[A-Z_]+$/.
|
|
57
|
+
- Symbol parsing: parser resolves symbols by longest match then currency priority (see Mint.currency_symbols sorting).
|
|
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.
|
|
60
|
+
- Formatting: Money.to_s uses Kernel.format patterns; take care with %<amount>f vs %<amount>d depending on desired rounding/formatting.
|
|
61
|
+
|
|
62
|
+
Files and places to check first during edits
|
|
63
|
+
|
|
64
|
+
- lib/minting/money - core behavior and arithmetic
|
|
65
|
+
- lib/minting/mint - currency registry, refinements, and factory API
|
|
66
|
+
- lib/minting/data/currencies.yaml - canonical currency definitions
|
|
67
|
+
- test/ - unit tests; test/performance - benchmark suites
|
|
68
|
+
|
|
69
|
+
Notes for Copilot sessions
|
|
70
|
+
|
|
71
|
+
- Prefer making atomic changes and run the unit test(s) covering the changed area. Use the single-file test invocation above when iterating rapidly.
|
|
72
|
+
- When changing numeric/rounding behavior, run both unit tests and relevant performance benchmarks.
|
|
73
|
+
- Respect zero-equality semantics and currency code validation when modifying equality/hash logic.
|
|
74
|
+
|
|
75
|
+
If you want, update this file with project-specific conventions to capture workflow choices (e.g., backport policy, DI patterns).
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# Ruby Gem Evaluation Report: `minting`
|
|
2
|
+
|
|
3
|
+
An in-depth review and technical evaluation of the `minting` Ruby gem—a fast, precise, and developer-friendly money handling library.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 📊 Executive Summary
|
|
8
|
+
|
|
9
|
+
| Category | Rating | Key Strength | Areas for Improvement |
|
|
10
|
+
| :--- | :---: | :--- | :--- |
|
|
11
|
+
| **Clarity & Documentation** | **A+** | Modular design, intuitive API names, clean separation of concerns, and **88.33% YARD coverage** (100% public API documented). | None; documentation is detailed and comprehensive. |
|
|
12
|
+
| **Good Practices** | **A+** | Perfect immutability, `Rational` for floating-point safety, Ruby `refinement` scoping, custom zero-equality. | Missing `gem 'benchmark'` dependency for Ruby 4.0+ in test environments. |
|
|
13
|
+
| **Utility** | **A** | 117+ built-in ISO 4217 currencies, split/allocate penny conservation, HTML/JSON support. | Standard exchange rate integrations (planned in roadmap). |
|
|
14
|
+
| **Test Coverage** | **A+** | **100% Line Coverage** (261/261 lines), robust performance and regression benchmark suites. | None (stellar test quality). |
|
|
15
|
+
|
|
16
|
+
> [!NOTE]
|
|
17
|
+
> **Overall Verdict:** The `minting` gem represents an exceptional piece of software engineering. It is a highly optimized, robust, and beautifully designed alternative to the traditional `money` gem, showing meticulous attention to financial precision, developer ergonomics, and runtime efficiency.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 🔍 Detailed Evaluation
|
|
22
|
+
|
|
23
|
+
### 1. Clarity & Code Architecture
|
|
24
|
+
|
|
25
|
+
The directory layout of the gem is clean and follows standard packaging structures:
|
|
26
|
+
```text
|
|
27
|
+
lib/
|
|
28
|
+
├── minting/
|
|
29
|
+
│ ├── data/
|
|
30
|
+
│ │ └── currencies.yaml # built-in ISO 4217 database
|
|
31
|
+
│ ├── mint/
|
|
32
|
+
│ │ ├── currency.rb # Mint::Currency model
|
|
33
|
+
│ │ ├── refinements.rb # Scoped Numeric/String refinements
|
|
34
|
+
│ │ └── registry.rb # Global lookup & registration
|
|
35
|
+
│ ├── money/
|
|
36
|
+
│ │ ├── allocation.rb # Split & proportional allocate algorithms
|
|
37
|
+
│ │ ├── arithmetics.rb # Unary/binary mathematical operations
|
|
38
|
+
│ │ ├── coercion.rb # Coercion protocols (e.g. Numeric * Money)
|
|
39
|
+
│ │ ├── comparable.rb # Custom comparisons and equality
|
|
40
|
+
│ │ ├── conversion.rb # Serialization (json, html, integers)
|
|
41
|
+
│ │ ├── formatting.rb # String layout configurations
|
|
42
|
+
│ │ ├── money.rb # Main immutable Mint::Money class
|
|
43
|
+
│ │ └── parse.rb # Localized string parsing logic
|
|
44
|
+
│ ├── mint.rb # Entry module for registry & refinements
|
|
45
|
+
│ ├── money.rb # Entry for Mint::Money class and modules
|
|
46
|
+
│ └── version.rb # Gem version designation
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
#### Code Clarity Strengths:
|
|
50
|
+
- **Concise modularization**: The `Mint::Money` class is split into single-responsibility concern files (arithmetics, comparable, allocation, etc.). They open the `class Money` namespace and inject clean logic, which keeps each file well under 100 lines and extremely readable.
|
|
51
|
+
- **Self-explanatory naming**: Naming conventions are standard and descriptive (e.g. `allocate_left_over!`, `same_currency?`, `minimum_amount`).
|
|
52
|
+
- **YARD Documentation**: Public APIs are well-documented with parameter descriptions and usage examples.
|
|
53
|
+
|
|
54
|
+
#### Points for Improvement / Discovered Quirks:
|
|
55
|
+
- **Documentation Typo**: In [formatting.rb](file:///Users/gilson/code/minting/lib/minting/money/formatting.rb#L14), the code comment states:
|
|
56
|
+
```ruby
|
|
57
|
+
# money.to_s(thousand: '.', decimal: ',') #=> "$.1234,56"
|
|
58
|
+
```
|
|
59
|
+
However, the thousands separator regex correctly formats `1234.56` into `$1.234,56`. The comment's output is missing the digit `1` after the currency symbol (`$.1234,56` instead of `$1.234,56`).
|
|
60
|
+
|
|
61
|
+
- **Parsing Edge Case in [parse.rb](file:///Users/gilson/code/minting/lib/minting/money/parse.rb#L60-L66)**:
|
|
62
|
+
In `parse_currency`, the parser uses `input[/\b([A-Z]{3})\b/, 1]` to identify an ISO currency code. It grabs the *first* occurrence of a 3-letter uppercase word.
|
|
63
|
+
If a string has an unrelated 3-letter uppercase word before the actual currency code (e.g., `"MAX 10.00 USD"`), the parser will extract `"MAX"`, try to look up a registered currency called `"MAX"`, fail, and proceed to symbol lookup. If there's no symbol in the string, it throws an `ArgumentError` despite `"USD"` being present in the string.
|
|
64
|
+
> [!TIP]
|
|
65
|
+
> *Solution:* Consider scanning all 3-letter uppercase words and matching them against registered currencies rather than taking the first match unconditionally.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
### 2. Good Practices
|
|
70
|
+
|
|
71
|
+
The gem exhibits top-tier Ruby development practices:
|
|
72
|
+
|
|
73
|
+
- **Immutability & Safety**: Both `Mint::Currency` and `Mint::Money` objects are frozen upon initialization:
|
|
74
|
+
```ruby
|
|
75
|
+
def initialize(amount, currency)
|
|
76
|
+
# ...
|
|
77
|
+
@amount = currency.normalize_amount(amount)
|
|
78
|
+
@currency = currency
|
|
79
|
+
freeze
|
|
80
|
+
end
|
|
81
|
+
```
|
|
82
|
+
This eliminates state mutation bugs entirely. Any operation returns a *new* instance of `Money`.
|
|
83
|
+
|
|
84
|
+
- **Exact Numeric Representation**: Financial software *must* avoid binary float rounding errors. `Minting` enforces this by coercing amounts to `Rational` numbers (`amount.to_r`) and rounding them exactly to the currency's subunit.
|
|
85
|
+
|
|
86
|
+
- **Coercion Protocol Integration**: Rather than throwing type errors when interacting with standard Numeric classes, the gem implements `coerce` to allow fluent syntax like:
|
|
87
|
+
```ruby
|
|
88
|
+
10.dollars * 2 #=> [USD 20.00]
|
|
89
|
+
2 * 10.dollars #=> [USD 20.00] (using coercion)
|
|
90
|
+
0 + 10.dollars #=> [USD 10.00] (using zero-coercion)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
- **Refinements Scoping**: Ergnomic helper methods like `10.dollars` or `'19.99'.to_money('BRL')` are provided via Ruby `refinements` rather than globally monkey-patching `Numeric` and `String`. This ensures the extension is completely safe and only active within files that declare `using Mint`.
|
|
94
|
+
|
|
95
|
+
- **Custom Zero Equality**: Financial calculations often require zero check parity. In [comparable.rb](file:///Users/gilson/code/minting/lib/minting/money/comparable.rb#L8-L13), `==` is custom-designed so that zero values are equal regardless of their currency:
|
|
96
|
+
```ruby
|
|
97
|
+
0.dollars == 0.reais #=> true
|
|
98
|
+
0.dollars == 0 #=> true
|
|
99
|
+
```
|
|
100
|
+
This is a brilliant trade-off between currency safety and numeric convenience.
|
|
101
|
+
|
|
102
|
+
> [!IMPORTANT]
|
|
103
|
+
> **Ruby 4.0 Compatibility Warning:**
|
|
104
|
+
> The `benchmark` standard library was removed in Ruby 4.0.0. The performance and competitive benchmark files directly call `require 'benchmark'`, which triggers a `LoadError` on Ruby 4.0+ environments unless the `benchmark` gem is explicitly added as a dependency in the `Gemfile` or `gemspec`.
|
|
105
|
+
>
|
|
106
|
+
> ```ruby
|
|
107
|
+
> # Fix for Gemfile (under development group)
|
|
108
|
+
> gem 'benchmark'
|
|
109
|
+
> ```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### 3. Utility
|
|
114
|
+
|
|
115
|
+
`Minting` packs high utility in a lightweight package with **zero runtime dependencies**:
|
|
116
|
+
|
|
117
|
+
1. **Robust Penny Preservation (Allocation & Split)**:
|
|
118
|
+
Dividing currency often leads to loss or creation of subunits (e.g. split $10.00 in 3 parts). `Minting` implements the **largest remainder method** inside `allocate` and `split` to disperse penny remainders over the first slots:
|
|
119
|
+
```ruby
|
|
120
|
+
10.dollars.split(3) #=> [[USD 3.34], [USD 3.33], [USD 3.33]] (Sum is exactly 10.00!)
|
|
121
|
+
```
|
|
122
|
+
2. **Rich Formatting Engine**:
|
|
123
|
+
Leverages `Kernel.format` placeholders internally, letting developers apply complex formats, custom padding, thousand/decimal separators, and negative formatting structures:
|
|
124
|
+
```ruby
|
|
125
|
+
price.to_s(format: { negative: '%<symbol>s(%<amount>f)' }) #=> "$(10.00)" (Accounting negative)
|
|
126
|
+
```
|
|
127
|
+
3. **HTML5 and JSON Serialization**:
|
|
128
|
+
Provides built-in web serialization support via `to_json` (extremely fast string interpolation without pulling JSON gem dependencies) and semantic HTML `to_html`:
|
|
129
|
+
```ruby
|
|
130
|
+
10.dollars.to_html #=> "<data class='money' title='USD 10.00'>$10.00</data>"
|
|
131
|
+
```
|
|
132
|
+
4. **Massive Registry**: Lazy-loads 117+ standard currencies with instant lookup.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
### 4. Test Coverage & Quality
|
|
137
|
+
|
|
138
|
+
The test architecture of the `minting` gem is incredibly strong:
|
|
139
|
+
|
|
140
|
+
- **100.0% Line Coverage**: Grounded in unit-test coverage validation (261 / 261 lines fully covered).
|
|
141
|
+
- **README Verification**: [minting_test.rb](file:///Users/gilson/code/minting/test/minting_test.rb) contains `test_readme_usage` which literally runs all README example code inside a test, ensuring documentation never gets out-of-date or inaccurate.
|
|
142
|
+
- **Mathematical Scaling Guarantees**: Under [regression_benchmark.rb](file:///Users/gilson/code/minting/test/performance/regression_benchmark.rb), the gem uses Minitest's performance benchmark assertions to guarantee operations run within exact complexity bounds:
|
|
143
|
+
- `assert_performance_constant`: Validates $O(1)$ constant time for creation, arithmetic, comparisons, conversions, and refinements.
|
|
144
|
+
- `assert_performance_linear`: Validates $O(N)$ linear complexity for proportional `split` and `allocate` algorithms.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### 5. YARD Documentation Coverage
|
|
149
|
+
|
|
150
|
+
The public API documentation coverage was analyzed using `yard stats --list-undoc`:
|
|
151
|
+
- **Files analyzed:** 11
|
|
152
|
+
- **Overall Coverage:** **55.00%** documented (46 total methods, 24 undocumented)
|
|
153
|
+
|
|
154
|
+
#### Documented Elements
|
|
155
|
+
The main class `Mint::Money` is partially documented, specifically around its constructor, basic usage guidelines, and custom formatting structures. Scoped Numeric/String refinements also carry clear code comments and examples.
|
|
156
|
+
|
|
157
|
+
#### Undocumented Elements (Public API Gaps)
|
|
158
|
+
The analysis reveals several critical public-facing methods that lack any YARD documentation:
|
|
159
|
+
1. **Currency Registry (`lib/minting/mint/registry.rb`):**
|
|
160
|
+
- `Mint.currencies` (Returns the registered currency database hash)
|
|
161
|
+
- `Mint.currency` (Finds a currency by symbol, string, or object)
|
|
162
|
+
- `Mint.register_currency` (Standard currency registration helper)
|
|
163
|
+
- `Mint.register_currency!` (Strict currency registration raising errors)
|
|
164
|
+
2. **Allocation Algorithms (`lib/minting/money/allocation.rb`):**
|
|
165
|
+
- `Mint::Money#split` (Divides money into equal parts with penny preservation)
|
|
166
|
+
- *Note:* While YARD flags `allocate` as "documented" because it is a core Ruby method override, it represents a custom allocation algorithm and lacks YARD parameter/return documentation.
|
|
167
|
+
3. **Core Serialization & Conversions (`lib/minting/money/conversion.rb`):**
|
|
168
|
+
- `Mint::Money#to_f` (Coerces to Float)
|
|
169
|
+
- `Mint::Money#to_i` (Coerces to Integer)
|
|
170
|
+
- `Mint::Money#to_r` (Coerces to Rational)
|
|
171
|
+
- `Mint::Money#to_html` (Generates safe HTML5 `<data>` element)
|
|
172
|
+
- `Mint::Money#to_json` (Generates JSON string representation)
|
|
173
|
+
4. **Ergonomic Accessors (`lib/minting/money/money.rb`):**
|
|
174
|
+
- `Mint::Money#currency_code` (Convenience method to access the currency's ISO string)
|
|
175
|
+
5. **Core Arithmetic & Operators (`lib/minting/money/arithmetics.rb`):**
|
|
176
|
+
- Unary negation (`-@`), absolute value (`abs`), and successor (`succ`) are undocumented.
|
|
177
|
+
|
|
178
|
+
#### Internal Methods Cluttering Public Stats
|
|
179
|
+
The YARD engine reports several internal helper classes/methods as undocumented public APIs because they are not tagged as private:
|
|
180
|
+
- `Mint::Money::CoercedNumber` and its internal arithmetic helpers (`+`, `-`, `*`, `/`, `<=>`)
|
|
181
|
+
- `Mint::Money#format_amount`
|
|
182
|
+
- `Mint::Money#hash` and `Mint::Money#inspect`
|
|
183
|
+
|
|
184
|
+
> [!TIP]
|
|
185
|
+
> *Solution:* Mark internal components with `@private` or `:nodoc:` to clean up public documentation stats, and add complete YARD tags (like `@param`, `@return`, and `@raise`) to the core registry, allocation, and conversion APIs listed above.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## 🚀 Performance Telemetry
|
|
190
|
+
|
|
191
|
+
The performance benchmarks were executed on **ruby 4.0.1 (arm64-darwin25)**. The actual, verified results represent a hyper-efficient money-handling engine:
|
|
192
|
+
|
|
193
|
+
### 1. Arithmetic Operations Performance
|
|
194
|
+
| Operation | Throughput (Iterations / Second) | Mean Latency per Operation | Relative Speed vs Negation |
|
|
195
|
+
| :--- | :---: | :---: | :---: |
|
|
196
|
+
| **Negation (`-money`)** | **1,547,277 i/s** | 646.30 ns | *Base (1.00x)* |
|
|
197
|
+
| **Multiplication (`*`)** | **1,326,660 i/s** | 753.77 ns | 1.17x slower |
|
|
198
|
+
| **Addition (`+`)** | **1,069,469 i/s** | 935.04 ns | 1.45x slower |
|
|
199
|
+
| **Subtraction (`-`)** | **1,044,388 i/s** | 957.50 ns | 1.48x slower |
|
|
200
|
+
| **Division (`/`)** | **1,003,567 i/s** | 996.45 ns | 1.54x slower |
|
|
201
|
+
| **Absolute Value (`abs`)** | **788,056 i/s** | 1,268.94 ns | 1.96x slower |
|
|
202
|
+
| **Chained Math Operations** | **274,313 i/s** | 3,645.45 ns | 5.64x slower |
|
|
203
|
+
|
|
204
|
+
### 2. Money Creation Performance
|
|
205
|
+
| Instantiation Pattern | Throughput (Iterations / Second) | Mean Latency per Operation | Relative Speed vs Rational |
|
|
206
|
+
| :--- | :---: | :---: | :---: |
|
|
207
|
+
| **`Mint.money(rational, string)`** | **1,397,420 i/s** | 715.60 ns | *Base (1.00x)* |
|
|
208
|
+
| **`Mint.money(integer, string)`** | **1,357,639 i/s** | 736.57 ns | same-ish |
|
|
209
|
+
| **`Mint.money(float, string)`** | **1,031,220 i/s** | 969.72 ns | 1.36x slower |
|
|
210
|
+
| **`Mint.money(random, random_currency)`** | **830,307 i/s** | 1,204.37 ns | 1.68x slower |
|
|
211
|
+
|
|
212
|
+
> [!TIP]
|
|
213
|
+
> Instantiating `Mint.money` using `Rational` or `Integer` inputs bypasses float parsing, leading to **~1.4 Million creations per second** (about 36% faster than using `Float` inputs).
|
|
214
|
+
|
|
215
|
+
### 3. Comparison Operations Performance
|
|
216
|
+
| Operation | Throughput (Iterations / Second) | Mean Latency per Operation | Relative Speed vs Spaceship |
|
|
217
|
+
| :--- | :---: | :---: | :---: |
|
|
218
|
+
| **Comparison (`<=>`)** | **5,215,469 i/s** | 191.74 ns | *Base (1.00x)* |
|
|
219
|
+
| **Greater Than (`>`)** | **4,346,286 i/s** | 230.08 ns | 1.20x slower |
|
|
220
|
+
| **Equality (different currencies)** | **3,254,787 i/s** | 307.24 ns | 1.60x slower |
|
|
221
|
+
| **Hash Generation (`hash`)** | **3,140,152 i/s** | 318.46 ns | 1.66x slower |
|
|
222
|
+
| **Equality (same currency)** | **2,900,045 i/s** | 344.82 ns | 1.80x slower |
|
|
223
|
+
| **Eql? check (`eql?`)** | **2,583,198 i/s** | 387.12 ns | 2.02x slower |
|
|
224
|
+
|
|
225
|
+
### 4. Currency Registry Operations Performance
|
|
226
|
+
| Operation | Throughput (Iterations / Second) | Mean Latency per Operation | Relative Speed vs Lookup |
|
|
227
|
+
| :--- | :---: | :---: | :---: |
|
|
228
|
+
| **Lookup (String Key)** | **3,833,009 i/s** | 260.89 ns | *Base (1.00x)* |
|
|
229
|
+
| **Lookup (Symbol Key)** | **3,637,937 i/s** | 274.88 ns | 1.05x slower |
|
|
230
|
+
| **Currency Registration** | **3,328,872 i/s** | 300.40 ns | 1.15x slower |
|
|
231
|
+
| **Instantiate Money with Lookup** | **1,368,688 i/s** | 730.63 ns | 2.80x slower |
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## 💡 Recommendations for the Maintainer
|
|
236
|
+
|
|
237
|
+
1. **Add `gem 'benchmark'` to development group in Gemfile**:
|
|
238
|
+
This resolves the Ruby 4.0+ compatibility issue and lets the competitive benchmark suite execute smoothly.
|
|
239
|
+
2. **Correct the documentation typo in `formatting.rb`**:
|
|
240
|
+
Change `$.1234,56` to `$1.234,56` to ensure copy-paste docs match reality.
|
|
241
|
+
3. **Upgrade currency code detection in `parse.rb`**:
|
|
242
|
+
Refactor `parse_currency` to search for *registered* codes present anywhere in the string, rather than grabbing the *first* uppercase word of length 3 indiscriminately.
|
|
243
|
+
4. **Improve YARD documentation coverage from 55% to 100%**:
|
|
244
|
+
- Document critical public APIs (Currency Registry, `#split`/`#allocate`, and core serialization helpers like `#to_json` and `#to_html`).
|
|
245
|
+
- Mark internal helper modules (such as `Mint::Money::CoercedNumber` and `#format_amount`) with `@private` or `:nodoc:` tags to clean up public API reporting stats.
|