minting 1.7.0 → 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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +80 -112
  3. data/Rakefile +13 -1
  4. data/bin/bench_check +46 -0
  5. data/doc/Mint/Currency.html +446 -46
  6. data/doc/Mint/CurrencyRegistry.html +7 -7
  7. data/doc/Mint/Money.html +203 -177
  8. data/doc/Mint/RangeStepPatch.html +277 -0
  9. data/doc/Mint/Registry.html +842 -0
  10. data/doc/Mint/UnknownCurrency.html +2 -2
  11. data/doc/Mint.html +385 -66
  12. data/doc/Minting.html +3 -3
  13. data/doc/_index.html +24 -9
  14. data/doc/agents/api_review-2026-06-15.md +342 -0
  15. data/doc/class_list.html +1 -1
  16. data/doc/file.README.html +97 -89
  17. data/doc/index.html +97 -89
  18. data/doc/method_list.html +97 -25
  19. data/doc/top-level-namespace.html +13 -5
  20. data/lib/minting/currency/currency.rb +75 -0
  21. data/lib/minting/mint/aliases.rb +3 -0
  22. data/lib/minting/mint/dsl/{refinements.rb → numeric.rb} +6 -5
  23. data/lib/minting/mint/dsl/range.rb +31 -18
  24. data/lib/minting/mint/dsl/string.rb +12 -0
  25. data/lib/minting/mint/dsl/top_level.rb +3 -0
  26. data/lib/minting/mint/locale_backend.rb +29 -0
  27. data/lib/minting/mint/mint.rb +28 -12
  28. data/lib/minting/mint/parser/parser.rb +62 -0
  29. data/lib/minting/mint/parser/separators.rb +39 -0
  30. data/lib/minting/mint/registry/registration.rb +33 -0
  31. data/lib/minting/mint/registry/registry.rb +38 -0
  32. data/lib/minting/mint/registry/symbols.rb +49 -0
  33. data/lib/minting/mint/registry/zeros.rb +18 -0
  34. data/lib/minting/mint.rb +13 -16
  35. data/lib/minting/money/allocation/allocation.rb +25 -0
  36. data/lib/minting/money/{allocation.rb → allocation/split.rb} +1 -19
  37. data/lib/minting/money/arithmetics/methods.rb +27 -0
  38. data/lib/minting/money/{arithmetics.rb → arithmetics/operators.rb} +0 -21
  39. data/lib/minting/money/clamp.rb +66 -0
  40. data/lib/minting/money/coercion.rb +10 -0
  41. data/lib/minting/money/comparable.rb +6 -0
  42. data/lib/minting/money/constructors.rb +14 -9
  43. data/lib/minting/money/format/formatting.rb +60 -0
  44. data/lib/minting/money/{formatting.rb → format/to_s.rb} +13 -36
  45. data/lib/minting/money/money.rb +12 -58
  46. data/lib/minting/version.rb +1 -1
  47. metadata +29 -19
  48. data/lib/minting/mint/currency/currency.rb +0 -36
  49. data/lib/minting/mint/currency/currency_registry.rb +0 -67
  50. data/lib/minting/mint/currency/world_currencies.rb +0 -16
  51. data/lib/minting/mint/parser.rb +0 -85
  52. /data/doc/agents/{AGENTS.md → expired/AGENTS.md} +0 -0
  53. /data/doc/agents/{copilot-instructions.md → expired/copilot-instructions.md} +0 -0
  54. /data/doc/agents/{gemini_gem_evaluation.md → expired/gemini_gem_evaluation.md} +0 -0
  55. /data/doc/agents/{recommendations.md → expired/recommendations.md} +0 -0
  56. /data/doc/agents/{rubocop-issues.md → expired/rubocop-issues.md} +0 -0
data/doc/Minting.html CHANGED
@@ -116,7 +116,7 @@
116
116
 
117
117
  </div>
118
118
  </dt>
119
- <dd><pre class="code"><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>1.6.3</span><span class='tstring_end'>&#39;</span></span></pre></dd>
119
+ <dd><pre class="code"><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>1.7.2</span><span class='tstring_end'>&#39;</span></span></pre></dd>
120
120
 
121
121
  </dl>
122
122
 
@@ -132,9 +132,9 @@
132
132
  </div>
133
133
 
134
134
  <div id="footer">
135
- Generated on Wed Jun 10 01:35:30 2026 by
135
+ Generated on Mon Jun 15 19:57:57 2026 by
136
136
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
137
- 0.9.44 (ruby-4.0.1).
137
+ 0.9.44 (ruby-4.0.5).
138
138
  </div>
