minting 1.9.7 → 2.0.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 +234 -116
- data/Rakefile +2 -7
- data/doc/agents/api_review-2026-06-15.md +1 -1
- data/doc/agents/copilot-instructions.md +2 -2
- data/doc/agents/expired/copilot-instructions.md +2 -2
- data/doc/agents/expired/gemini_gem_evaluation.md +2 -2
- data/lib/minting/aliases.rb +22 -0
- data/lib/minting/currency/currency.rb +81 -9
- data/lib/minting/data/crypto-currencies.yaml +126 -0
- data/lib/minting/mint/i18n.rb +79 -29
- data/lib/minting/mint/mint.rb +1 -26
- data/lib/minting/mint/registry/crypto.rb +59 -0
- data/lib/minting/mint/registry/registration.rb +1 -2
- data/lib/minting/mint/registry/symbols.rb +37 -30
- data/lib/minting/mint/rounding.rb +9 -7
- data/lib/minting/mint.rb +1 -2
- data/lib/minting/money/allocation/allocation.rb +2 -2
- data/lib/minting/money/allocation/split.rb +1 -1
- data/lib/minting/money/arithmetics/operators.rb +10 -13
- data/lib/minting/money/clamp.rb +6 -6
- data/lib/minting/money/coercion.rb +1 -1
- data/lib/minting/money/comparable.rb +3 -3
- data/lib/minting/money/constructors.rb +3 -42
- data/lib/minting/money/conversion.rb +22 -18
- data/lib/minting/money/format/format.rb +100 -0
- data/lib/minting/money/format/formatter.rb +102 -0
- data/lib/minting/money/format/to_s.rb +20 -102
- data/lib/minting/money/format/validator.rb +34 -0
- data/lib/minting/money/money.rb +25 -9
- data/lib/minting/money/parse.rb +127 -0
- data/lib/minting/money/rounding.rb +27 -0
- data/lib/minting/version.rb +1 -1
- data/lib/minting.rb +17 -8
- metadata +9 -29
- data/doc/Mint/Currency.html +0 -2032
- data/doc/Mint/Money.html +0 -5139
- data/doc/Mint/RangeStepPatch.html +0 -277
- data/doc/Mint/Registry.html +0 -863
- data/doc/Mint/Rounding.html +0 -506
- data/doc/Mint/UnknownCurrency.html +0 -138
- data/doc/Mint.html +0 -931
- data/doc/Minting.html +0 -142
- data/doc/Numeric.html +0 -479
- data/doc/String.html +0 -241
- data/doc/_index.html +0 -206
- data/doc/class_list.html +0 -54
- data/doc/css/common.css +0 -1
- data/doc/css/full_list.css +0 -206
- data/doc/css/style.css +0 -1089
- data/doc/file.README.html +0 -291
- data/doc/file_list.html +0 -59
- data/doc/frames.html +0 -22
- data/doc/index.html +0 -291
- data/doc/js/app.js +0 -801
- data/doc/js/full_list.js +0 -334
- data/doc/js/jquery.js +0 -4
- data/doc/method_list.html +0 -758
- data/doc/top-level-namespace.html +0 -135
- data/lib/minting/mint/aliases.rb +0 -16
- data/lib/minting/mint/parser/parser.rb +0 -97
- data/lib/minting/mint/parser/separators.rb +0 -41
- data/lib/minting/money/format/formatting.rb +0 -130
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>
|
|
7
|
-
Top Level Namespace
|
|
8
|
-
|
|
9
|
-
— Documentation by YARD 0.9.44
|
|
10
|
-
|
|
11
|
-
</title>
|
|
12
|
-
|
|
13
|
-
<link rel="stylesheet" href="css/style.css" type="text/css">
|
|
14
|
-
|
|
15
|
-
<link rel="stylesheet" href="css/common.css" type="text/css">
|
|
16
|
-
|
|
17
|
-
<script type="text/javascript">
|
|
18
|
-
pathId = "";
|
|
19
|
-
relpath = '';
|
|
20
|
-
</script>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
|
24
|
-
|
|
25
|
-
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
</head>
|
|
29
|
-
<body>
|
|
30
|
-
<div id="main_progress" aria-hidden="true"></div>
|
|
31
|
-
|
|
32
|
-
<div class="nav_wrap">
|
|
33
|
-
<iframe id="nav" src="class_list.html?1"></iframe>
|
|
34
|
-
<div id="resizer"></div>
|
|
35
|
-
</div>
|
|
36
|
-
|
|
37
|
-
<div id="main" tabindex="-1">
|
|
38
|
-
<div id="header">
|
|
39
|
-
<div id="menu">
|
|
40
|
-
|
|
41
|
-
<a href="_index.html">Index</a> »
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<span class="title">Top Level Namespace</span>
|
|
45
|
-
|
|
46
|
-
</div>
|
|
47
|
-
|
|
48
|
-
<div id="search">
|
|
49
|
-
|
|
50
|
-
<a class="full_list_link" id="class_list_link"
|
|
51
|
-
href="class_list.html">
|
|
52
|
-
|
|
53
|
-
<svg width="24" height="24">
|
|
54
|
-
<rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
|
|
55
|
-
<rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
|
|
56
|
-
<rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
|
|
57
|
-
</svg>
|
|
58
|
-
</a>
|
|
59
|
-
|
|
60
|
-
</div>
|
|
61
|
-
<div class="clear"></div>
|
|
62
|
-
</div>
|
|
63
|
-
|
|
64
|
-
<div id="content"><h1>Top Level Namespace
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
</h1>
|
|
69
|
-
<div class="box_info">
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
</div>
|
|
82
|
-
|
|
83
|
-
<h2>Defined Under Namespace</h2>
|
|
84
|
-
<p class="children">
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<strong class="modules">Modules:</strong> <span class='object_link'><a href="Mint.html" title="Mint (module)">Mint</a></span>, <span class='object_link'><a href="Minting.html" title="Minting (module)">Minting</a></span>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
<strong class="classes">Classes:</strong> <span class='object_link'><a href="Numeric.html" title="Numeric (class)">Numeric</a></span>, <span class='object_link'><a href="String.html" title="String (class)">String</a></span>
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
</p>
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
<h2>
|
|
98
|
-
Constant Summary
|
|
99
|
-
<small><a href="#" class="constants_summary_toggle">collapse</a></small>
|
|
100
|
-
</h2>
|
|
101
|
-
|
|
102
|
-
<dl class="constants">
|
|
103
|
-
|
|
104
|
-
<dt id="Money-constant" class="">Money =
|
|
105
|
-
|
|
106
|
-
</dt>
|
|
107
|
-
<dd><pre class="code"><span class='const'><span class='object_link'><a href="Mint.html" title="Mint (module)">Mint</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Mint/Money.html" title="Mint::Money (class)">Money</a></span></span></pre></dd>
|
|
108
|
-
|
|
109
|
-
<dt id="Currency-constant" class="">Currency =
|
|
110
|
-
|
|
111
|
-
</dt>
|
|
112
|
-
<dd><pre class="code"><span class='const'><span class='object_link'><a href="Mint.html" title="Mint (module)">Mint</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Mint/Currency.html" title="Mint::Currency (class)">Currency</a></span></span></pre></dd>
|
|
113
|
-
|
|
114
|
-
</dl>
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
</div>
|
|
126
|
-
|
|
127
|
-
<div id="footer">
|
|
128
|
-
Generated on Wed Jul 1 01:11:52 2026 by
|
|
129
|
-
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
|
130
|
-
0.9.44 (ruby-4.0.5).
|
|
131
|
-
</div>
|
|
132
|
-
|
|
133
|
-
</div>
|
|
134
|
-
</body>
|
|
135
|
-
</html>
|
data/lib/minting/mint/aliases.rb
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Optional top-level alias for Mint::Currency.
|
|
4
|
-
#
|
|
5
|
-
# Mint::Money is auto-bound as the top-level Money constant by
|
|
6
|
-
# `require 'minting'` (see lib/minting.rb). Currency is not auto-bound
|
|
7
|
-
# because application domain models are commonly named Currency (e.g. a
|
|
8
|
-
# Rails model). Require this file to opt in:
|
|
9
|
-
#
|
|
10
|
-
# require 'minting/mint/aliases'
|
|
11
|
-
#
|
|
12
|
-
if defined?(Currency) && Currency != Mint::Currency
|
|
13
|
-
warn "minting: top-level Currency is already defined (#{Currency}); skipping alias."
|
|
14
|
-
else
|
|
15
|
-
Currency = Mint::Currency unless defined?(Currency)
|
|
16
|
-
end
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Mint Money parsing
|
|
4
|
-
module Mint
|
|
5
|
-
extend self
|
|
6
|
-
|
|
7
|
-
# Parses a human-readable money string into a {Money} object.
|
|
8
|
-
#
|
|
9
|
-
# Returns +nil+ when the input is invalid or currency cannot be determined.
|
|
10
|
-
#
|
|
11
|
-
# @param input [String] Amount input, optionally including a currency symbol or code
|
|
12
|
-
# @param currency [String, Symbol, Currency, nil] ISO code when not present in +input+
|
|
13
|
-
# @return [Money, nil]
|
|
14
|
-
#
|
|
15
|
-
# @example With explicit currency
|
|
16
|
-
# Mint.parse('19.99', 'USD') #=> [USD 19.99]
|
|
17
|
-
# Mint.parse('garbage', 'USD') #=> nil
|
|
18
|
-
#
|
|
19
|
-
# @example With symbol or code in the string
|
|
20
|
-
# Mint.parse('$19.99') #=> [USD 19.99]
|
|
21
|
-
# Mint.parse('USD 1,234.56') #=> [USD 1234.56]
|
|
22
|
-
def parse(input, currency = nil)
|
|
23
|
-
return nil unless input.is_a?(String)
|
|
24
|
-
|
|
25
|
-
input = input.strip
|
|
26
|
-
return nil if input.empty?
|
|
27
|
-
|
|
28
|
-
currency = parse_currency(input, currency)
|
|
29
|
-
return nil unless currency
|
|
30
|
-
|
|
31
|
-
amount = parse_amount(input)
|
|
32
|
-
return nil unless amount
|
|
33
|
-
|
|
34
|
-
amount = currency.normalize_amount(amount)
|
|
35
|
-
Mint::Money.new(amount, currency)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Like {.parse} but raises on failure.
|
|
39
|
-
#
|
|
40
|
-
# @param input [String] Amount input, optionally including a currency symbol or code
|
|
41
|
-
# @param currency [String, Symbol, Currency, nil] ISO code when not present in +input+
|
|
42
|
-
# @return [Money]
|
|
43
|
-
# @raise [ArgumentError] when +input+ is invalid or currency cannot be determined
|
|
44
|
-
#
|
|
45
|
-
# @example
|
|
46
|
-
# Mint.parse!('19.99', 'USD') #=> [USD 19.99]
|
|
47
|
-
# Mint.parse!('garbage', 'USD') #=> ArgumentError
|
|
48
|
-
def parse!(input, currency = nil)
|
|
49
|
-
raise ArgumentError, 'input must be a String' unless input.is_a?(String)
|
|
50
|
-
|
|
51
|
-
input = input.strip
|
|
52
|
-
raise ArgumentError, 'input cannot be empty' if input.empty?
|
|
53
|
-
|
|
54
|
-
currency = parse_currency(input, currency)
|
|
55
|
-
raise ArgumentError, "Currency [#{currency}] not found" unless currency
|
|
56
|
-
|
|
57
|
-
amount = parse_amount(input)
|
|
58
|
-
raise ArgumentError, "Could not parse [#{input}]" unless amount
|
|
59
|
-
|
|
60
|
-
amount = currency.normalize_amount(amount)
|
|
61
|
-
Mint::Money.new(amount, currency)
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
private
|
|
65
|
-
|
|
66
|
-
# Extracts a numeric value from input that should only contain an amount.
|
|
67
|
-
# @private
|
|
68
|
-
def parse_amount(input)
|
|
69
|
-
accounting_negative = input.start_with?('(') && input.end_with?(')')
|
|
70
|
-
|
|
71
|
-
# Remove any charater that is not a digit, comma or period
|
|
72
|
-
numeric_input = input.scan(/[\d.,-]/).join
|
|
73
|
-
numeric = parse_separators(numeric_input)
|
|
74
|
-
return nil unless numeric
|
|
75
|
-
|
|
76
|
-
amount = Rational(numeric)
|
|
77
|
-
accounting_negative ? -amount : amount
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Extracts currency from a string by matching ISO code or symbol.
|
|
81
|
-
#
|
|
82
|
-
# Scans all uppercase words and returns the first registered code, falling
|
|
83
|
-
# back to symbol matching. This correctly handles inputs like
|
|
84
|
-
# "MAX 10.00 USD" where the first uppercase word isn't a currency code.
|
|
85
|
-
# @private
|
|
86
|
-
def parse_currency(input, currency = nil)
|
|
87
|
-
input.scan(/\b([A-Z_]+)\b/) do |(code)|
|
|
88
|
-
found = Currency.for_code(code)
|
|
89
|
-
return found if found
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
found = Registry.detect_currency(input)
|
|
93
|
-
return found if found
|
|
94
|
-
|
|
95
|
-
Currency.resolve(currency)
|
|
96
|
-
end
|
|
97
|
-
end
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Mint Money parsing
|
|
4
|
-
module Mint
|
|
5
|
-
extend self
|
|
6
|
-
|
|
7
|
-
private
|
|
8
|
-
|
|
9
|
-
# Classifies the separator pattern in a numeric string.
|
|
10
|
-
# @private
|
|
11
|
-
def classify_separators(numeric)
|
|
12
|
-
case [numeric.count('.'), numeric.count(',')]
|
|
13
|
-
in [0, 1] if numeric[-4] == ',' then :thousands_comma # Comma is a thousand separator
|
|
14
|
-
in [0, 1] then :decimal_comma # Only one comma: decimal (e.g. 19,99 or 1,4 or 1,2345).
|
|
15
|
-
in [0, 0] | [1, 0] then :decimal_period # e.g. "1500" or "34.21".
|
|
16
|
-
in [p, c] if p > 1 && c > 1 then :invalid # Both separators appear multiple times
|
|
17
|
-
in [p, c] if p > 0 && c > 0 then :mixed # Commas and dots: the rightmost one is the decimal
|
|
18
|
-
else :thousands # Multiple of the same separator only (e.g. 1,234,567)
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# Converts locale-specific decimal/thousand separators into a plain decimal string.
|
|
23
|
-
# @private
|
|
24
|
-
def parse_separators(numeric)
|
|
25
|
-
return nil unless numeric.match?(/\d/)
|
|
26
|
-
|
|
27
|
-
case classify_separators(numeric)
|
|
28
|
-
when :decimal_period then numeric # Nothing to normalize (e.g. "1500" or "34.21").
|
|
29
|
-
when :decimal_comma then numeric.tr(',', '.') # Only one comma: decimal (e.g. 19,99 or 1,234).
|
|
30
|
-
when :thousands_comma then numeric.delete(',')
|
|
31
|
-
when :thousands then numeric.delete('.,')
|
|
32
|
-
when :invalid then nil
|
|
33
|
-
when :mixed # Commas and dots: the rightmost one is the decimal separator.
|
|
34
|
-
if numeric.rindex(',') > numeric.rindex('.')
|
|
35
|
-
numeric.delete('.').tr(',', '.')
|
|
36
|
-
else
|
|
37
|
-
numeric.delete(',')
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Mint
|
|
4
|
-
# :nodoc:
|
|
5
|
-
class Money
|
|
6
|
-
def self.compiled_formatters
|
|
7
|
-
@compiled_formatters ||= {}
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
# Build each sign template with subunit precision baked in, returning a
|
|
11
|
-
# frozen Hash of templates keyed by sign (:positive, :negative, :zero).
|
|
12
|
-
# Keeps %<symbol>s and %<currency>s as format args so width/padding specifiers work.
|
|
13
|
-
# @private
|
|
14
|
-
def self.compile_templates(format_hash, subunit)
|
|
15
|
-
[
|
|
16
|
-
format_hash[:positive] || DEFAULT_FORMAT,
|
|
17
|
-
format_hash[:negative],
|
|
18
|
-
format_hash[:zero]
|
|
19
|
-
].map do |sign_format|
|
|
20
|
-
next unless sign_format
|
|
21
|
-
|
|
22
|
-
# Injects the currency's subunit precision into %<amount>f specifiers
|
|
23
|
-
# (e.g. '%<amount>f' → '%<amount>.2f' for USD), preserving any
|
|
24
|
-
# existing width/alignment specifier (e.g. '%<amount>+10f' stays).
|
|
25
|
-
sign_format = sign_format.gsub(/%<amount>(\s*\+?\d*)f/, "%<amount>\\1.#{subunit}f")
|
|
26
|
-
|
|
27
|
-
# For zero-subunit currencies (JPY, KRW, etc.), strip %<fractional>d
|
|
28
|
-
# specifiers entirely since there is no fractional part to display.
|
|
29
|
-
sign_format.gsub!(/%<fractional>[^%]*?d/, '') if subunit.zero?
|
|
30
|
-
|
|
31
|
-
sign_format
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# Builds and returns a lambda that formats amounts for a fixed
|
|
36
|
-
# [format_config, currency, decimal, thousand] combination.
|
|
37
|
-
# The lambda is cached by {.compiled_formatters}.
|
|
38
|
-
# @private
|
|
39
|
-
def self.compile_formatter(format_hash, currency, decimal, thousand) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
40
|
-
subunit = currency.subunit
|
|
41
|
-
has_decimal_substitution = decimal != '.'
|
|
42
|
-
escaped_decimal = Regexp.escape(decimal)
|
|
43
|
-
has_thousand_separator = thousand && !thousand.empty?
|
|
44
|
-
|
|
45
|
-
templates = compile_templates(format_hash, subunit)
|
|
46
|
-
positive_template, negative_template, zero_template = templates
|
|
47
|
-
|
|
48
|
-
# Detect whether templates use %<fractional> and/or %<d-symbol>
|
|
49
|
-
all_templates = templates.compact.join
|
|
50
|
-
needs_fractional = all_templates.include?('%<fractional>')
|
|
51
|
-
needs_integral = all_templates.include?('%<amount>') || all_templates.include?('%<integral>')
|
|
52
|
-
multiplier = currency.fractional_multiplier
|
|
53
|
-
symbol = currency.symbol
|
|
54
|
-
|
|
55
|
-
args = {
|
|
56
|
-
currency: currency.code,
|
|
57
|
-
dsymbol: currency.disambiguate_symbol || symbol,
|
|
58
|
-
symbol: symbol
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
lambda do |amount|
|
|
62
|
-
format_template = if negative_template && amount < 0
|
|
63
|
-
amount = -amount
|
|
64
|
-
negative_template
|
|
65
|
-
elsif zero_template && amount == 0
|
|
66
|
-
zero_template
|
|
67
|
-
else
|
|
68
|
-
positive_template
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
args[:amount] = amount
|
|
72
|
-
args[:integral] = amount.to_i
|
|
73
|
-
args[:fractional] = ((amount.abs % 1) * multiplier).to_i if needs_fractional
|
|
74
|
-
|
|
75
|
-
result = Kernel.format(format_template, **args)
|
|
76
|
-
result.gsub!(/(?<=\d)\.(?=\d)/, decimal) if has_decimal_substitution
|
|
77
|
-
|
|
78
|
-
if needs_integral && has_thousand_separator && (amount >= 1000 || amount <= -1000)
|
|
79
|
-
# Split on the decimal separator between digits only — symbols may contain '.' (e.g. د.إ).
|
|
80
|
-
parts = result.split(/(?<=\d)#{escaped_decimal}(?=\d)/, 2)
|
|
81
|
-
# Insert the thousands delimiter before each run of 3 digits from the right.
|
|
82
|
-
parts[0].gsub!(/(\d)(?=(?:\d{3})+(?:[^\d]|$))/) { Regexp.last_match(1) + thousand }
|
|
83
|
-
result = parts.join(decimal)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
result
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
private
|
|
91
|
-
|
|
92
|
-
# Validates that format hash contains only known keys.
|
|
93
|
-
# @private
|
|
94
|
-
def validate_format_hash(format)
|
|
95
|
-
unknown = format.keys - %i[positive negative zero]
|
|
96
|
-
|
|
97
|
-
raise ArgumentError, "Unknown format parameter(s): #{unknown.inspect}. " unless unknown.empty?
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
# Validates +decimal+ and +thousand+ separator arguments.
|
|
101
|
-
# @private
|
|
102
|
-
def validate_separators!(decimal:, thousand:)
|
|
103
|
-
case decimal
|
|
104
|
-
when nil # :noop
|
|
105
|
-
when '' then raise ArgumentError, 'decimal must be a non-empty'
|
|
106
|
-
when thousand then raise ArgumentError, "decimal and thousand cannot be identical: #{decimal.inspect}"
|
|
107
|
-
when String # :noop
|
|
108
|
-
else raise ArgumentError, "decimal must be a String, false, or nil, got #{decimal.inspect}"
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
case thousand
|
|
112
|
-
when false, nil, String # :noop
|
|
113
|
-
else raise ArgumentError, "thousand must be a String, false, or nil, got #{thousand.inspect}"
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
# Applies a format template to the money amount, returning a formatted string.
|
|
118
|
-
# Uses a cached compiled formatter lambda that pre-resolves currency-specific
|
|
119
|
-
# values (symbol, code, subunit) so per-call work is reduced to
|
|
120
|
-
# Kernel.format + optional separator substitutions.
|
|
121
|
-
# @private
|
|
122
|
-
def format_amount(format, decimal:, thousand:)
|
|
123
|
-
key = [format, currency_code, decimal, thousand].hash
|
|
124
|
-
|
|
125
|
-
formatter = Money.compiled_formatters[key] ||= Money.compile_formatter(format, currency, decimal, thousand)
|
|
126
|
-
|
|
127
|
-
formatter.call(amount)
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
end
|