minting 1.6.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 293a64ebe3fd00ed8f6d062b596234550f85ead517b7f8094648dd9502be2f86
4
- data.tar.gz: 3a98bb94ce601340b9cced8b275ee0be3e578d7e3cf781245cdd0fe5ee8038f1
3
+ metadata.gz: 3c50c230bea20784e9834f9f0c2fd9968b73cb94cbd14aa06a0eb4e3a3e41d74
4
+ data.tar.gz: d57f1a4e5a4664c721a286e1ff93eba1558cbf24babeaca6615af755d53663eb
5
5
  SHA512:
6
- metadata.gz: fe587b6b5180c831b1e32092dfe9857ae37516f7fd274215be27e21af1263e99a93725f019e3be96962c5f20c13519326fc2b18b81b88e22aab43dc0d93a9b49
7
- data.tar.gz: b0b8ca0b0dd832668e7b37dfbcca6e3606c673496c8149de90e9d2ffd21a166205e37ae9b455e36b1a71b07729a1d8c5bac074022991d45906ec7c410ec73740
6
+ metadata.gz: 1a4d800520eaa79c57a0cb0111e8b40dcf603fe49fdc697e2b065e3b295c2fe7599285f7af1bc987c0cec3ac13a94a95ef836eb741e86f34986e915e4f904daa
7
+ data.tar.gz: fc8840f398dead653854665cae2afea1f333e973678eac67df401c045af45ee5370a50f365debdd85f42404b80c10cd68c7bba5dc9b086afa2e9c705d323bbe2
data/README.md CHANGED
@@ -212,75 +212,10 @@ Bug reports and pull requests are welcome on GitHub at <https://github.com/gferr
212
212
 
213
213
  1. Fork and create a feature branch
214
214
  2. Run the test suite: `rake`
215
- 3. Run performance suites as needed: `BENCH=true rake bench:performance`
215
+ 3. Run performance suites as needed: `rake bench:performance`
216
216
  4. Open a PR with a clear description and benchmarks if relevant
217
217
 
218
218
 
219
- ## Performance
220
-
221
- This gem includes a performance suite under `test/performance`:
222
-
223
- - Core operations (creation, arithmetic, comparisons)
224
- - Algorithm benchmarks (split, allocate)
225
- - Memory and GC pressure tests
226
- - Competitive benchmarks vs `money` gem
227
-
228
- Run locally:
229
-
230
- ```bash
231
- # All performance suites
232
- BENCH=true rake bench:performance
233
-
234
- # Competitive vs money gem
235
- BENCH=true rake bench:competitive
236
-
237
- # Regression checks
238
- rake bench:regression
239
- ```
240
-
241
- ## Benchmark Summary: Minting vs Money Gem
242
-
243
- Generated by Qwen from the latest benchmark run on Ruby 4.0.1. - 2026-05-30
244
-
245
- ### Key Takeaways
246
-
247
- - **Mint is consistently faster** than the Money gem across all measured operations.
248
- - **Mint is 2.28x faster** in the 50,000-transaction simulation.
249
- - **Mint object creation is 2.76x faster** than `Money.from_amount`.
250
- - In formatting and conversion, Mint is often **10+x **.
251
- - Mint’s performance advantage is especially strong for numeric conversion, string formatting, comparisons, and high-volume transaction loops.
252
-
253
- ### Performance Highlights
254
-
255
- | Category | Mint | Money | Approx. Ratio |
256
- | --- | --- | --- | --- |
257
- | High-volume transactions | 195,412 ops/sec | 85,882 ops/sec | 2.28x faster |
258
- | `Mint.money` creation | 1.14M ops/sec | — | 2.76x faster than `Money.from_amount` |
259
- | `some.dollars` creation | 990k ops/sec | — | 1.15x faster than `Mint.money` |
260
- | `Money.new` creation | — | 715k ops/sec | Mint 1.59x faster |
261
- | `to_f` formatting | 8.8M–9.3M ops/sec | 0.7M ops/sec | ~12x faster |
262
- | `to_d` conversion | 2.1M–2.3M ops/sec | 0.73M–0.79M ops/sec | ~3x faster |
263
- | `to_s` formatting | 300k–420k ops/sec | 109k–132k ops/sec | ~3x faster |
264
- | `inspect` formatting | ~2.6–2.9M ops/sec | ~1.1–1.16M ops/sec | ~2.5x faster |
265
- | `to_json` formatting | ~2.0–2.2M ops/sec | ~110k–126k ops/sec | ~17x faster |
266
- | Currency lookup `Mint.currency('USD')` | 3.82M ops/sec | — | 1.60x faster than `Money::Currency.new` |
267
- | Currency lookup `Money::Currency.find('USD')` | 3.63M ops/sec | 1.67M ops/sec | 2.29x faster |
268
- | Addition | 1.11M ops/sec | 0.37M ops/sec | 3.0x faster |
269
- | Subtraction | 1.11M ops/sec | 0.36M ops/sec | 3.0x faster |
270
- | Multiplication | 1.28M ops/sec | 0.51M ops/sec | 2.5x faster |
271
- | Division | 1.04M ops/sec | 0.37M ops/sec | 2.8x faster |
272
- | Ratio division | 2.94M ops/sec | 0.39M ops/sec | 7.6x faster |
273
- | Comparison (`==`, `<`, `>`) | 2.5M–4.1M ops/sec | 0.35M–0.38M ops/sec | 7x–10x faster |
274
- | Allocation (`Mint.allocate`) | 279k ops/sec | 146k ops/sec | 1.9x faster |
275
- | Split (`Mint.split`) | 215k ops/sec | 85k ops/sec | 3.3x faster |
276
-
277
- ### Commands Used
278
-
279
- ```sh
280
- BENCH=true bundle exec ruby -Ilib:test -r ./test/test_helper.rb test/performance/competitive_performance_benchmark.rb
281
- BENCH=true bundle exec ruby -Ilib:test -r ./test/test_helper.rb test/performance/competitive_memory_benchmark.rb
282
- ```
283
-
284
219
  ## License