139
139
 
140
140
  </div>
data/doc/_index.html CHANGED
@@ -89,13 +89,6 @@
89
89
 
90
90
  </li>
91
91
 
92
- <li>
93
- <span class='object_link'><a href="Mint/CurrencyRegistry.html" title="Mint::CurrencyRegistry (module)">CurrencyRegistry</a></span>
94
-
95
- <small>(Mint)</small>
96
-
97
- </li>
98
-
99
92
  </ul>
100
93
  </ul>
101
94
 
@@ -125,6 +118,28 @@
125
118
  </ul>
126
119
 
127
120
 
121
+ <ul id="alpha_R" class="alpha">
122
+ <li class="letter">R</li>
123
+ <ul>
124
+
125
+ <li>
126
+ <span class='object_link'><a href="Mint/RangeStepPatch.html" title="Mint::RangeStepPatch (module)">RangeStepPatch</a></span>
127
+
128
+ <small>(Mint)</small>
129
+
130
+ </li>
131
+
132
+ <li>
133
+ <span class='object_link'><a href="Mint/Registry.html" title="Mint::Registry (module)">Registry</a></span>
134
+
135
+ <small>(Mint)</small>
136
+
137
+ </li>
138
+
139
+ </ul>
140
+ </ul>
141
+
142
+
128
143
  <ul id="alpha_U" class="alpha">
129
144
  <li class="letter">U</li>
130
145
  <ul>
@@ -148,9 +163,9 @@
148
163
  </div>
149
164
 
150
165
  <div id="footer">
151
- Generated on Wed Jun 10 01:35:30 2026 by
166
+ Generated on Mon Jun 15 19:57:57 2026 by
152
167
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
153
- 0.9.44 (ruby-4.0.1).
168
+ 0.9.44 (ruby-4.0.5).
154
169
  </div>
155
170
 
156
171
  </div>
