minting 1.3.0 → 1.5.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.
@@ -0,0 +1,335 @@
1
+ # `minting` — Recommendations Roadmap
2
+
3
+ Living document of improvement recommendations for the `minting` gem,
4
+ produced from a full read of `AGENTS.md`, `README.md`, `CHANGELOG.md`,
5
+ and the entire `lib/` + `test/` tree (v1.3.0 baseline).
6
+
7
+ **Conventions**
8
+
9
+ - P0 / P1 / P2 / P3 = priority. P0 = small, high-value, ship now.
10
+ - ✅ = done (committed on `master` or an in-flight branch).
11
+ - 🟡 = partially done.
12
+ - Each item is sized to be a single PR unless marked *[epic]*.
13
+ - File references are relative to repo root.
14
+
15
+ ---
16
+
17
+ ## TL;DR
18
+
19
+ The gem is in **excellent shape**: ~1,400 LoC, 100% line coverage,
20
+ RuboCop + Reek clean, performance bench suite, immutable `Rational`
21
+ internals, 117 built-in currencies. The recommendations below are
22
+ **polish, hardening, and roadmap** — not "fix the broken thing".
23
+
24
+ Two of the highest-leverage moves are already in flight:
25
+
26
+ - ✅ **README-as-spec** — `test_readme_usage` now asserts the
27
+ README's `to_s` / `to_json` / `to_hash` / `split` / `allocate`
28
+ examples instead of just exercising them silently.
29
+ - ✅ **`Money.from_fractional`** — symmetric inverse of `#fractional`
30
+ with full test + README + CHANGELOG coverage.
31
+
32
+ The remaining work is mostly documentation discipline, API ergonomics,
33
+ and the two roadmap items already advertised in the README (I18n +
34
+ exchange rates).
35
+
36
+ ---
37
+
38
+ ## 🔴 P0 — Quick wins, ship now
39
+
40
+ ### P0-1 ✅ Tighten README-as-spec
41
+
42
+ **Status:** Merged in commit `20359dc`.
43
+
44
+ - Turned 14 commented `#=>` examples in `test/minting_test.rb` into
45
+ real `assert_equal` calls.
46
+ - Fixed typo: `conversiob` → `conversion`.
47
+ - Corrected the README's `to_json` example to match the actual output
48
+ (the real string has spaces around the `:`).
49
+ - Softened the "2× faster" headline to point at the Performance
50
+ section where the measured ratios live.
51
+ - Added a one-line `rubocop:disable` on `test_readme_usage` (the spec
52
+ has to stay monolithic to serve as a spec).
53
+
54
+ **Why it mattered:** the test was passing while the README silently
55
+ drifted. Now any example change in the README either stays correct or
56
+ fails CI.
57
+
58
+ ### P0-2 🟡 Auto-require `bigdecimal` and clean up `to_d`
59
+
60
+ **Status:** Partial — the dependency was added in commit `33b2c01`
61
+ (`s.add_dependency 'bigdecimal', '>= 4.0'` in `minting.gemspec`).
62
+ The runtime guard in `lib/minting/money/conversion.rb` (lines 10–14)
63
+ is now redundant:
64
+
65
+ ```ruby
66
+ def to_d
67
+ raise NoMethodError, 'decimal gem required' unless defined?(BigDecimal)
68
+ amount.to_d 0
69
+ end
70
+ ```
71
+
72
+ **Follow-up:** delete the `raise NoMethodError` line. It's dead code
73
+ once `bigdecimal` is a hard dependency. One-line PR.
74
+
75
+ ### P0-3 ✅ Fix CHANGELOG typos
76
+
77
+ **Status:** Done in commit `20359dc` — "is no private" →
78
+ "is now private" in the v1.3.0 entry. Watch for new typos as
79
+ items are added under `[Unreleased]`.
80
+
81
+ ### P0-4 Add a CI gate on the marketing claims
82
+
83
+ The README's "**2× faster**" / "**10×+ for formatting**" headline and
84
+ the table at the bottom (lines 213–247) are generated from a single
85
+ Qwen run on 2026-05-30 and aren't reproducible from CI.
86
+
87
+ **Concrete action:** in CI, run
88
+
89
+ ```sh
90
+ BENCH=true rake bench:regression
91
+ ```
92
+
93
+ and fail the build if any benchmark regresses by more than (e.g.)
94
+ 20% vs the stored baseline in `pkg/bench_baseline.json`. The baseline
95
+ file is optional but easy to maintain.
96
+
97
+ This makes the table *defensible* rather than aspirational.
98
+
99
+ ---
100
+
101
+ ## 🟠 P1 — Correctness, clarity, small ergonomics
102
+
103
+ ### P1-1 🟡 `Money.from_fractional(integer, currency)`
104
+
105
+ **Status:** Merged on branch `feature/money-from-fractional` (commit
106
+ `150a2d0`).
107
+
108
+ - Inverse of `#fractional`. Uses `Rational(n, fractional_multiplier)`
109
+ so subunit-0 currencies work without a special case.
110
+ - Integer-only contract — rejects `Float` / `String` / `Rational` with
111
+ `ArgumentError` to preserve the exactness guarantee.
112
+ - Accepts `String`, `Symbol`, or `Currency` for the currency arg,
113
+ matching `Mint.money` / `Money.create`.
114
+ - 4 new tests + 3 new README-spec asserts; 100% coverage maintained.
115
+
116
+ **To do** Add benchmark tests
117
+
118
+ ### P1-2 Add `Money#clamp(min, max)`, `Money#min(other)`, `Money#max(other)`
119
+
120
+ `Comparable` is already mixed in (`lib/minting/money/comparable.rb`),
121
+ so `min` and `max` work *implicitly* through the `<=>` machinery —
122
+ but only when both operands are the same currency. A first-class
123
+ `#clamp` would be a one-liner and very useful in domain code
124
+ (pricing floors/ceilings, exchange-rate bands, etc.).
125
+
126
+ ```ruby
127
+ # Sketch
128
+ def clamp(min, max)
129
+ raise ArgumentError, 'min/max must share currency' unless
130
+ same_currency?(min) && same_currency?(max)
131
+ return min if self < min
132
+ return max if self > max
133
+ self
134
+ end
135
+ ```
136
+
137
+ Add tests for: in-range, below-min, above-max, equal-to-bound,
138
+ non-matching-currency error.
139
+
140
+ ### P1-3 Improve `to_s` format-hash handling
141
+
142
+ `lib/minting/money/formatting.rb` accepts `format:` as either a
143
+ `String` or a `Hash` with `:positive` / `:negative` / `:zero` keys.
144
+ The current code:
145
+
146
+ - Falls through to `'%<symbol>s%<amount>f'` if `:positive` is missing
147
+ and the input is non-zero non-negative (line 58).
148
+ - Has no test coverage for the hash form at all.
149
+
150
+ **Action:** add explicit tests for:
151
+
152
+ - `{ positive: '...', negative: '...', zero: '...' }`
153
+ - `{ positive: '...' }` alone (default for neg/zero)
154
+ - Mixing hash and `decimal:` / `thousand:` kwargs
155
+
156
+ ### P1-4 Document and harden registry thread-safety
157
+
158
+ `lib/minting/mint/registry.rb` memoizes `@currencies`,
159
+ `@currency_symbols`, and `@zero` with `||=`. In a multi-threaded
160
+ server (Rails, Sidekiq, Puma) the first call to `Mint.money` from
161
+ two threads can:
162
+
163
+ - Both call `YAML.load_file` and `register` the same currencies.
164
+ - Both call `@currency_symbols ||= ...` and produce a non-frozen copy
165
+ that diverges.
166
+
167
+ **Action:** either
168
+
169
+ 1. Wrap the lazy initializers in `Mutex#synchronize` (3 places), or
170
+ 2. Switch to `Concurrent::Map` (no new dep), or
171
+ 3. Eager-load in a Railtie/loaded hook and treat the registry as
172
+ read-only after boot.
173
+
174
+ Option 3 is the most pragmatic. Document the contract.
175
+
176
+ ### P1-5 Make `Mint.currencies` immutable
177
+
178
+ `currencies` returns the live `@currencies` hash. A caller can
179
+ `currencies.delete('USD')` and break invariants. Either:
180
+
181
+ - Return a frozen copy (`@currencies.dup.freeze`), or
182
+ - Document loudly that the returned hash must be treated read-only.
183
+
184
+ ### P1-6 Improve `coercion.rb` — add `**` and a clean `==`
185
+
186
+ `lib/minting/money/coercion.rb` defines `CoercedNumber` (used by
187
+ `Money#coerce`) with `+`, `-`, `*`, `/`, `<=>`, but no `**` or `==`.
188
+
189
+ ```ruby
190
+ 2 ** 0.dollars #=> 1 (works)
191
+ 2 ** 5.dollars #=> NoMethodError (silent failure)
192
+ ```
193
+
194
+ `==` falls through to `Comparable`, which raises `ArgumentError`, but
195
+ a custom `==` on `CoercedNumber` would give a more specific error
196
+ message.
197
+
198
+ **Action:** add `**` and `==` to `CoercedNumber`. Tests in
199
+ `test/money/money_coercion_test.rb` (or wherever they live).
200
+
201
+ ### P1-7 Add `inspect` round-trip test
202
+
203
+ `Money#inspect` is implemented in `lib/minting/money/money.rb:79`
204
+ as `"[#{currency_code} %0.#{currency.subunit}f]"` — e.g.
205
+ `[USD 9.99]`. The existing parser (`lib/minting/money/parse.rb`)
206
+ can almost read this back, but a clean round-trip would be a nice
207
+ property test:
208
+
209
+ ```ruby
210
+ m = Mint.money(9.99, 'USD')
211
+ assert_equal m, Mint::Money.parse(m.inspect.delete_prefix('[').delete_suffix(']'))
212
+ ```
213
+
214
+ Make it a one-liner or a property-based test with 10–20 random
215
+ amounts. Cheap, catches regressions in either direction.
216
+
217
+ ---
218
+
219
+ ## 🟡 P2 — Roadmap items already advertised in README
220
+
221
+ ### P2-1 I18n / locale-aware formatting *[epic]*
222
+
223
+ Foundation is already there: `to_s(decimal: ',', thousand: '.')`
224
+ works. What's missing is locale-keyed defaults:
225
+
226
+ ```ruby
227
+ Mint.money(9.99, 'USD').to_s(locale: :'pt-BR') #=> "US$ 9,99"
228
+ Mint.money(9.99, 'USD').to_s(locale: :'fr') #=> "9,99 $US"
229
+ ```
230
+
231
+ **Action plan (multi-PR):**
232
+
233
+ 1. Add `Mint::Locale` value object + a small `locales.yaml` (or
234
+ build off `currencies.yaml` since most formatting info is per-
235
+ currency already).
236
+ 2. Add `I18n` integration behind a `require 'minting/i18n'` opt-in.
237
+ 3. Default to "no locale" so existing code is unaffected.
238
+
239
+ ### P2-2 Exchange rates *[epic]*
240
+
241
+ Mentioned in the README Roadmap. The hard part isn't the math, it's
242
+ the rate source. Suggested shape:
243
+
244
+ ```ruby
245
+ Mint.bank = Mint::Bank::MemoryStore.new(
246
+ 'USD' => { 'EUR' => 0.92, 'JPY' => 149.50 }
247
+ )
248
+ Mint.money(10, 'USD').exchange('EUR') #=> [EUR 9.20]
249
+ ```
250
+
251
+ Multi-PR epic:
252
+
253
+ 1. `Mint::Bank` interface with `#exchange(money, target)`.
254
+ 2. `Mint::Bank::MemoryStore` (in-memory, good for tests).
255
+ 3. `Mint::Bank::ECB` or `Mint::Bank::OpenExchangeRates` (network).
256
+ 4. Update `minting-rails` to expose bank config in the Railtie.
257
+
258
+ ### P2-3 Reek / RuboCop tightening
259
+
260
+ `.rubocop_todo.yml` still has 9 pre-existing offenses, all in
261
+ `test/performance/*` and `test/mint_benchmark.rb`. They're
262
+ "`Metrics::BlockLength`" / "`Metrics::MethodLength`" — fixable by
263
+ extracting helper methods or by scoping the cop to non-bench code.
264
+
265
+ ---
266
+
267
+ ## 🟢 P3 — Polish
268
+
269
+ ### P3-1 ✅ Move AI-generated docs to `doc/agents/`
270
+
271
+ Done in commit `ff08578`. Consider also moving `pkg/` artifacts to
272
+ a release script that builds on tag rather than committing pre-built
273
+ gems.
274
+
275
+ ### P3-2 `Gemfile.lock` policy
276
+
277
+ `Gemfile.lock` is currently committed. The project is a *gem*, not
278
+ an app, so the conventional choice is to *not* commit it (let
279
+ consumers' Bundler resolve). Or, if you prefer reproducibility for
280
+ CI, commit it and document the choice in `CONTRIBUTING.md`.
281
+
282
+ ### P3-3 Add `SECURITY.md` and `CODE_OF_CONDUCT.md`
283
+
284
+ Standard hygiene for a public Ruby gem. One PR each, copy-paste
285
+ from the GitHub templates.
286
+
287
+ ### P3-4 Document `Money#each_currency` / `Mint.currencies.each_value`
288
+
289
+ The README's "Features" list doesn't mention that you can iterate
290
+ registered currencies. Trivial doc addition; potential ergonomic
291
+ win for tooling authors.
292
+
293
+ ### P3-5 Decide on `pkg/` retention
294
+
295
+ `pkg/minting-*.gem` is in `.gitignore`, but old builds may still
296
+ sit there. Run `rake clobber_pkg` (if it exists) or just `rm -rf
297
+ pkg/*` once. Then add a `git clean -nd pkg` check to `Rakefile` if
298
+ you want belt-and-braces.
299
+
300
+ ---
301
+
302
+ ## Suggested next PR (after `from_fractional` merges)
303
+
304
+ **Title:** *Delete dead `to_d` guard, document `bigdecimal` as a hard dependency*
305
+
306
+ **Scope:**
307
+
308
+ - `lib/minting/money/conversion.rb`: remove
309
+ `raise NoMethodError, 'decimal gem required' unless defined?(BigDecimal)`.
310
+ - `CHANGELOG.md`: add a one-liner under `[Unreleased]` → `### Other`.
311
+ - `test/money/money_conversion_test.rb`: add a sanity test that
312
+ `to_d` returns a `BigDecimal` (already covered indirectly, but
313
+ make it explicit).
314
+
315
+ One-line source change, one-line test, one-line changelog. ~10 min.
316
+
317
+ ---
318
+
319
+ ## Metrics to watch going forward
320
+
321
+ | Metric | Current | Target |
322
+ |---|---|---|
323
+ | Line coverage | 100% (284/284) | ≥ 100% — don't regress |
324
+ | Test count | 80 runs, 342 assertions | Grow with each new method |
325
+ | `to_s` Hash format coverage | 0 tests | ≥ 6 (P1-3) |
326
+ | Bench variance vs baseline | unmeasured | ≤ 20% (P0-4) |
327
+ | Public API methods with YARD | ~all | 100% (already achieved) |
328
+ | Open `rubocop_todo` offenses | 9 in `test/performance/*` | 0 (P2-3) |
329
+ | `Gemfile.lock` policy | committed | documented (P3-2) |
330
+ | `to_d` runtime guard | present but dead | removed (suggested next PR) |
331
+
332
+ ---
333
+
334
+ *Last updated alongside PR #2 (`Money.from_fractional`). Refresh
335
+ after each release.*
@@ -0,0 +1,332 @@
1
+ # Rubocop Issues Assessment: Minting Gem
2
+
3
+ **Date:** 2026-06-03
4
+ **Total Issues Found:** 93 (88 conventions, 5 warnings)
5
+ **Files Analyzed:** 37
6
+ **Files with Issues:** 23
7
+
8
+ ---
9
+
10
+ ## Executive Summary
11
+
12
+ The minting gem's rubocop analysis reveals **93 total violations**, primarily concentrated in test code rather than production code. The violations fall into two broad categories:
13
+
14
+ 1. **Production Code Issues (9 violations):** Focused on architectural patterns, complexity, and parameter design
15
+ 2. **Test Code Issues (84 violations):** Mostly style/complexity issues in test assertions
16
+
17
+ The good news: **Most issues are low-risk conventions** affecting code style and test organization, not correctness or performance. The critical items requiring attention are marked with ⚠️.
18
+
19
+ ---
20
+
21
+ ## Issues by Category
22
+
23
+ ### 🚨 Critical Issues (Must Address)
24
+
25
+ #### 1. **Lint/ItWithoutArgumentsInBlock** (Warning)
26
+ - **Severity:** Warning (Ruby 3.4 compatibility)
27
+ - **Location:** `lib/minting/money/allocation.rb:56`
28
+ - **Issue:** `it` calls without arguments will have different semantics in Ruby 3.4
29
+ - **Action:** Use `it()` or `self.it` explicitly
30
+ - **Status:** Actionable, simple fix
31
+
32
+ #### 2. **Lint/EmptyWhen** (Warnings) - 4 occurrences
33
+ - **Severity:** Warning
34
+ - **Locations:** `lib/minting/money/money.rb` (lines 136, 137, 147, 148)
35
+ - **Issue:** Empty `when` branches in case statement (likely fall-through logic)
36
+ - **Action:** Either add `# rubocop:disable` comment if intentional, or refactor the case logic
37
+ - **Status:** Needs clarification on intent
38
+
39
+ ---
40
+
41
+ ### ⚠️ High Priority - Production Code (Should Address Soon)
42
+
43
+ #### 1. **ThreadSafety/ClassInstanceVariable** - 4 occurrences
44
+ - **Severity:** Convention
45
+ - **Location:** `lib/minting/mint/registry.rb` (lines 21, 64, 72, 80)
46
+ - **Issue:** Class instance variables used in registry pattern for caching
47
+ - **Current Design:** Registry caches currencies and symbol mappings using `@@` class variables
48
+ - **Trade-offs:**
49
+ - ✅ Simple, works well for single-threaded scenarios
50
+ - ❌ Not thread-safe; could cause race conditions if accessed concurrently
51
+ - ❌ Rubocop flags this as an anti-pattern for multi-threaded apps
52
+ - **Recommendation:** Current design is acceptable for a gem that caches static data (currencies don't change at runtime). Consider adding thread-safety if multi-threaded use becomes a requirement.
53
+ - **Effort:** Medium (if refactored, would use class-level synchronization or lazy initialization)
54
+
55
+ #### 2. **Metrics/ParameterLists** - 1 occurrence
56
+ - **Severity:** Convention
57
+ - **Location:** `lib/minting/mint/currency.rb:23`
58
+ - **Issue:** Currency constructor has 6 parameters (limit: 5)
59
+ - **Current Signature:** `initialize(code:, subunit:, symbol: nil, priority: 0, minimum_amount: nil, ...)`
60
+ - **Recommendation:** Consider if all parameters are essential. Could use keyword arguments or a builder pattern.
61
+ - **Effort:** Low-to-Medium (API surface change)
62
+
63
+ #### 3. **Metrics/AbcSize (Low Severity)** - 3 occurrences in production code
64
+ - **Severity:** Convention
65
+ - **Locations:**
66
+ - `lib/minting/money/allocation.rb:15` - `allocate` method (18.68/17)
67
+ - `lib/minting/money/formatting.rb:81` - `format_amount` method (22.65/17)
68
+ - `lib/minting/money/money.rb:122` - `clamp` method (20.05/17)
69
+ - **Issue:** These methods slightly exceed the complexity threshold, but are core business logic
70
+ - **Recommendation:** These are acceptable as-is; splitting would reduce clarity. Monitor if they grow further.
71
+ - **Effort:** Low priority; monitor only
72
+
73
+ #### 4. **Metrics/CyclomaticComplexity & Metrics/PerceivedComplexity** - 3 occurrences
74
+ - **Severity:** Convention
75
+ - **Locations:**
76
+ - `lib/minting/money/money.rb:122` - `clamp` method
77
+ - `lib/minting/money/parse.rb:43` - `normalize_separators` method
78
+ - `test/performance/benchmark_helper.rb:71` - test utility
79
+ - **Issue:** Multiple conditional branches increase cyclomatic complexity
80
+ - **Recommendation:** These methods handle multi-condition business logic; acceptable for now. Refactor only if maintainability becomes an issue.
81
+ - **Effort:** Low priority
82
+
83
+ ---
84
+
85
+ ### 📊 Medium Priority - Test Code Issues
86
+
87
+ #### 1. **Minitest/MultipleAssertions** - 34 occurrences (37% of total issues)
88
+ - **Severity:** Convention
89
+ - **Affected Files:** Most test files
90
+ - **Issue:** Test methods exceed 3 assertions (max threshold)
91
+ - **Examples:**
92
+ - `test/mint_test.rb:21` - 10 assertions
93
+ - `test/money/money_allocation_test.rb:26` - 12 assertions
94
+ - `test/money/money_arithmetics_test.rb:6` - 7 assertions
95
+ - **Rationale in Minting:** This is a measurement gem where comprehensive verification is necessary. Each operation's correctness depends on multiple properties:
96
+ - Amount correctness
97
+ - Currency preservation
98
+ - Rounding behavior
99
+ - Edge cases (zero, negative, large values)
100
+ - **Recommendation:** Consider documenting this intentional choice in `.rubocop.yml` or refactoring into helper assertions that reduce per-test assertion count while maintaining test coverage.
101
+ - **Effort:** Medium-to-High (would require restructuring test organization)
102
+
103
+ #### 2. **Metrics/AbcSize (Test Context)** - 22 occurrences
104
+ - **Severity:** Convention
105
+ - **Affected Files:** Primarily benchmark and test files
106
+ - **Issue:** Complex test methods with many branches
107
+ - **Recommendation:** These are acceptable in test code; they reflect the complexity of the scenarios being tested. Low priority.
108
+ - **Effort:** Low priority
109
+
110
+ #### 3. **Metrics/ClassLength** - 4 occurrences
111
+ - **Severity:** Convention
112
+ - **Locations:**
113
+ - `test/money/money_format_test.rb:3` - 232 lines
114
+ - `test/performance/algorithm_benchmark.rb:5` - 172 lines
115
+ - `test/performance/competitive_performance_benchmark.rb:5` - 182 lines
116
+ - `test/performance/regression_benchmark.rb:5` - 151 lines
117
+ - **Issue:** Test classes exceed 100-line limit
118
+ - **Recommendation:** These are large test suites for good reason (comprehensive coverage). Could be split into smaller classes, but test organization should prioritize clarity over line counts.
119
+ - **Effort:** Medium (if refactored)
120
+
121
+ #### 4. **Metrics/BlockLength** - 3 occurrences
122
+ - **Severity:** Convention
123
+ - **Affected Files:** Performance benchmark files
124
+ - **Issue:** Block length exceeds 25 lines
125
+ - **Recommendation:** Low priority for benchmarks; these require comprehensive setup and measurement
126
+ - **Effort:** Low priority
127
+
128
+ #### 5. **Metrics/ModuleLength** - 1 occurrence
129
+ - **Severity:** Convention
130
+ - **Location:** `test/performance/benchmark_helper.rb:11` (105 lines)
131
+ - **Issue:** Module slightly exceeds 100-line limit
132
+ - **Recommendation:** This is a utility module for benchmarks; acceptable as-is
133
+ - **Effort:** Low priority
134
+
135
+ #### 6. **Performance/CollectionLiteralInLoop** - 1 occurrence
136
+ - **Severity:** Convention
137
+ - **Location:** `test/performance/algorithm_benchmark.rb:19`
138
+ - **Issue:** Array literal created in loop
139
+ - **Recommendation:** Extract to constant or variable outside loop for performance
140
+ - **Effort:** Low
141
+
142
+ ---
143
+
144
+ ## Issue Distribution Analysis
145
+
146
+ ### By Component
147
+ ```
148
+ Production Code Issues:
149
+ ├── lib/minting/mint/currency.rb: 1
150
+ ├── lib/minting/mint/registry.rb: 4 (thread safety)
151
+ ├── lib/minting/money/allocation.rb: 2
152
+ ├── lib/minting/money/formatting.rb: 1
153
+ ├── lib/minting/money/money.rb: 7 (complexity)
154
+ └── lib/minting/money/parse.rb: 2
155
+
156
+ Test/Benchmark Issues:
157
+ ├── Unit tests (test/): 43
158
+ └── Benchmarks (test/performance/): 42
159
+ ```
160
+
161
+ ### By Severity
162
+ - **Warnings:** 5 (Ruby 3.4 compatibility + empty when branches)
163
+ - **Conventions:** 88 (style, complexity, structure)
164
+
165
+ ### By Cop Type
166
+ ```
167
+ Minitest/MultipleAssertions 34 (37%) - Test assertion counts
168
+ Metrics/AbcSize 33 (35%) - Complexity of methods
169
+ ThreadSafety/ClassInstanceVar 4 (4%) - Registry caching pattern
170
+ Lint/EmptyWhen 4 (4%) - Incomplete case logic
171
+ Metrics/ClassLength 4 (4%) - Large test classes
172
+ Metrics/CyclomaticComplexity 3 (3%) - Branch complexity
173
+ Metrics/PerceivedComplexity 3 (3%) - Subjective complexity
174
+ Metrics/BlockLength 3 (3%) - Large blocks
175
+ Others 5 (5%) - Various minor issues
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Rubocop Configuration
181
+
182
+ **Current thresholds** (in `.rubocop.yml`):
183
+ - Line length: 120 characters
184
+ - Method length: 30 lines
185
+ - Default complexity thresholds apply
186
+
187
+ **Active plugins:**
188
+ - rubocop-minitest (for test-specific rules)
189
+ - rubocop-packaging (gem packaging rules)
190
+ - rubocop-performance (performance optimizations)
191
+ - rubocop-rake (Rakefile rules)
192
+ - rubocop-thread_safety (concurrency patterns)
193
+
194
+ ---
195
+
196
+ ## Recommendations by Priority
197
+
198
+ ### Priority 1: Fix Immediately
199
+ - [ ] Fix `Lint/ItWithoutArgumentsInBlock` in `lib/minting/money/allocation.rb:56`
200
+ - **Effort:** < 5 minutes
201
+ - **Impact:** Ensures Ruby 3.4+ compatibility
202
+
203
+ - [ ] Clarify or fix `Lint/EmptyWhen` in `lib/minting/money/money.rb` (4 lines)
204
+ - **Effort:** < 10 minutes
205
+ - **Impact:** Clarifies intent of case statement logic
206
+
207
+ ### Priority 2: Plan Refactoring
208
+ - [ ] Evaluate thread-safety requirements for `Registry` class
209
+ - **Effort:** Medium
210
+ - **Impact:** Determines if class variable pattern is acceptable
211
+ - **Timeline:** Consider for v2.0 or if multi-threaded use is required
212
+
213
+ - [ ] Decide on parameter design for `Currency` (6 parameters vs. 5 max)
214
+ - **Effort:** Low
215
+ - **Impact:** Could simplify API
216
+ - **Timeline:** Can defer if current design is stable
217
+
218
+ ### Priority 3: Test Organization (Optional)
219
+ - [ ] Consider refactoring test assertions to reduce `Minitest/MultipleAssertions` violations
220
+ - **Effort:** High
221
+ - **Impact:** Improves test organization, but current approach is acceptable
222
+ - **Timeline:** Only if test maintainability becomes an issue
223
+
224
+ - [ ] Consider splitting large test/benchmark classes (`money_format_test.rb`, benchmark files)
225
+ - **Effort:** Medium-High
226
+ - **Impact:** Improves file organization
227
+ - **Timeline:** Optional; not critical
228
+
229
+ ---
230
+
231
+ ## Configuration Adjustments
232
+
233
+ ### Option A: Accept Current Baseline (Recommended)
234
+ Keep current `.rubocop.yml` configuration. The violations represent:
235
+ - Acceptable complexity trade-offs for a measurement gem
236
+ - Best practices for test organization given domain requirements
237
+ - Intentional design choices for performance and clarity
238
+
239
+ ### Option B: Relax Thresholds
240
+ Add exceptions to `.rubocop.yml` for known violations:
241
+ ```yaml
242
+ Minitest/MultipleAssertions:
243
+ Enabled: false # Disable for this gem; multi-assertion tests are necessary
244
+
245
+ Metrics/AbcSize:
246
+ Exclude:
247
+ - test/performance/**/*
248
+ ```
249
+
250
+ ### Option C: Strict Compliance (Not Recommended)
251
+ Refactor to meet all thresholds. Would require:
252
+ - Splitting large test classes (disruptive to test organization)
253
+ - Reducing assertions per test (reduces coverage clarity)
254
+ - Architectural changes to reduce method complexity
255
+
256
+ ---
257
+
258
+ ## Summary Table
259
+
260
+ | Category | Count | Severity | Effort | Recommendation |
261
+ |----------|-------|----------|--------|-----------------|
262
+ | Ruby 3.4 Compatibility | 1 | High | Low | **Fix immediately** |
263
+ | Empty When Branches | 4 | Medium | Low | **Fix/clarify** |
264
+ | Thread Safety | 4 | Medium | Medium | Monitor & plan |
265
+ | Parameter Lists | 1 | Low | Low | Optional refactor |
266
+ | Method Complexity | 8 | Low | Medium | Accept as-is |
267
+ | Test Assertions | 34 | Low | High | Accept/document intent |
268
+ | Test Organization | 37 | Low | High | Optional refactor |
269
+
270
+ ---
271
+
272
+ ## Next Steps
273
+
274
+ 1. **Immediate (This Sprint):**
275
+ - Fix `Lint/ItWithoutArgumentsInBlock` warning
276
+ - Address `Lint/EmptyWhen` warnings (fix or document intent)
277
+ - Run full test suite to verify fixes don't break anything
278
+
279
+ 2. **Short-term (This Quarter):**
280
+ - Evaluate thread-safety requirements
281
+ - Decide on currency parameter design
282
+ - Document rationale for test assertion counts
283
+
284
+ 3. **Long-term (v2.0 Planning):**
285
+ - Consider architectural improvements to reduce complexity
286
+ - Evaluate thread-safety refactoring if multi-threaded use becomes a requirement
287
+ - Plan test organization improvements if maintainability becomes an issue
288
+
289
+ ---
290
+
291
+ ## Appendix: Full Issue List
292
+
293
+ ### Production Code - All Issues
294
+
295
+ **lib/minting/mint/currency.rb**
296
+ - Line 23: `Metrics/ParameterLists` - 6 parameters vs. 5 max
297
+
298
+ **lib/minting/mint/registry.rb**
299
+ - Line 21, 64, 72, 80: `ThreadSafety/ClassInstanceVariable` - 4 violations (class instance variables for caching)
300
+
301
+ **lib/minting/money/allocation.rb**
302
+ - Line 15: `Metrics/AbcSize` - allocate method (18.68/17)
303
+ - Line 56: `Lint/ItWithoutArgumentsInBlock` - Warning for Ruby 3.4
304
+
305
+ **lib/minting/money/formatting.rb**
306
+ - Line 81: `Metrics/AbcSize` - format_amount method (22.65/17)
307
+
308
+ **lib/minting/money/money.rb**
309
+ - Line 122: `Metrics/AbcSize` - clamp method (20.05/17)
310
+ - Line 122: `Metrics/CyclomaticComplexity` - clamp method (11/7)
311
+ - Line 122: `Metrics/PerceivedComplexity` - clamp method (10/8)
312
+ - Lines 136, 137, 147, 148: `Lint/EmptyWhen` - 4 empty when branches
313
+
314
+ **lib/minting/money/parse.rb**
315
+ - Line 43: `Metrics/CyclomaticComplexity` - normalize_separators (8/7)
316
+ - Line 43: `Metrics/PerceivedComplexity` - normalize_separators (9/8)
317
+
318
+ ### Test Code - Summary
319
+ - 34 `Minitest/MultipleAssertions` violations across test files
320
+ - 22 `Metrics/AbcSize` violations in tests
321
+ - 4 `Metrics/ClassLength` violations
322
+ - 3 `Metrics/BlockLength` violations in benchmarks
323
+ - 1 `Metrics/ModuleLength` in benchmark_helper.rb
324
+ - 1 `Performance/CollectionLiteralInLoop` in algorithm_benchmark.rb
325
+
326
+ See detailed output above for specific line numbers and messages.
327
+
328
+ ---
329
+
330
+ **Generated:** 2026-06-03
331
+ **Rubocop Version:** 1.87.0
332
+ **Ruby Version:** 4.0.1