285
220
 
286
221
  MIT
data/Rakefile CHANGED
@@ -12,33 +12,29 @@ Rake::TestTask.new(:test) do |t|
12
12
  t.ruby_opts << '-rtest_helper.rb'
13
13
  end
14
14
 
15
- Rake::TestTask.new('bench') do |t|
15
+ Rake::TestTask.new('bench:all') do |t|
16
16
  t.libs = %w[lib test]
17
- t.pattern = 'test/performance/*_benchmark.rb'
17
+ t.pattern = 'test/performance/**/*_benchmark.rb'
18
18
  end
19
19
 
20
- Rake::TestTask.new('bench:parse') do |t|
20
+ Rake::TestTask.new('bench:core') do |t|
21
21
  t.libs = %w[lib test]
22
- t.pattern = 'test/performance/parse_benchmark.rb'
23
- t.ruby_opts << '-r test_helper.rb'
22
+ t.pattern = 'test/performance/core/parse_benchmark.rb'
24
23
  end
25
24
 
26
- Rake::TestTask.new('bench:edge') do |t|
25
+ Rake::TestTask.new('bench:memory') do |t|
27
26
  t.libs = %w[lib test]
28
- t.pattern = 'test/performance/algorithm_benchmark.rb'
29
- t.ruby_opts << '-r test_helper.rb'
27
+ t.pattern = 'test/performance/memory/*_benchmark.rb'
30
28
  end
31
29
 
32
30
  Rake::TestTask.new('bench:regression') do |t|
33
31
  t.libs = %w[lib test]
34
- t.pattern = 'test/performance/regression_benchmark.rb'
35
- t.ruby_opts << '-r test_helper.rb'
32
+ t.pattern = 'test/performance/regression/*_benchmark.rb'
36
33
  end
37
34
 
38
35
  Rake::TestTask.new('bench:competitive') do |t|
39
36
  t.libs = %w[lib test]
40
- t.pattern = 'test/performance/competitive_performance_benchmark.rb'
41
- t.ruby_opts << '-r test_helper.rb'
37
+ t.pattern = 'test/performance/competitive/**/*_benchmark.rb'
42
38
  end
43
39
 
44
40
  RuboCop::RakeTask.new(:cop)
data/doc/agents/AGENTS.md CHANGED
@@ -20,6 +20,6 @@ Project highlights & conventions:
20
20
  - Amounts stored as Rational; prefer rationals or decimal strings for precision
21
21
  - Zero-equality: zeros equal across currencies; non-zero comparisons require same currency
22
22
  - Currency codes must match /^[A-Z_]+$/
23
- - Tests: Minitest; performance benches under test/performance (use BENCH=true)
23
+ - Tests: Minitest; performance benches under test/performance
24
24
 
25
25
  Edit guidance: keep this file minimal and link to existing docs; add repo-specific agent tips here.
@@ -21,9 +21,9 @@ Build, test and lint commands
21
21
  - Run a single test method by name (Minitest -n regexp):