@@ -0,0 +1,342 @@
1
+ # Minting gem API review
2
+
3
+ **Data:** 2026-06-15
4
+ **Author:** Wave AI (via Wave Terminal assistant)
5
+
6
+ ## 1. Top-level API surface (Mint / Money / Currency)
7
+
8
+ ### What’s good
9
+
10
+ - `Mint.money(10, 'USD')` is clear and discoverable.
11
+ - Refinements (`10.dollars`, `4.to_money('USD')`) are modern and opt-in.
12
+ - Optional `minting/dsl` with `Money` / `Currency` constants is a good compromise for ergonomics vs. gem conflicts.
13
+
14
+ ### Suggestions
15
+
16
+ 1. **Tighten constructor story (too many verbs)**
17
+ Right now you have:
18
+ - `Mint.money(amount, code)`
19
+ - `Mint.zero(currency)`
20
+ - `Mint::Money.create(amount, currency)`
21
+ - `Mint::Money.from_fractional(fractional, currency)`
22
+ - `Money.create` / `Money.from_fractional` / `price.mint(new_amount)` (instance)
23
+
24
+ Consider a simpler, more opinionated set:
25
+
26
+ - **Class side:**
27
+ - `Money.from(amount, currency)` (instead of `create`)
28
+ - `Money.from_fractional(fractional, currency)`
29
+ - **Module side:**
30
+ - `Mint.money(amount, currency)` (delegates to `Money.from`)
31
+ - `Mint.from_fractional(fractional, currency)` (delegates to `Money.from_fractional`)
32
+ - `Mint.zero(currency)`
33
+
34
+ And **remove / de-emphasize** `Money.create` from public docs (keep as private alias if needed).
35
+
36
+ 2. **Rename `mint(new_amount)` → clearer “copy with” semantics**
37
+
38
+ `price.mint(15.00)` is cute but not self-describing. Consider:
39
+
40
+ - `price.with_amount(15.00)`
41
+ - or `price.change(amount: 15.00)`
42
+ - or `price.rebuild(15.00)` (less good, but at least indicates a copy)
43
+
44
+ And surface this in README (“Immutability helpers”).
45
+
46
+ 3. **Top-level constants: make the opt-in path prominent, but one-liner**
47
+
48
+ You already have:
49
+
50
+ ```ruby
51
+ require "minting"
52
+ require "minting/dsl" # opt-in top‑level Money / Currency
53
+ ```
54
+
55
+ For Rails developers, you might recommend in README:
56
+
57
+ ```ruby
58
+ # config/initializers/minting.rb
59
+ require "minting/dsl"
60
+ ```
61
+
62
+ And add a short “Rails setup” snippet, since that’s what competing gems usually highlight first.
63
+
64
+ ---
65
+
66
+ ## 2. Money semantics & naming
67
+
68
+ ### Zero equality semantics
69
+
70
+ You do something non-standard and powerful:
71
+
72
+ ```ruby
73
+ Mint.money(0, 'USD') == Mint.money(0, 'EUR') # => true
74
+ Mint.money(0, 'USD') == 0 # => true
75
+ ```
76
+
77
+ This is very nice for totals, but surprising vs. `money` gem.
78
+
79
+ **Changes:**
80
+
81
+ 1. **Make this a named feature, with an escape hatch**
82
+
83
+ - Give it a name in docs: e.g. “currency-agnostic zero”.
84
+ - Provide an **explicit predicate or helper**:
85
+
86
+ ```ruby
87
+ price.zero? # usual predicate
88
+ price.strictly_zero_in?('USD') # same_currency? + zero?
89
+ ```
90
+
91
+ - Consider a **config flag** or alternate comparator:
92
+
93
+ ```ruby
94
+ Mint.strict_zero_comparison = true
95
+ # or
96
+ price.eql_in_currency?(other_price)
97
+ ```
98
+
99
+ 2. **Docs: add a clear “Gotchas” section**
100
+ Call out at the bottom of README “Behavior that differs from `money` gem”, starting with zero. This directly addresses “gap to competitors” and reduces surprises.
101
+
102
+ ### `same_currency?`
103
+
104
+ Current doc:
105
+
106
+ ```ruby
107
+ # @param other [Currency] the target currency to compare
108
+ def same_currency?(other) = other.currency == currency
109
+ ```
110
+
111
+ This is odd: the param type is a `Currency` in the docs, but you call `other.currency` which implies it’s actually `Money`.
112
+
113
+ **Change:**
114
+
115
+ - Decide on the intent; then:
116
+ - If it compares two `Money` objects, define:
117
+
118
+ ```ruby
119
+ # @param other [Money]
120
+ def same_currency?(other) = other.currency == currency
121
+ ```
122
+
123
+ - Or if you want both forms, accept either and update docs:
124
+
125
+ ```ruby
126
+ def same_currency?(other)
127
+ other_currency =
128
+ case other
129
+ when Mint::Money then other.currency
130
+ when Mint::Currency then other
131
+ else
132
+ Currency.resolve!(other)
133
+ end
134
+
135
+ other_currency == currency
136
+ end
137
+ ```
138
+
139
+ - Consider renaming to `same_currency_as?(other)` to better match Ruby predicate idioms and feel less “type-ish”.
140
+
141
+ ---
142
+
143
+ ## 3. Parsing API
144
+
145
+ `Mint.parse` is already strong. To beat competitors, lean into this.
146
+
147
+ ### Suggestions
148
+
149
+ 1. **Return type & error modes**
150
+
151
+ - Document explicitly that `Mint.parse` returns `Mint::Money`.
152
+ - Add **two modes**:
153
+ - `Mint.parse!(...)` – raises on failure.
154
+ - `Mint.parse(...)` – returns `nil` on failure or perhaps `Result` object in future, but nil is sufficient initially.
155
+
156
+ 2. **Expose configuration hooks**
157
+
158
+ Even if not fully implemented yet, define the shape:
159
+
160
+ ```ruby
161
+ Mint.parser.default_currency = 'USD'
162
+ Mint.parser.symbol_priority = %w[USD CAD AUD]
163
+ ```
164
+
165
+ That gives you a path to match/beat `monetize` / `money` ecosystem flexibility.
166
+
167
+ 3. **Quality-of-life alias**
168
+
169
+ - `Mint.money_from(str, currency_code = nil)`
170
+ - Or a class method: `Mint::Money.parse(str, currency: nil)`
171
+
172
+ That keeps “all money stuff under Money” for folks who avoid module functions.
173
+
174
+ ---
175
+
176
+ ## 4. Formatting API
177
+
178
+ Current:
179
+
180
+ - `to_s(format: '%<symbol>s%<amount>f')`
181
+ - Hash format for per-sign: `{ negative: '(%<symbol>s%<amount>f)' }`
182
+
183
+ This is powerful but low-level. Competing gems typically offer higher-level presets and localization.
184
+
185
+ ### Suggestions
186
+
187
+ 1. **Named formats / shortcuts**
188
+
189
+ Something like:
190
+
191
+ ```ruby
192
+ price.format # same as to_s
193
+ price.format(:iso) # "USD 9.99"
194
+ price.format(:symbol) # "$9.99"
195
+ price.format(:code) # "9.99 USD"
196
+ ```
197
+
198
+ Internally, use a format registry:
199
+
200
+ ```ruby
201
+ Mint.formats.register(:iso, '%<currency>s %<amount>f')
202
+ ```
203
+
204
+ 2. **Accounting format shortcut**
205
+
206
+ You already support per-sign hash formats; expose a named helper:
207
+
208
+ ```ruby
209
+ price.to_accounting # ($1,234.56) for negatives, 0.00 for zero
210
+ ```
211
+
212
+ 3. **JSON / Rails integration**
213
+
214
+ You have:
215
+
216
+ ```ruby
217
+ price.to_json # => {"currency":"USD","amount":"9.99"}
218
+ price.to_hash # => { currency: "USD", amount: "9.99" }
219
+ ```
220
+
221
+ For better Rails DX & parity:
222
+
223
+ - Add `as_json` delegating to `to_hash`.
224
+ - Mention in README: “Works with Rails serialization (`as_json` implemented).”
225
+
226
+ ---
227
+
228
+ ## 5. Currency API
229
+
230
+ Current:
231
+
232
+ - `Mint.currency_for_code('USD')`
233
+ - `Mint.currency_for_symbol('$')`
234
+ - `Mint.register_currency(...)`
235
+ - `Mint.world_currencies` (+ internal `Registry`)
236
+
237
+ ### Suggestions
238
+
239
+ 1. **Consistent names & variants**
240
+
241
+ Consider:
242
+
243
+ - `Mint.currency('USD')` → primary lookup (by code; maybe symbol in future).
244
+ - Keep `currency_for_code` / `currency_for_symbol` as explicit variants, but guide people to the simple `currency`.
245
+
246
+ 2. **Clarify custom currency lifecycle**
247
+
248
+ Competitors often have quirks here; you can win on clarity:
249
+
250
+ - Are custom currencies persisted per process?
251
+ - Are they thread-safe?
252
+ - Are they ordered by `priority` in deterministic way?
253
+
254
+ Add a small “Custom currencies” section with examples and guarantees.
255
+
256
+ 3. **Expose the registry read-only**
257
+
258
+ Instead of `world_currencies` returning “the frozen world-currencies hash” (but you also have `Registry.currencies`), maybe have:
259
+
260
+ ```ruby
261
+ Mint.currencies # => { "USD" => <Currency>, ... } (frozen hash)
262
+ ```
263
+
264
+ And keep `Registry` private in docs.
265
+
266
+ ---
267
+
268
+ ## 6. Error types & clarity
269
+
270
+ ### UnknownCurrency
271
+
272
+ ```ruby
273
+ # Unknown currency excpetion
274
+ class UnknownCurrency < StandardError; end
275
+ ```
276
+
277
+ - Typo in comment (“excpetion”).
278
+ - Recommend renaming to `UnknownCurrencyError` for conventional Ruby style.
279
+ - Ensure it’s actually raised from `Currency.resolve!` (and document that).
280
+
281
+ ### Validation errors
282
+
283
+ You currently raise `ArgumentError` for several things (`amount must be Numeric`, `fractional must be an Integer`).
284
+
285
+ Consider:
286
+
287
+ - Keeping `ArgumentError` but **documenting all the failure cases** in YARD and README.
288
+ - Or, for one level up in clarity, introduce:
289
+
290
+ ```ruby
291
+ class InvalidMoneyArgument < ArgumentError; end
292
+ ```
293
+
294
+ and use that everywhere. This is a small DX win in large apps.
295
+
296
+ ---
297
+
298
+ ## 7. Documentation structure (DX & competitiveness)
299
+
300
+ Your README is already strong. To push it over the top vs. `money` gem and friends:
301
+
302
+ 1. **Add a “Comparison” section**
303
+
304
+ Not necessarily with names, but structurally:
305
+
306
+ - “What Minting does differently”
307
+ - Always-exact `Rational` amounts.
308
+ - Currency-agnostic zero.
309
+ - Faster formatting (link benchmarks).
310
+ - Separate `minting-rails` companion for clean Rails integration.
311
+
312
+ 2. **Add “Common tasks” cheatsheet**
313
+
314
+ Short, scannable table:
315
+
316
+ | Task | Code |
317
+ |------------------------------|-----------------------------------|
318
+ | New amount | `Mint.money(10, 'USD')` |
319
+ | From cents | `Mint::Money.from_fractional(999, 'USD')` |
320
+ | Change amount immutably | `price.with_amount(15)` (or `mint`) |
321
+ | Parse user input | `Mint.parse('$19.99')` |
322
+ | Serialize to JSON | `price.to_hash` / `price.as_json` |
323
+ | Clamp to range | `price.clamp(0, 100)` |
324
+ | Split / allocate | `ten.split(3)` / `ten.allocate([...])` |
325
+
326
+ This directly optimizes developer experience.
327
+
328
+ 3. **Prominent “Rails” heading**
329
+
330
+ Right now you just mention `minting-rails`. Make it a full section:
331
+
332
+ - How to add gem.
333
+ - Example migration / attribute definition (even if in the other repo, mirror one snippet here).
334
+
335
+ ---
336
+
337
+ ## 8. Minor polish
338
+
339
+ - In `README` “Json serialization” → “JSON serialization”.
340
+ - Make sure `Mint::Money`’s `inspect` and `to_s` are clearly differentiated in docs:
341
+ - `inspect` → developer/debugging (`[USD 10.00]`).
342
+ - `to_s` → user-facing, configurable formatting.
data/doc/class_list.html CHANGED
@@ -46,7 +46,7 @@
46
46
 
