minting 1.9.6 → 1.9.7
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 +30 -25
- data/Rakefile +16 -20
- data/bin/bench_check +2 -2
- data/doc/Mint/Currency.html +183 -94
- data/doc/Mint/Money.html +866 -334
- data/doc/Mint/RangeStepPatch.html +1 -1
- data/doc/Mint/Registry.html +10 -10
- data/doc/Mint/Rounding.html +2 -2
- data/doc/Mint/UnknownCurrency.html +6 -4
- data/doc/Mint.html +33 -106
- data/doc/Minting.html +2 -2
- data/doc/Numeric.html +479 -0
- data/doc/String.html +241 -0
- data/doc/_index.html +27 -1
- data/doc/agents/copilot-instructions.md +3 -3
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +47 -33
- data/doc/index.html +47 -33
- data/doc/method_list.html +125 -53
- data/doc/top-level-namespace.html +5 -21
- data/lib/minting/currency/currency.rb +6 -3
- data/lib/minting/data/world-currencies.yaml +75 -23
- data/lib/minting/mint/i18n.rb +1 -1
- data/lib/minting/mint/rounding.rb +1 -0
- data/lib/minting/money/conversion.rb +1 -0
- data/lib/minting/money/format/formatting.rb +88 -55
- data/lib/minting/money/format/to_s.rb +20 -3
- data/lib/minting/money/money.rb +0 -4
- data/lib/minting/version.rb +1 -1
- metadata +3 -2
- data/doc/Mint/CurrencyRegistry.html +0 -511
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 88ad41982102280b92486a6c6b1e05ae274afca3ccaf9daed2b778bc88f89764
|
|
4
|
+
data.tar.gz: 40c71dfc229055ddeb61c59c6a05977d8cce464c9ea869d07556be3efa529920
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fd6f1a5b8c3fdd281080743065a366b7a0334423f373a0970ecfff386682e0fd545103a3eb42aa516bca0dfd0a6709d2fbcc422b4966e610ca5efe55ddf270a4
|
|
7
|
+
data.tar.gz: 76f592147f190433923741d40707b830448c80a7b3fe003392c9c8740c06a41b8f2433e7d832db27b8758f8d46f272b9fd33d9d2b3e33fb0a2742aa2fc6aa19d
|
data/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Fast, precise, and developer-friendly money handling for Ruby.
|
|
|
12
12
|
```ruby
|
|
13
13
|
require 'minting'
|
|
14
14
|
|
|
15
|
-
price =
|
|
15
|
+
price = Money.from(19.99, 'USD') #=> [USD 19.99]
|
|
16
16
|
tax = price * 0.08 #=> [USD 1.60]
|
|
17
17
|
total = price + tax #=> [USD 21.59]
|
|
18
18
|
|
|
@@ -23,8 +23,8 @@ total.currency_code #=> "USD"
|
|
|
23
23
|
### Exact precision
|
|
24
24
|
Amounts are stored as `Rational` and rounded to the currency subunit. No floating-point surprises, ever.
|
|
25
25
|
|
|
26
|
-
###
|
|
27
|
-
Minting is
|
|
26
|
+
### Good performance
|
|
27
|
+
Minting is fast! See full benchmarks in the [Performance Guide](bench/BENCHMARKS.md).
|
|
28
28
|
|
|
29
29
|
### Clean, modern API
|
|
30
30
|
Intuitive interface, descriptive error messages, and sensible defaults. Works the way you expect.
|
|
@@ -55,38 +55,38 @@ gem 'minting'
|
|
|
55
55
|
require 'minting'
|
|
56
56
|
|
|
57
57
|
# Create money
|
|
58
|
-
ten =
|
|
58
|
+
ten = Money.from(10, 'USD') #=> [USD 10.00]
|
|
59
59
|
|
|
60
|
-
1.dollar ==
|
|
60
|
+
1.dollar == Money.from(1, 'USD') #=> true
|
|
61
61
|
ten = 10.dollars #=> [USD 10.00]
|
|
62
62
|
4.to_money('USD') #=> [USD 4.00]
|
|
63
63
|
|
|
64
64
|
# Comparisons
|
|
65
65
|
ten == 10.dollars #=> true
|
|
66
|
-
ten ==
|
|
67
|
-
ten >
|
|
66
|
+
ten == Money.from(10, 'EUR') #=> false
|
|
67
|
+
ten > Money.from(9.99, 'USD') #=> true
|
|
68
68
|
|
|
69
69
|
# Zero equality semantics
|
|
70
70
|
# Any zero amount is treated as equal, regardless of currency
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
Money.from(0, 'USD') == Money.from(0, 'EUR') #=> true
|
|
72
|
+
Money.from(0, 'USD') == 0 #=> true
|
|
73
|
+
Money.from(0, 'USD') == 0.0 #=> true
|
|
74
74
|
|
|
75
75
|
# Non-zero numerics are not equal to Money objects
|
|
76
|
-
|
|
76
|
+
Money.from(10, 'USD') == 10 #=> false
|
|
77
77
|
|
|
78
78
|
# Format (uses Kernel.format syntax)
|
|
79
|
-
price =
|
|
80
|
-
loss =
|
|
79
|
+
price = Money.from(9.99, 'USD')
|
|
80
|
+
loss = Money.from(-1234.56, 'USD')
|
|
81
81
|
|
|
82
82
|
# Built-in named presets
|
|
83
83
|
loss.format(:accounting) #=> "($1,234.56)"
|
|
84
|
-
|
|
84
|
+
Money.from(1234.56, 'EUR').format(:european) #=> "1.234,56 €"
|
|
85
85
|
price.format(:amount) #=> "9.99"
|
|
86
86
|
price.format(:currency) #=> "USD 9.99"
|
|
87
87
|
|
|
88
88
|
# Presets can be overridden with explicit kwargs
|
|
89
|
-
|
|
89
|
+
Money.from(1234.56, 'EUR').format(:european, format: '%<amount>f %<currency>s')
|
|
90
90
|
#=> "1.234,56 EUR"
|
|
91
91
|
|
|
92
92
|
# Or use direct format strings
|
|
@@ -97,7 +97,7 @@ price.format(format: '%<symbol>s%<amount>+f') #=> "$+9.99",
|
|
|
97
97
|
(-price).format(format: '%<amount>f') #=> "-9.99",
|
|
98
98
|
|
|
99
99
|
# Format with padding
|
|
100
|
-
price_in_euros =
|
|
100
|
+
price_in_euros = Money.from(12.34, 'EUR')
|
|
101
101
|
|
|
102
102
|
price.format(format: '--%<amount>7d') #=> "-- 9"
|
|
103
103
|
price.format(format: ' %<amount>10f %<currency>s') #=> " 9.99 USD"
|
|
@@ -107,16 +107,21 @@ price_in_euros.format(format: '%<symbol>2s%<amount>+10f') #=> " € +12.34
|
|
|
107
107
|
|
|
108
108
|
# Integral & fractional parts
|
|
109
109
|
price.format(format: '%<integral>d %<fractional>d/100') #=> "9 99/100"
|
|
110
|
-
|
|
110
|
+
Money.from(0.99, 'USD').format(format: '%<integral>d dollars and %<fractional>02d cents')
|
|
111
111
|
#=> "0 dollars and 99 cents"
|
|
112
112
|
|
|
113
113
|
# Per-sign Hash format (e.g. accounting parentheses for losses)
|
|
114
|
-
loss =
|
|
114
|
+
loss = Money.from(-1234.56, 'USD')
|
|
115
115
|
loss.format(format: { negative: '(%<symbol>s%<amount>f)' }) #=> "($1,234.56)"
|
|
116
|
-
|
|
116
|
+
Money.from(0, 'BRL').format(format: { zero: '--' }) #=> "--"
|
|
117
117
|
# All three keys at once:
|
|
118
118
|
fmt = { positive: '%<symbol>s%<amount>f', negative: '(%<symbol>s%<amount>f)', zero: '--' }
|
|
119
|
-
|
|
119
|
+
Money.from(1234.56, 'USD').format(format: fmt) #=> "$1,234.56"
|
|
120
|
+
|
|
121
|
+
# Disambiguated symbol (e.g. "US$" vs "C$" vs "A$")
|
|
122
|
+
Money.from(10, 'USD').format(format: '%<dsymbol>s%<amount>f') #=> "US$10.00"
|
|
123
|
+
Money.from(10, 'CAD').format(format: '%<dsymbol>s%<amount>f') #=> "C$10.00"
|
|
124
|
+
Money.from(10, 'EUR').format(format: '%<dsymbol>s%<amount>f') #=> "€10.00" (no dsymbol, falls back to symbol)
|
|
120
125
|
|
|
121
126
|
# Json serialization
|
|
122
127
|
|
|
@@ -147,8 +152,8 @@ ten.allocate([1, 2, 3]) #=> [[USD 1.67], [USD 3.33], [USD 5.00]]
|
|
|
147
152
|
|
|
148
153
|
# Clamping to a range
|
|
149
154
|
|
|
150
|
-
price =
|
|
151
|
-
min_price =
|
|
155
|
+
price = Money.from(50, 'USD')
|
|
156
|
+
min_price = Money.from(75, 'USD')
|
|
152
157
|
|
|
153
158
|
price.clamp(0, 100) #=> [USD 50.00] (returns self, no new object)
|
|
154
159
|
price.clamp(0, 25) #=> [USD 25.00] (clamped to max)
|
|
@@ -201,8 +206,8 @@ Mint::Currency.for_symbol('€') #=> #<Currency code="EUR" ...>
|
|
|
201
206
|
**Rounding modes** — Wrap operations in `Mint.with_rounding(mode)` to change how amounts are rounded to the subunit:
|
|
202
207
|
|
|
203
208
|
```ruby
|
|
204
|
-
Mint.with_rounding(:half_down) {
|
|
205
|
-
Mint.with_rounding(:ceil) {
|
|
209
|
+
Mint.with_rounding(:half_down) { Money.from(1.005, 'USD') } #=> [USD 1.00]
|
|
210
|
+
Mint.with_rounding(:ceil) { Money.from(1.001, 'USD') } #=> [USD 1.01]
|
|
206
211
|
Mint.with_rounding(:floor) { Mint.parse('1.009', 'USD') } #=> [USD 1.00]
|
|
207
212
|
```
|
|
208
213
|
|
|
@@ -212,7 +217,7 @@ Modes: `:half_up` (default), `:half_down`, `:floor`, `:ceil`, `:truncate`, `:dow
|
|
|
212
217
|
|
|
213
218
|
**Division** — `money / 5` returns new `Money`; `money / other_money` returns a numeric ratio, not money.
|
|
214
219
|
|
|
215
|
-
**Zero equality** — Any zero amount is considered equal across currencies and to numeric zero (`
|
|
220
|
+
**Zero equality** — Any zero amount is considered equal across currencies and to numeric zero (`Money.from(0, 'USD') == Money.from(0, 'EUR')` is intentionally `true`). Non-zero amounts must match currency and value.
|
|
216
221
|
|
|
217
222
|
**Zero helper** — `Currency.zero('USD')` returns a frozen zero-Money, useful as a default value for discounts, totals, or counters.
|
|
218
223
|
|
data/Rakefile
CHANGED
|
@@ -15,46 +15,42 @@ Rake::TestTask.new(:test) do |t|
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
Rake::TestTask.new('bench:all') do |t|
|
|
18
|
-
t.libs = %w[lib
|
|
19
|
-
t.pattern = '
|
|
18
|
+
t.libs = %w[lib bench]
|
|
19
|
+
t.pattern = 'bench/{core,memory,regression}/*_benchmark.rb'
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
Rake::TestTask.new('bench:core') do |t|
|
|
23
|
-
t.libs = %w[lib
|
|
24
|
-
t.pattern = '
|
|
23
|
+
t.libs = %w[lib bench]
|
|
24
|
+
t.pattern = 'bench/core/parse_benchmark.rb'
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
Rake::TestTask.new('bench:memory') do |t|
|
|
28
|
-
t.libs = %w[lib
|
|
29
|
-
t.pattern = '
|
|
28
|
+
t.libs = %w[lib bench]
|
|
29
|
+
t.pattern = 'bench/memory/*_benchmark.rb'
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
Rake::TestTask.new('bench:regression') do |t|
|
|
33
|
-
t.libs = %w[lib
|
|
34
|
-
t.pattern = '
|
|
33
|
+
t.libs = %w[lib bench]
|
|
34
|
+
t.pattern = 'bench/regression/*_benchmark.rb'
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
desc 'Run competitive benchmarks (Money gem)'
|
|
38
|
-
task 'bench:
|
|
39
|
-
|
|
38
|
+
task 'bench:against:money' do
|
|
39
|
+
dir = 'bench/competitive/money'
|
|
40
|
+
sh "BUNDLE_GEMFILE=#{dir}/Gemfile bundle exec ruby -Ilib -I#{dir} -Ibench -e \"Dir[File.join(__dir__, '#{dir}/**/*_benchmark.rb')].each { |f| require f }\""
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
desc 'Run competitive benchmarks (Shopify Money)'
|
|
43
|
-
task 'bench:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
desc 'Run all competitive benchmarks (both Money and Shopify)'
|
|
48
|
-
task 'bench:competitive:all' do
|
|
49
|
-
sh 'bundle exec ruby -Ilib -Itest -e "Dir[File.join(__dir__, \"test/performance/competitive/money/**/*_benchmark.rb\")].each { |f| require f }"'
|
|
50
|
-
sh 'BUNDLE_WITHOUT=money_bench bundle exec ruby -Ilib -Itest -e "Dir[File.join(__dir__, \"test/performance/competitive/shopify/**/*_benchmark.rb\")].each { |f| require f }"'
|
|
44
|
+
task 'bench:against:shopify' do
|
|
45
|
+
dir = 'bench/competitive/shopify'
|
|
46
|
+
sh "BUNDLE_GEMFILE=#{dir}/Gemfile bundle exec ruby -Ilib -I#{dir} -Ibench -e \"Dir[File.join(__dir__, '#{dir}/**/*_benchmark.rb')].each { |f| require f }\""
|
|
51
47
|
end
|
|
52
48
|
|
|
53
49
|
desc 'Run core benchmarks and update the baseline'
|
|
54
50
|
task 'bench:baseline' do
|
|
55
51
|
platform = RUBY_PLATFORM
|
|
56
|
-
baseline = "
|
|
57
|
-
sh "ruby
|
|
52
|
+
baseline = "bench/check/results/baseline-#{platform}.json"
|
|
53
|
+
sh "ruby bench/check/runner.rb #{baseline}"
|
|
58
54
|
puts "Baseline updated for #{platform}."
|
|
59
55
|
end
|
|
60
56
|
|
data/bin/bench_check
CHANGED
|
@@ -5,7 +5,7 @@ require 'json'
|
|
|
5
5
|
require 'tempfile'
|
|
6
6
|
|
|
7
7
|
platform = RUBY_PLATFORM
|
|
8
|
-
results_dir = File.expand_path('../
|
|
8
|
+
results_dir = File.expand_path('../bench/check/results', __dir__)
|
|
9
9
|
baseline_path = File.join(results_dir, "baseline-#{platform}.json")
|
|
10
10
|
threshold = (ARGV[0] || 0.80).to_f
|
|
11
11
|
|
|
@@ -17,7 +17,7 @@ end
|
|
|
17
17
|
baseline = JSON.parse(File.read(baseline_path))
|
|
18
18
|
current_json = Tempfile.new(%w[bench_current .json])
|
|
19
19
|
|
|
20
|
-
unless system("bundle exec ruby
|
|
20
|
+
unless system("bundle exec ruby bench/check/runner.rb #{current_json.path}")
|
|
21
21
|
puts 'Benchmark runner failed.'
|
|
22
22
|
exit 1
|
|
23
23
|
end
|