22
22
  - ruby -Ilib:test -r ./test/test_helper.rb test/money/money_test.rb -n /test_creation/
23
23
 
24
- - Performance suites (set BENCH=true as in README):
25
- - BENCH=true rake bench:performance
26
- - BENCH=true rake bench:competitive
24
+ - Performance suites:
25
+ - rake bench:performance
26
+ - rake bench:competitive
27
27
  - rake bench:regression
28
28
 
29
29
  - Linting:
@@ -56,7 +56,7 @@ Key conventions and repo-specific rules
56
56
  - Currency registration: use Mint.register_currency for idempotent registration; Mint.register_currency! raises on duplicates. Codes must match /^[A-Z_]+$/.
57
57
  - Symbol parsing: parser resolves symbols by longest match then currency priority (see Mint.currency_symbols sorting).
58
58
  - Tests: test_helper.rb configures coverage (SimpleCov) and loads minitest; when running tests outside rake, require test_helper (-r ./test/test_helper.rb).
59
- - Benchmarks: set BENCH=true to enable benchmark-heavy tasks; benchmarks use Minitest::Benchmark patterns.
59
+ - Benchmarks: set to enable benchmark-heavy tasks; benchmarks use Minitest::Benchmark patterns.
60
60
  - Formatting: Money.to_s uses Kernel.format patterns; take care with %<amount>f vs %<amount>d depending on desired rounding/formatting.
61
61
 
62
62
  Files and places to check first during edits
@@ -87,7 +87,7 @@ Qwen run on 2026-05-30 and aren't reproducible from CI.
87
87
  **Concrete action:** in CI, run
88
88
 
89
89
  ```sh
90
- BENCH=true rake bench:regression
90
+ ake bench:regression
91
91
  ```
92
92
 
93
93
  and fail the build if any benchmark regresses by more than (e.g.)
@@ -1,22 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mint
4
- # Represents a specific currency unit, identified by ISO 4217 alphabetic code
4
+ # Represents a specific currency unit, identified by ISO 4217 alphabetic code.
5
+ # Currency objects are immutable and define the properties of a monetary unit
6
+ # including its subunit precision, display symbol, and formatting rules.
5
7
  #
6
8
  # @see https://www.iso.org/iso-4217-currency-codes.html
9
+ # @attr_reader code [String] ISO 4217 currency code (e.g., "USD", "EUR")
10
+ # @attr_reader subunit [Integer] Number of decimal places (0 for JPY, 2 for USD, 3 for IQD)
11
+ # @attr_reader symbol [String] Display symbol (e.g., "$", "€", "R$")
12
+ # @attr_reader priority [Integer] Parser precedence for symbol detection
13
+ # @attr_reader country [String, nil] Associated country code
14
+ # @attr_reader name [String, nil] Currency name
15
+ # @attr_reader fractional_multiplier [Integer] 10^subunit, used for fractional conversions
16
+ # @attr_reader minimum_amount [Rational] Smallest representable amount (1/fractional_multiplier)
7
17
  Currency = Data.define(:code, :subunit, :symbol, :priority, :country, :name,
8
- :fractional_multiplier, :minimum_amount) do
18
+ :fractional_multiplier) do
9
19
  def initialize(code:, symbol:, subunit: 0, priority: 0, country: nil, name: nil)
10
20
  subunit = subunit.to_i
11
21
  priority = priority.to_i
12
22
  fractional_multiplier = 10**subunit
13
- minimum_amount = Rational(1, fractional_multiplier)
14
23
  super(code:, subunit:, symbol:, priority:, country:, name:,
15
- fractional_multiplier:, minimum_amount:)
24
+ fractional_multiplier:)
16
25
  end
17
26
 
18
27
  def inspect = "<Currency:(#{code} #{symbol} #{subunit} #{name})>"
19
28
 
29
+ def minimum_amount = Rational(1, fractional_multiplier)
30
+
20
31
  # Normalizes numeric amounts for this currency
21
32
  # 1. Converts to Rational
22
33
  # 2. Rounds to respect currency subunit
@@ -3,7 +3,7 @@
3
3
  # Mint currency registration and factory (public API)
4
4
  module Mint
5
5
  # Unknown currency excpetion
6
- class UnknownCurreny < StandardError
6
+ class UnknownCurrency < StandardError
7
7
  end
8
8
 
9
9
  # Creates a new {Money} instance with the given amount and currency code.
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Mint Money parsing
3
4
  module Mint
4
5
  extend self
5
6
 
@@ -4,9 +4,13 @@ module Mint
4
4
  # Implements the standard Ruby coercion protocol.