47
47
  <ul id="full_list" class="class">
48
48
  <li id="object_" class="odd"><div class="item" style="padding-left:30px"><span class='object_link'><a href="top-level-namespace.html" title="Top Level Namespace (root)">Top Level Namespace</a></span></div></li>
49
- <li id='object_Mint' class='even'><div class='item' style='padding-left:30px'><a tabindex='0' class='toggle' role='button' aria-label='Mint child nodes' aria-expanded='false' aria-controls='object_Mint'></a> <span class='object_link'><a href="Mint.html" title="Mint (module)">Mint</a></span><small class='search_info'>Top Level Namespace</small></div><div aria-labelledby='object_Mint'><ul><li id='object_Mint::Currency' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Mint/Currency.html" title="Mint::Currency (class)">Currency</a></span> &lt; Data<small class='search_info'>Mint</small></div></li><li id='object_Mint::CurrencyRegistry' class='collapsed even'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Mint/CurrencyRegistry.html" title="Mint::CurrencyRegistry (module)">CurrencyRegistry</a></span><small class='search_info'>Mint</small></div></li><li id='object_Mint::Money' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Mint/Money.html" title="Mint::Money (class)">Money</a></span> &lt; Object<small class='search_info'>Mint</small></div></li><li id='object_Mint::UnknownCurrency' class='collapsed even'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Mint/UnknownCurrency.html" title="Mint::UnknownCurrency (class)">UnknownCurrency</a></span> &lt; StandardError<small class='search_info'>Mint</small></div></li></ul></div></li><li id='object_Minting' class='odd'><div class='item' style='padding-left:30px'><span class='object_link'><a href="Minting.html" title="Minting (module)">Minting</a></span><small class='search_info'>Top Level Namespace</small></div></li>
49
+ <li id='object_Mint' class='even'><div class='item' style='padding-left:30px'><a tabindex='0' class='toggle' role='button' aria-label='Mint child nodes' aria-expanded='false' aria-controls='object_Mint'></a> <span class='object_link'><a href="Mint.html" title="Mint (module)">Mint</a></span><small class='search_info'>Top Level Namespace</small></div><div aria-labelledby='object_Mint'><ul><li id='object_Mint::Currency' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Mint/Currency.html" title="Mint::Currency (class)">Currency</a></span> &lt; Data<small class='search_info'>Mint</small></div></li><li id='object_Mint::Money' class='collapsed even'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Mint/Money.html" title="Mint::Money (class)">Money</a></span> &lt; Object<small class='search_info'>Mint</small></div></li><li id='object_Mint::RangeStepPatch' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Mint/RangeStepPatch.html" title="Mint::RangeStepPatch (module)">RangeStepPatch</a></span><small class='search_info'>Mint</small></div></li><li id='object_Mint::Registry' class='collapsed even'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Mint/Registry.html" title="Mint::Registry (module)">Registry</a></span><small class='search_info'>Mint</small></div></li><li id='object_Mint::UnknownCurrency' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Mint/UnknownCurrency.html" title="Mint::UnknownCurrency (class)">UnknownCurrency</a></span> &lt; StandardError<small class='search_info'>Mint</small></div></li></ul></div></li><li id='object_Minting' class='even'><div class='item' style='padding-left:30px'><span class='object_link'><a href="Minting.html" title="Minting (module)">Minting</a></span><small class='search_info'>Top Level Namespace</small></div></li>
50
50
 
51
51
  </ul>
52
52
  </div>