minting 1.1.1 → 1.2.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 +48 -5
- data/Rakefile +11 -8
- data/lib/minting/mint/currency.rb +6 -2
- data/lib/minting/mint/registry.rb +5 -2
- data/lib/minting/money/allocation.rb +2 -1
- data/lib/minting/money/arithmetics.rb +1 -1
- data/lib/minting/money/conversion.rb +5 -3
- data/lib/minting/money/money.rb +3 -4
- data/lib/minting/money/parse.rb +1 -1
- data/lib/minting/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b05d1cbbf6c714102f38f165effba77384e4fc5cebf691d4b9ab75082c22b992
|
|
4
|
+
data.tar.gz: 2d5bb5aeb6e527a2d4419bcca35d79c426709797975e9ec38217558d875ea9b8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ce7311d97cbe60c2f71ba5f65d1313704084eebae842e012c2cd857230055ef56007575e8f89887f8ae157c004d205e43370ada8d2fc2b5f3ea00b7c21c8017a
|
|
7
|
+
data.tar.gz: e4903099bb997667472ddc0c26ff07b3894bb9fa5122221c94045d84f2699e28d203646429e341a8119177a07212991f04d36c5806a12c8388766a3a8aebb6eb
|
data/README.md
CHANGED
|
@@ -91,6 +91,11 @@ price_in_euros.to_s(format: '%<symbol>2s%<amount>+10f') #=> " € +12.34"
|
|
|
91
91
|
|
|
92
92
|
price.to_json # => "{ "currency": "USD", "amount": "9.99" }"
|
|
93
93
|
|
|
94
|
+
# Hash conversion
|
|
95
|
+
|
|
96
|
+
price.to_hash #=> {currency: "USD", amount: "9.99"}
|
|
97
|
+
|
|
98
|
+
|
|
94
99
|
# Proportional allocation and split
|
|
95
100
|
|
|
96
101
|
ten.split(3) #=> [[USD 3.34], [USD 3.33], [USD 3.33]]
|
|
@@ -192,11 +197,6 @@ This gem includes a performance suite under `test/performance`:
|
|
|
192
197
|
- Memory and GC pressure tests
|
|
193
198
|
- Competitive benchmarks vs `money` gem
|
|
194
199
|
|
|
195
|
-
On a typical machine, reference numbers are:
|
|
196
|
-
|
|
197
|
-
- Money creation: ~1.6M ops/sec
|
|
198
|
-
- Addition: ~1.7M ops/sec
|
|
199
|
-
|
|
200
200
|
Run locally:
|
|
201
201
|
|
|
202
202
|
```bash
|
|
@@ -210,6 +210,49 @@ BENCH=true rake bench:competitive
|
|
|
210
210
|
rake bench:regression
|
|
211
211
|
```
|
|
212
212
|
|
|
213
|
+
## Benchmark Summary: Minting vs Money Gem
|
|
214
|
+
|
|
215
|
+
Generated by Qwen from the latest benchmark run on Ruby 4.0.1. - 2026-05-30
|
|
216
|
+
|
|
217
|
+
### Key Takeaways
|
|
218
|
+
|
|
219
|
+
- **Mint is consistently faster** than the Money gem across all measured operations.
|
|
220
|
+
- **Mint is 2.28x faster** in the 50,000-transaction simulation.
|
|
221
|
+
- **Mint object creation is 2.76x faster** than `Money.from_amount`.
|
|
222
|
+
- In formatting and conversion, Mint is often **10+x **.
|
|
223
|
+
- Mint’s performance advantage is especially strong for numeric conversion, string formatting, comparisons, and high-volume transaction loops.
|
|
224
|
+
|
|
225
|
+
### Performance Highlights
|
|
226
|
+
|
|
227
|
+
| Category | Mint | Money | Approx. Ratio |
|
|
228
|
+
| --- | --- | --- | --- |
|
|
229
|
+
| High-volume transactions | 195,412 ops/sec | 85,882 ops/sec | 2.28x faster |
|
|
230
|
+
| `Mint.money` creation | 1.14M ops/sec | — | 2.76x faster than `Money.from_amount` |
|
|
231
|
+
| `some.dollars` creation | 990k ops/sec | — | 1.15x faster than `Mint.money` |
|
|
232
|
+
| `Money.new` creation | — | 715k ops/sec | Mint 1.59x faster |
|
|
233
|
+
| `to_f` formatting | 8.8M–9.3M ops/sec | 0.7M ops/sec | ~12x faster |
|
|
234
|
+
| `to_d` conversion | 2.1M–2.3M ops/sec | 0.73M–0.79M ops/sec | ~3x faster |
|
|
235
|
+
| `to_s` formatting | 300k–420k ops/sec | 109k–132k ops/sec | ~3x faster |
|
|
236
|
+
| `inspect` formatting | ~2.6–2.9M ops/sec | ~1.1–1.16M ops/sec | ~2.5x faster |
|
|
237
|
+
| `to_json` formatting | ~2.0–2.2M ops/sec | ~110k–126k ops/sec | ~17x faster |
|
|
238
|
+
| Currency lookup `Mint.currency('USD')` | 3.82M ops/sec | — | 1.60x faster than `Money::Currency.new` |
|
|
239
|
+
| Currency lookup `Money::Currency.find('USD')` | 3.63M ops/sec | 1.67M ops/sec | 2.29x faster |
|
|
240
|
+
| Addition | 1.11M ops/sec | 0.37M ops/sec | 3.0x faster |
|
|
241
|
+
| Subtraction | 1.11M ops/sec | 0.36M ops/sec | 3.0x faster |
|
|
242
|
+
| Multiplication | 1.28M ops/sec | 0.51M ops/sec | 2.5x faster |
|
|
243
|
+
| Division | 1.04M ops/sec | 0.37M ops/sec | 2.8x faster |
|
|
244
|
+
| Ratio division | 2.94M ops/sec | 0.39M ops/sec | 7.6x faster |
|
|
245
|
+
| Comparison (`==`, `<`, `>`) | 2.5M–4.1M ops/sec | 0.35M–0.38M ops/sec | 7x–10x faster |
|
|
246
|
+
| Allocation (`Mint.allocate`) | 279k ops/sec | 146k ops/sec | 1.9x faster |
|
|
247
|
+
| Split (`Mint.split`) | 215k ops/sec | 85k ops/sec | 3.3x faster |
|
|
248
|
+
|
|
249
|
+
### Commands Used
|
|
250
|
+
|
|
251
|
+
```sh
|
|
252
|
+
BENCH=true bundle exec ruby -Ilib:test -r ./test/test_helper.rb test/performance/competitive_performance_benchmark.rb
|
|
253
|
+
BENCH=true bundle exec ruby -Ilib:test -r ./test/test_helper.rb test/performance/competitive_memory_benchmark.rb
|
|
254
|
+
```
|
|
255
|
+
|
|
213
256
|
## License
|
|
214
257
|
|
|
215
258
|
MIT
|
data/Rakefile
CHANGED
|
@@ -12,15 +12,20 @@ Rake::TestTask.new(:test) do |t|
|
|
|
12
12
|
t.ruby_opts << '-rtest_helper.rb'
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
Rake::TestTask.new(
|
|
15
|
+
Rake::TestTask.new('bench') do |t|
|
|
16
16
|
t.libs = %w[lib test]
|
|
17
|
-
t.pattern = 'test
|
|
17
|
+
t.pattern = 'test/performance/*_benchmark.rb'
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
Rake::TestTask.new('bench:performance') do |t|
|
|
20
|
+
Rake::TestTask.new('bench:parse') do |t|
|
|
22
21
|
t.libs = %w[lib test]
|
|
23
|
-
t.pattern = 'test/performance
|
|
22
|
+
t.pattern = 'test/performance/parse_benchmark.rb'
|
|
23
|
+
t.ruby_opts << '-r test_helper.rb'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
Rake::TestTask.new('bench:edge') do |t|
|
|
27
|
+
t.libs = %w[lib test]
|
|
28
|
+
t.pattern = 'test/performance/algorithm_benchmark.rb'
|
|
24
29
|
t.ruby_opts << '-r test_helper.rb'
|
|
25
30
|
end
|
|
26
31
|
|
|
@@ -32,12 +37,10 @@ end
|
|
|
32
37
|
|
|
33
38
|
Rake::TestTask.new('bench:competitive') do |t|
|
|
34
39
|
t.libs = %w[lib test]
|
|
35
|
-
t.pattern = 'test/performance/
|
|
40
|
+
t.pattern = 'test/performance/competitive_performance_benchmark.rb'
|
|
36
41
|
t.ruby_opts << '-r test_helper.rb'
|
|
37
42
|
end
|
|
38
43
|
|
|
39
|
-
task 'bench:all' => ['bench', 'bench:performance']
|
|
40
|
-
|
|
41
44
|
RuboCop::RakeTask.new(:cop)
|
|
42
45
|
|
|
43
46
|
YARD::Rake::YardocTask.new do |t|
|
|
@@ -3,7 +3,10 @@ module Mint
|
|
|
3
3
|
#
|
|
4
4
|
# @see https://www.iso.org/iso-4217-currency-codes.html
|
|
5
5
|
class Currency
|
|
6
|
-
attr_reader :code, :subunit, :symbol,
|
|
6
|
+
attr_reader :code, :subunit, :symbol,
|
|
7
|
+
:country,
|
|
8
|
+
:fractional_multiplier, :minimum_amount,
|
|
9
|
+
:name, :priority
|
|
7
10
|
|
|
8
11
|
def inspect
|
|
9
12
|
"<Currency:(#{code} #{symbol} #{subunit})>"
|
|
@@ -18,7 +21,8 @@ module Mint
|
|
|
18
21
|
@priority = priority.to_i
|
|
19
22
|
@country = country
|
|
20
23
|
@name = name
|
|
21
|
-
@
|
|
24
|
+
@fractional_multiplier = 10**@subunit
|
|
25
|
+
@minimum_amount = 1r / fractional_multiplier
|
|
22
26
|
freeze
|
|
23
27
|
end
|
|
24
28
|
end
|
|
@@ -14,6 +14,9 @@ module Mint
|
|
|
14
14
|
raise ArgumentError, "Currency [#{currency_code}] not registered. Check Mint.currencies"
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
+
# Returns default zero, no currency money
|
|
18
|
+
def self.zero = @zero ||= Money.new(0, Mint.currency('XXX'))
|
|
19
|
+
|
|
17
20
|
# Finds a registered currency by its code, symbol,
|
|
18
21
|
# or retrieves it directly if already a Currency object.
|
|
19
22
|
#
|
|
@@ -34,11 +37,11 @@ module Mint
|
|
|
34
37
|
#
|
|
35
38
|
# @param code [String, Symbol] the unique currency code (e.g. 'USD', :EUR)
|
|
36
39
|
# @param subunit [Integer] the decimal subunit precision (defaults to 2)
|
|
37
|
-
# @param symbol [String] the display symbol (defaults to '
|
|
40
|
+
# @param symbol [String] the display symbol (defaults to '')
|
|
38
41
|
# @param priority [Integer] parser precedence priority (defaults to 0)
|
|
39
42
|
# @return [Currency] the registered or existing Currency instance
|
|
40
43
|
# @raise [ArgumentError] if the code layout is invalid or register throws an error
|
|
41
|
-
def self.register_currency(code:, subunit: 2, symbol: '
|
|
44
|
+
def self.register_currency(code:, subunit: 2, symbol: '', priority: 0)
|
|
42
45
|
code = code.to_s
|
|
43
46
|
currencies[code] || register_currency!(code:, subunit:, symbol:, priority:)
|
|
44
47
|
end
|
|
@@ -14,7 +14,8 @@ module Mint
|
|
|
14
14
|
raise ArgumentError, 'Need at least 1 proportion element' if proportions.empty?
|
|
15
15
|
raise ArgumentError, 'Proportions total must not be zero' if whole.zero?
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
subunit = currency.subunit
|
|
18
|
+
amounts = proportions.map { |rate| (amount * rate.to_r / whole).round(subunit) }
|
|
18
19
|
allocate_left_over!(amounts: amounts, left_over: amount - amounts.sum)
|
|
19
20
|
end
|
|
20
21
|
|
|
@@ -39,15 +39,17 @@ module Mint
|
|
|
39
39
|
amount.to_i
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
def to_hash
|
|
43
|
+
{ currency: currency_code, amount: Kernel.format("%0.#{currency.subunit}f", amount) }
|
|
44
|
+
end
|
|
45
|
+
|
|
42
46
|
# Serializes the money instance to a standard JSON object containing the amount and currency.
|
|
43
47
|
# Highly optimized to run without external dependencies.
|
|
44
48
|
#
|
|
45
49
|
# @return [String] the JSON serialized string representation
|
|
46
50
|
def to_json(*_args)
|
|
47
|
-
subunit = currency.subunit
|
|
48
51
|
Kernel.format(
|
|
49
|
-
%({"currency": "#{currency_code}", "amount": "%0.#{subunit}f"}),
|
|
50
|
-
amount
|
|
52
|
+
%({"currency": "#{currency_code}", "amount": "%0.#{currency.subunit}f"}), amount
|
|
51
53
|
)
|
|
52
54
|
end
|
|
53
55
|
|
data/lib/minting/money/money.rb
CHANGED
|
@@ -24,6 +24,8 @@ module Mint
|
|
|
24
24
|
# @return [String] the ISO currency code
|
|
25
25
|
def currency_code = currency.code
|
|
26
26
|
|
|
27
|
+
def fractional = (amount * currency.fractional_multiplier).to_i
|
|
28
|
+
|
|
27
29
|
# Generates a stable hash key for Money instances.
|
|
28
30
|
#
|
|
29
31
|
# @return [Integer] the calculated hash value
|
|
@@ -33,7 +35,7 @@ module Mint
|
|
|
33
35
|
# @param new_amount [Numeric] The new amount
|
|
34
36
|
# @return [Money] A new Money object or self
|
|
35
37
|
def mint(new_amount)
|
|
36
|
-
new_amount
|
|
38
|
+
new_amount == amount ? self : Money.new(new_amount, currency)
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
# Returns a standard developer-oriented string inspection of the Money object.
|
|
@@ -48,8 +50,5 @@ module Mint
|
|
|
48
50
|
# @param other [Object] the target object to compare
|
|
49
51
|
# @return [Boolean] true if currencies match, false otherwise
|
|
50
52
|
def same_currency?(other) = other.respond_to?(:currency) && other.currency == currency
|
|
51
|
-
|
|
52
|
-
# Returns default zero no currency money
|
|
53
|
-
def self.zero = @zero ||= new(0, Mint.currencies('XXX'))
|
|
54
53
|
end
|
|
55
54
|
end
|
data/lib/minting/money/parse.rb
CHANGED
|
@@ -32,7 +32,7 @@ module Mint
|
|
|
32
32
|
# Extracts a numeric value from input that should only contain an amount.
|
|
33
33
|
def self.parse_amount(input)
|
|
34
34
|
# Remove any charater that is not a digit, comma or period
|
|
35
|
-
numeric = input.scan(/[\d
|
|
35
|
+
numeric = input.scan(/[\d.,-]/).join
|
|
36
36
|
numeric = normalize_separators(numeric)
|
|
37
37
|
Rational(numeric)
|
|
38
38
|
end
|
data/lib/minting/version.rb
CHANGED