5
5
  class Money
6
6
  # Allows {Money} to interact seamlessly as the right-hand operand in Numeric arithmetic.
7
+ # This enables expressions like `5 + money` where `5` is a Numeric and `money` is a Money object.
7
8
  #
8
9
  # @param other [Numeric] the left-hand operand to coerce
9
10
  # @return [Array(CoercedNumber, Money)] coerced operand array
11
+ # @example
12
+ # price = Mint.money(10, 'USD')
13
+ # 5 + price #=> [USD 15.00] (via coercion)
10
14
  def coerce(other)
11
15
  [CoercedNumber.new(other), self]
12
16
  end
@@ -44,9 +44,16 @@ module Mint
44
44
  new(amount, checked_currency)
45
45
  end
46
46
 
47
- # Returns a new Money object with the specified amount, or self if unchanged
48
- # @param new_amount [Numeric] The new amount
49
- # @return [Money] A new Money object or self
47
+ # Returns a new Money object with the specified amount, or self if unchanged.
48
+ # This is the primary method for creating a modified copy of a Money instance
49
+ # while preserving immutability.
50
+ #
51
+ # @param new_amount [Numeric] The new monetary amount
52
+ # @return [Money] A new Money object with the new amount, or self if the amount is unchanged
53
+ # @example
54
+ # price = Mint.money(10.00, 'USD')
55
+ # price.mint(15.00) #=> [USD 15.00]
56
+ # price.mint(10.00) #=> [USD 10.00] (returns self)
50
57
  def mint(new_amount)
51
58
  new_amount = currency.normalize_amount(new_amount)
52
59
  new_amount == amount ? self : Money.new(new_amount, currency)
@@ -9,6 +9,8 @@ module Mint
9
9
  # Converts the monetary amount to a BigDecimal object.
10
10
  #
11
11
  # @return [BigDecimal] the decimal representation of the money amount
12
+ # @example
13
+ # Mint.money(9.99, 'USD').to_d #=> 0.999e1
12
14
  def to_d = amount.to_d 0
13
15
 
14
16
  # Converts the monetary amount to a standard float.
@@ -31,8 +33,17 @@ module Mint
31
33
  # Truncates and converts the monetary amount to an Integer.
32
34
  #
33
35
  # @return [Integer] the integer representation of the money amount
36
+ # @example
37
+ # Mint.money(9.99, 'USD').to_i #=> 9
38
+ # Mint.money(-9.99, 'USD').to_i #=> -9
34
39
  def to_i = amount.to_i
35
40
 
41
+ # Returns a Hash representation of the money instance.
42
+ #
43
+ # @return [Hash] hash with :currency (String) and :amount (String) keys
44
+ # @example
45
+ # Mint.money(134120, 'BRL').to_hash
46
+ # #=> { currency: "BRL", amount: "134120.00" }
36
47
  def to_hash
37
48
  { currency: currency_code, amount: Kernel.format("%0.#{currency.subunit}f", amount) }
38
49
  end
@@ -11,9 +11,19 @@ module Mint
11
11
 
12
12
  # Returns the ISO 3-letter currency code string.
13
13
  #
14
- # @return [String] the ISO currency code
14
+ # @return [String] the ISO currency code (e.g., "USD", "EUR", "BRL")
15
+ # @example
16
+ # Mint.money(100, 'USD').currency_code #=> "USD"
15
17
  def currency_code = currency.code
16
18
 
19
+ # Returns the monetary amount expressed in the currency's smallest unit (fractional units).
20
+ # For example, cents for USD (subunit 2), yen for JPY (subunit 0), fils for IQD (subunit 3).
21
+ #
22
+ # @return [Integer] the amount in fractional units
23
+ # @example
24
+ # Mint.money(1234.56, 'USD').fractional #=> 123456
25
+ # Mint.money(1000, 'JPY').fractional #=> 1000
26
+ # Mint.money(123.456, 'IQD').fractional #=> 123456
17
27
  def fractional = (amount * currency.fractional_multiplier).to_i
18
28
 
19
29
  # Generates a stable hash key for Money instances.
@@ -3,5 +3,5 @@
3
3
  # Root namespace for the Minting library.
4
4
  module Minting
5
5
  # Current version of the Minting gem.
6
- VERSION = '1.6.0'
6
+ VERSION = '1.6.1'
7
7
  end
data/lib/minting.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'minting/mint'
4
- require 'minting/money'
5
4
  require 'minting/version'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minting
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0
4
+ version: 1.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gilson Ferraz