amounts 0.0.5 → 0.0.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.
@@ -0,0 +1,163 @@
1
+ # Registry overview:
2
+ # Major Fiat Currencies:
3
+ # - This preset is a curated basket of widely used fiat currencies for general product and treasury workflows.
4
+ # - Minor-unit precision follows ISO-4217-style currency fractions: most entries use 2 decimals, while JPY uses 0.
5
+ # - Display symbols are English-oriented defaults inspired by CLDR conventions and may need localization in real applications.
6
+ # - Source: Unicode CLDR currency symbols and localization notes: https://cldr.unicode.org/translation/currency-names-and-symbols/currency-names
7
+
8
+ # USD: United States dollar.
9
+ # Fact: Uses 2 decimal places for cents.
10
+ # Preset default: Uses "$" as the default display symbol in an English-oriented UI.
11
+ # Preset default: Renders as a prefix amount, for example "$12.34".
12
+ # Source: Currency symbol conventions: https://cldr.unicode.org/translation/currency-names-and-symbols/currency-names
13
+ Amount.register :USD,
14
+ decimals: 2,
15
+ display_symbol: "$",
16
+ display_position: :prefix,
17
+ ui_decimals: 2
18
+
19
+ # EUR: Euro.
20
+ # Fact: Uses 2 decimal places for cents.
21
+ # Preset default: Uses the euro sign as the default display symbol.
22
+ # Preset default: Renders as a prefix amount for a compact English-oriented default.
23
+ # Source: Currency symbol conventions: https://cldr.unicode.org/translation/currency-names-and-symbols/currency-names
24
+ Amount.register :EUR,
25
+ decimals: 2,
26
+ display_symbol: "€",
27
+ display_position: :prefix,
28
+ ui_decimals: 2
29
+
30
+ # GBP: British pound sterling.
31
+ # Fact: Uses 2 decimal places for pence.
32
+ # Preset default: Uses the pound sign as the default display symbol.
33
+ # Preset default: Renders as a prefix amount.
34
+ # Source: Currency symbol conventions: https://cldr.unicode.org/translation/currency-names-and-symbols/currency-names
35
+ Amount.register :GBP,
36
+ decimals: 2,
37
+ display_symbol: "£",
38
+ display_position: :prefix,
39
+ ui_decimals: 2
40
+
41
+ # JPY: Japanese yen.
42
+ # Fact: Uses 0 decimal places under standard minor-unit conventions.
43
+ # Preset default: Uses the yen sign as the default display symbol.
44
+ # Preset default: Renders as a prefix amount with no fractional UI digits.
45
+ # Source: Currency symbol conventions: https://cldr.unicode.org/translation/currency-names-and-symbols/currency-names
46
+ Amount.register :JPY,
47
+ decimals: 0,
48
+ display_symbol: "¥",
49
+ display_position: :prefix,
50
+ ui_decimals: 0
51
+
52
+ # CHF: Swiss franc.
53
+ # Fact: Uses 2 decimal places for rappen/centimes.
54
+ # Preset default: Uses the ISO code as the display symbol to avoid locale ambiguity.
55
+ # Preset default: Renders as a suffix amount, for example "12.34 CHF".
56
+ # Source: Currency symbol conventions: https://cldr.unicode.org/translation/currency-names-and-symbols/currency-names
57
+ Amount.register :CHF,
58
+ decimals: 2,
59
+ display_symbol: "CHF",
60
+ display_position: :suffix,
61
+ ui_decimals: 2
62
+
63
+ # CAD: Canadian dollar.
64
+ # Fact: Uses 2 decimal places for cents.
65
+ # Preset default: Uses "CA$" instead of plain "$" to avoid collision with USD.
66
+ # Source: Currency symbol conventions: https://cldr.unicode.org/translation/currency-names-and-symbols/currency-names
67
+ Amount.register :CAD,
68
+ decimals: 2,
69
+ display_symbol: "CA$",
70
+ display_position: :prefix,
71
+ ui_decimals: 2
72
+
73
+ # AUD: Australian dollar.
74
+ # Fact: Uses 2 decimal places for cents.
75
+ # Preset default: Uses "A$" instead of plain "$" to avoid collision with USD.
76
+ # Source: Currency symbol conventions: https://cldr.unicode.org/translation/currency-names-and-symbols/currency-names
77
+ Amount.register :AUD,
78
+ decimals: 2,
79
+ display_symbol: "A$",
80
+ display_position: :prefix,
81
+ ui_decimals: 2
82
+
83
+ # NZD: New Zealand dollar.
84
+ # Fact: Uses 2 decimal places for cents.
85
+ # Preset default: Uses "NZ$" instead of plain "$" to avoid collision with USD.
86
+ # Source: Currency symbol conventions: https://cldr.unicode.org/translation/currency-names-and-symbols/currency-names
87
+ Amount.register :NZD,
88
+ decimals: 2,
89
+ display_symbol: "NZ$",
90
+ display_position: :prefix,
91
+ ui_decimals: 2
92
+
93
+ # SGD: Singapore dollar.
94
+ # Fact: Uses 2 decimal places for cents.
95
+ # Preset default: Uses "S$" instead of plain "$" to avoid collision with USD.
96
+ # Source: Currency symbol conventions: https://cldr.unicode.org/translation/currency-names-and-symbols/currency-names
97
+ Amount.register :SGD,
98
+ decimals: 2,
99
+ display_symbol: "S$",
100
+ display_position: :prefix,
101
+ ui_decimals: 2
102
+
103
+ # HKD: Hong Kong dollar.
104
+ # Fact: Uses 2 decimal places for cents.
105
+ # Preset default: Uses "HK$" instead of plain "$" to avoid collision with USD.
106
+ # Source: Currency symbol conventions: https://cldr.unicode.org/translation/currency-names-and-symbols/currency-names
107
+ Amount.register :HKD,
108
+ decimals: 2,
109
+ display_symbol: "HK$",
110
+ display_position: :prefix,
111
+ ui_decimals: 2
112
+
113
+ # CNY: Chinese yuan renminbi.
114
+ # Fact: Uses 2 decimal places for jiao/fen display.
115
+ # Preset default: Uses "CN¥" to avoid confusion with other yen/yuan symbols in multi-currency UIs.
116
+ # Source: Currency symbol conventions: https://cldr.unicode.org/translation/currency-names-and-symbols/currency-names
117
+ Amount.register :CNY,
118
+ decimals: 2,
119
+ display_symbol: "CN¥",
120
+ display_position: :prefix,
121
+ ui_decimals: 2
122
+
123
+ # INR: Indian rupee.
124
+ # Fact: Uses 2 decimal places for paise.
125
+ # Preset default: Uses the rupee sign as the default display symbol.
126
+ # Preset default: Renders as a prefix amount.
127
+ # Source: Currency symbol conventions: https://cldr.unicode.org/translation/currency-names-and-symbols/currency-names
128
+ Amount.register :INR,
129
+ decimals: 2,
130
+ display_symbol: "₹",
131
+ display_position: :prefix,
132
+ ui_decimals: 2
133
+
134
+ # MXN: Mexican peso.
135
+ # Fact: Uses 2 decimal places for centavos.
136
+ # Preset default: Uses "MX$" instead of plain "$" to avoid collision with USD.
137
+ # Source: Currency symbol conventions: https://cldr.unicode.org/translation/currency-names-and-symbols/currency-names
138
+ Amount.register :MXN,
139
+ decimals: 2,
140
+ display_symbol: "MX$",
141
+ display_position: :prefix,
142
+ ui_decimals: 2
143
+
144
+ # BRL: Brazilian real.
145
+ # Fact: Uses 2 decimal places for centavos.
146
+ # Preset default: Uses the common Brazilian real symbol "R$".
147
+ # Source: Currency symbol conventions: https://cldr.unicode.org/translation/currency-names-and-symbols/currency-names
148
+ Amount.register :BRL,
149
+ decimals: 2,
150
+ display_symbol: "R$",
151
+ display_position: :prefix,
152
+ ui_decimals: 2
153
+
154
+ # AED: United Arab Emirates dirham.
155
+ # Fact: Uses 2 decimal places for fils.
156
+ # Preset default: Uses the ISO code as the display symbol for an unambiguous English-oriented default.
157
+ # Preset default: Renders as a suffix amount.
158
+ # Source: Currency symbol conventions: https://cldr.unicode.org/translation/currency-names-and-symbols/currency-names
159
+ Amount.register :AED,
160
+ decimals: 2,
161
+ display_symbol: "AED",
162
+ display_position: :suffix,
163
+ ui_decimals: 2
@@ -0,0 +1,151 @@
1
+ # Registry overview:
2
+ # Precious And Industrial Metals:
3
+ # - This preset is a curated basket of metals commonly tracked in treasury, vault, and commodities workflows.
4
+ # - Display-unit conversion factors use standard mass relationships such as troy-ounce to gram and kilogram to pound.
5
+ # - Storage decimals here are preset defaults chosen for application headroom; they are not exchange-mandated market standards.
6
+ # - Source: NIST precious-metals conversion guidance: https://www.nist.gov/pml/weights-and-measures/laws-and-regulations/precious-metals-conversion
7
+ # - Source: NIST metric and customary conversion guidance: https://www.nist.gov/pml/weights-and-measures/common-conversion-factors-ii
8
+
9
+ # GOLD: Gold measured with troy-ounce display defaults.
10
+ # Fact: The alternate gram display uses 1 troy ounce = 31.1035 grams.
11
+ # Fact: The alternate kilogram display uses 1 troy ounce = 0.0311035 kilograms.
12
+ # Preset default: Uses 8 storage decimals as a high-precision preset default rather than a market mandate.
13
+ # Preset default: Defaults to troy-ounce display because that is common for bullion pricing.
14
+ # Source: NIST precious-metals conversion guidance: https://www.nist.gov/pml/weights-and-measures/laws-and-regulations/precious-metals-conversion
15
+ Amount.register :GOLD,
16
+ decimals: 8,
17
+ display_symbol: "oz t",
18
+ display_position: :suffix,
19
+ ui_decimals: 4,
20
+ display_units: {
21
+ oz_t: { scale: 1, symbol: "oz t", ui_decimals: 4 },
22
+ gram: { scale: "31.1035", symbol: "g", ui_decimals: 2 },
23
+ kg: { scale: "0.0311035", symbol: "kg", ui_decimals: 5 }
24
+ },
25
+ default_display: :oz_t
26
+
27
+ # SILVER: Silver measured with troy-ounce display defaults.
28
+ # Fact: The alternate gram display uses 1 troy ounce = 31.1035 grams.
29
+ # Fact: The alternate kilogram display uses 1 troy ounce = 0.0311035 kilograms.
30
+ # Preset default: Uses 8 storage decimals as a high-precision preset default.
31
+ # Preset default: Defaults to troy-ounce display for consistency with common bullion quoting.
32
+ # Source: NIST precious-metals conversion guidance: https://www.nist.gov/pml/weights-and-measures/laws-and-regulations/precious-metals-conversion
33
+ Amount.register :SILVER,
34
+ decimals: 8,
35
+ display_symbol: "oz t",
36
+ display_position: :suffix,
37
+ ui_decimals: 4,
38
+ display_units: {
39
+ oz_t: { scale: 1, symbol: "oz t", ui_decimals: 4 },
40
+ gram: { scale: "31.1035", symbol: "g", ui_decimals: 2 },
41
+ kg: { scale: "0.0311035", symbol: "kg", ui_decimals: 5 }
42
+ },
43
+ default_display: :oz_t
44
+
45
+ # PLATINUM: Platinum measured with troy-ounce display defaults.
46
+ # Fact: The alternate gram display uses 1 troy ounce = 31.1035 grams.
47
+ # Fact: The alternate kilogram display uses 1 troy ounce = 0.0311035 kilograms.
48
+ # Preset default: Uses 8 storage decimals as a high-precision preset default.
49
+ # Preset default: Defaults to troy-ounce display for commodity-style pricing.
50
+ # Source: NIST precious-metals conversion guidance: https://www.nist.gov/pml/weights-and-measures/laws-and-regulations/precious-metals-conversion
51
+ Amount.register :PLATINUM,
52
+ decimals: 8,
53
+ display_symbol: "oz t",
54
+ display_position: :suffix,
55
+ ui_decimals: 4,
56
+ display_units: {
57
+ oz_t: { scale: 1, symbol: "oz t", ui_decimals: 4 },
58
+ gram: { scale: "31.1035", symbol: "g", ui_decimals: 2 },
59
+ kg: { scale: "0.0311035", symbol: "kg", ui_decimals: 5 }
60
+ },
61
+ default_display: :oz_t
62
+
63
+ # PALLADIUM: Palladium measured with troy-ounce display defaults.
64
+ # Fact: The alternate gram display uses 1 troy ounce = 31.1035 grams.
65
+ # Fact: The alternate kilogram display uses 1 troy ounce = 0.0311035 kilograms.
66
+ # Preset default: Uses 8 storage decimals as a high-precision preset default.
67
+ # Preset default: Defaults to troy-ounce display for commodity-style pricing.
68
+ # Source: NIST precious-metals conversion guidance: https://www.nist.gov/pml/weights-and-measures/laws-and-regulations/precious-metals-conversion
69
+ Amount.register :PALLADIUM,
70
+ decimals: 8,
71
+ display_symbol: "oz t",
72
+ display_position: :suffix,
73
+ ui_decimals: 4,
74
+ display_units: {
75
+ oz_t: { scale: 1, symbol: "oz t", ui_decimals: 4 },
76
+ gram: { scale: "31.1035", symbol: "g", ui_decimals: 2 },
77
+ kg: { scale: "0.0311035", symbol: "kg", ui_decimals: 5 }
78
+ },
79
+ default_display: :oz_t
80
+
81
+ # COPPER: Copper measured with metric-mass defaults.
82
+ # Fact: The tonne display uses 1 kg = 0.001 t.
83
+ # Fact: The pound display uses 1 kg = 2.2046226218 lb.
84
+ # Preset default: Uses kilograms as the default display unit for industrial inventory and warehouse-style workflows.
85
+ # Preset default: Uses 6 storage decimals as a high-precision preset default.
86
+ # Source: NIST metric and customary conversion guidance: https://www.nist.gov/pml/weights-and-measures/common-conversion-factors-ii
87
+ Amount.register :COPPER,
88
+ decimals: 6,
89
+ display_symbol: "kg",
90
+ display_position: :suffix,
91
+ ui_decimals: 3,
92
+ display_units: {
93
+ kg: { scale: 1, symbol: "kg", ui_decimals: 3 },
94
+ tonne: { scale: "0.001", symbol: "t", ui_decimals: 6 },
95
+ lb: { scale: "2.2046226218", symbol: "lb", ui_decimals: 2 }
96
+ },
97
+ default_display: :kg
98
+
99
+ # ALUMINUM: Aluminum measured with metric-mass defaults.
100
+ # Fact: The tonne display uses 1 kg = 0.001 t.
101
+ # Fact: The pound display uses 1 kg = 2.2046226218 lb.
102
+ # Preset default: Uses kilograms as the default display unit.
103
+ # Preset default: Uses 6 storage decimals as a high-precision preset default.
104
+ # Source: NIST metric and customary conversion guidance: https://www.nist.gov/pml/weights-and-measures/common-conversion-factors-ii
105
+ Amount.register :ALUMINUM,
106
+ decimals: 6,
107
+ display_symbol: "kg",
108
+ display_position: :suffix,
109
+ ui_decimals: 3,
110
+ display_units: {
111
+ kg: { scale: 1, symbol: "kg", ui_decimals: 3 },
112
+ tonne: { scale: "0.001", symbol: "t", ui_decimals: 6 },
113
+ lb: { scale: "2.2046226218", symbol: "lb", ui_decimals: 2 }
114
+ },
115
+ default_display: :kg
116
+
117
+ # NICKEL: Nickel measured with metric-mass defaults.
118
+ # Fact: The tonne display uses 1 kg = 0.001 t.
119
+ # Fact: The pound display uses 1 kg = 2.2046226218 lb.
120
+ # Preset default: Uses kilograms as the default display unit.
121
+ # Preset default: Uses 6 storage decimals as a high-precision preset default.
122
+ # Source: NIST metric and customary conversion guidance: https://www.nist.gov/pml/weights-and-measures/common-conversion-factors-ii
123
+ Amount.register :NICKEL,
124
+ decimals: 6,
125
+ display_symbol: "kg",
126
+ display_position: :suffix,
127
+ ui_decimals: 3,
128
+ display_units: {
129
+ kg: { scale: 1, symbol: "kg", ui_decimals: 3 },
130
+ tonne: { scale: "0.001", symbol: "t", ui_decimals: 6 },
131
+ lb: { scale: "2.2046226218", symbol: "lb", ui_decimals: 2 }
132
+ },
133
+ default_display: :kg
134
+
135
+ # ZINC: Zinc measured with metric-mass defaults.
136
+ # Fact: The tonne display uses 1 kg = 0.001 t.
137
+ # Fact: The pound display uses 1 kg = 2.2046226218 lb.
138
+ # Preset default: Uses kilograms as the default display unit.
139
+ # Preset default: Uses 6 storage decimals as a high-precision preset default.
140
+ # Source: NIST metric and customary conversion guidance: https://www.nist.gov/pml/weights-and-measures/common-conversion-factors-ii
141
+ Amount.register :ZINC,
142
+ decimals: 6,
143
+ display_symbol: "kg",
144
+ display_position: :suffix,
145
+ ui_decimals: 3,
146
+ display_units: {
147
+ kg: { scale: 1, symbol: "kg", ui_decimals: 3 },
148
+ tonne: { scale: "0.001", symbol: "t", ui_decimals: 6 },
149
+ lb: { scale: "2.2046226218", symbol: "lb", ui_decimals: 2 }
150
+ },
151
+ default_display: :kg
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated by:
4
+ # rails generate amounts:registry <%= preset_source_label %>
5
+ #
6
+ # Included preset categories: <%= preset_categories.join(", ") %>
7
+ # Registered symbols: <%= registered_symbols.join(", ") %>
8
+ #
9
+ # This generator only seeds type registrations and display metadata.
10
+ # Comments marked "Fact" describe protocol, standards, or conversion details
11
+ # that are intrinsic to the asset or unit definition.
12
+ # Comments marked "Preset default" describe application-facing defaults chosen
13
+ # by this gem for a sensible starting point.
14
+ # Directional default rates are intentionally omitted because conversion policy
15
+ # is application-specific.
16
+
17
+ require "amount"
18
+ require "amount/active_record"
19
+
20
+ Rails.application.config.to_prepare do
21
+ next if Amount.registry.registered?(:<%= registry_guard_symbol %>)
22
+
23
+ <%= preset_body %>
24
+
25
+ # Register directional default rates only when your application has an
26
+ # explicit conversion policy for the pair in question.
27
+ #
28
+ # Example:
29
+ # Amount.register_default_rate :USD, :USDC, "1"
30
+ # Amount.register_default_rate :USDC, :USD, "1"
31
+
32
+ Amount.registry.lock! if Rails.env.production?
33
+ end
@@ -245,14 +245,14 @@ class AmountActiveRecordTest < Minitest::Test
245
245
  holding = ValidatedHolding.new(amount: "USDC|0.00", reserve: "USDC|1.25")
246
246
 
247
247
  refute holding.valid?
248
- assert_includes holding.errors[:amount], "must be greater than USDC|0.0"
248
+ assert_includes holding.errors[:amount], "must be greater than USDC|0.00"
249
249
  end
250
250
 
251
251
  def test_amount_validator_rejects_amount_above_upper_bound
252
252
  holding = ValidatedHolding.new(amount: "USDC|1000.01", reserve: "USDC|1.25")
253
253
 
254
254
  refute holding.valid?
255
- assert_includes holding.errors[:amount], "must be less than or equal to USDC|1000.0"
255
+ assert_includes holding.errors[:amount], "must be less than or equal to USDC|1000.00"
256
256
  end
257
257
 
258
258
  def test_amount_validator_allows_nil_when_allow_nil_is_set
@@ -271,14 +271,14 @@ class AmountActiveRecordTest < Minitest::Test
271
271
  holding = ValidatedHolding.new(amount: "USDC|100.00", reserve: "USDC|1.25", fee: 0)
272
272
 
273
273
  refute holding.valid?
274
- assert_includes holding.errors[:fee], "must be greater than SOL|0.0"
274
+ assert_includes holding.errors[:fee], "must be greater than SOL|0.0000"
275
275
  end
276
276
 
277
277
  def test_amount_validator_rejects_fixed_symbol_amount_at_exclusive_upper_bound
278
278
  holding = ValidatedHolding.new(amount: "USDC|100.00", reserve: "USDC|1.25", fee: 10)
279
279
 
280
280
  refute holding.valid?
281
- assert_includes holding.errors[:fee], "must be less than SOL|10.0"
281
+ assert_includes holding.errors[:fee], "must be less than SOL|10.0000"
282
282
  end
283
283
 
284
284
  def test_amount_validator_rejects_cross_type_threshold_without_rate
data/test/test_amount.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "json"
3
4
  require_relative "test_helper"
4
5
 
5
6
  class AmountTest < Minitest::Test
@@ -51,7 +52,7 @@ class AmountTest < Minitest::Test
51
52
  amount = Amount.parse("USDC|1.5")
52
53
 
53
54
  assert_equal 1_500_000, amount.atomic
54
- assert_equal "USDC|1.5", amount.to_s
55
+ assert_equal "USDC|1.50", amount.to_s
55
56
  end
56
57
 
57
58
  def test_parse_accepts_explicit_v1_prefix
@@ -140,7 +141,7 @@ class AmountTest < Minitest::Test
140
141
 
141
142
  assert_equal "$1.50", amount.ui
142
143
  assert_equal "1.500000", amount.formatted
143
- assert_equal "USDC|1.5", amount.to_s
144
+ assert_equal "USDC|1.50", amount.to_s
144
145
  end
145
146
 
146
147
  def test_frozen_amount_arithmetic_returns_unfrozen_result
@@ -207,6 +208,55 @@ class AmountTest < Minitest::Test
207
208
  end
208
209
  end
209
210
 
211
+ def test_trim_zeros_strips_trailing_zeros
212
+ Amount.register :TZ, decimals: 9, display_symbol: "TZ", display_position: :suffix, trim_zeros: true
213
+
214
+ assert_equal "2.5 TZ", Amount.new("2.5", :TZ).ui
215
+ assert_equal "1 TZ", Amount.new("1.0", :TZ).ui
216
+ assert_equal "0 TZ", Amount.new("0.0", :TZ).ui
217
+ assert_equal "1.123 TZ", Amount.new("1.123", :TZ).ui
218
+ assert_equal "0.00005 TZ", Amount.new(50_000, :TZ, from: :atomic).ui
219
+ end
220
+
221
+ def test_trim_zeros_false_preserves_trailing_zeros
222
+ assert_equal "$1.50", Amount.new("1.5", :USDC).ui
223
+ assert_equal "$1.00", Amount.new("1.0", :USDC).ui
224
+ end
225
+
226
+ def test_trim_zeros_with_decorated_false
227
+ Amount.register :TZ, decimals: 9, display_symbol: "TZ", display_position: :suffix, trim_zeros: true
228
+
229
+ assert_equal "2.5", Amount.new("2.5", :TZ).ui(decorated: false)
230
+ assert_equal "1", Amount.new("1.0", :TZ).ui(decorated: false)
231
+ end
232
+
233
+ def test_trim_zeros_with_display_units
234
+ Amount.register :TZG, decimals: 8, display_symbol: "oz t", display_position: :suffix,
235
+ trim_zeros: true,
236
+ display_units: { gram: { scale: "31.1035", symbol: "g", ui_decimals: 4 } }
237
+
238
+ assert_equal "31.1035 g", Amount.new("1.0", :TZG).ui(unit: :gram)
239
+ end
240
+
241
+ def test_trim_zeros_display_unit_override
242
+ Amount.register :TZO, decimals: 6, display_symbol: "T", display_position: :suffix,
243
+ trim_zeros: true,
244
+ display_units: { fixed: { scale: 1, symbol: "F", ui_decimals: 2, trim_zeros: false } }
245
+
246
+ assert_equal "1.50 F", Amount.new("1.5", :TZO).ui(unit: :fixed)
247
+ end
248
+
249
+ def test_trim_zeros_call_site_override
250
+ assert_equal "1.5 SOL", Amount.new("1.5", :SOL).ui(trim_zeros: true)
251
+ assert_equal "1.0000 SOL", Amount.new("1.0", :SOL).ui(trim_zeros: false)
252
+ end
253
+
254
+ def test_trim_zeros_call_site_overrides_registry
255
+ Amount.register :TZR, decimals: 6, display_symbol: "T", display_position: :suffix, trim_zeros: true
256
+
257
+ assert_equal "1.500000 T", Amount.new("1.5", :TZR).ui(trim_zeros: false)
258
+ end
259
+
210
260
  def test_predicates
211
261
  assert Amount.new(0, :USDC).zero?
212
262
  assert Amount.new(1, :USDC).positive?
@@ -489,6 +539,19 @@ class AmountTest < Minitest::Test
489
539
  assert_match(/missing key/, error.message)
490
540
  end
491
541
 
542
+ def test_as_json_returns_compact_string
543
+ assert_equal "USDC|1.50", Amount.new("1.5", :USDC).as_json
544
+ end
545
+
546
+ def test_to_json_returns_quoted_compact_string
547
+ assert_equal '"USDC|1.50"', Amount.new("1.5", :USDC).to_json
548
+ end
549
+
550
+ def test_as_json_works_when_nested_in_hash
551
+ hash = { amount: Amount.new("1.5", :USDC) }
552
+ assert_equal({ "amount" => "USDC|1.50" }, JSON.parse(hash.to_json))
553
+ end
554
+
492
555
  def test_load_rejects_unknown_serialization_version
493
556
  assert_raises(Amount::InvalidInput) do
494
557
  Amount.load(v: 2, atomic: "1500000", symbol: "USDC")
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+ require "tmpdir"
5
+ require "fileutils"
6
+ require_relative "../lib/amounts"
7
+ require_relative "../lib/generators/amount/active_record/registry_generator"
8
+ require "rails/generators"
9
+
10
+ class AmountRegistryGeneratorTest < Minitest::Test
11
+ def setup
12
+ @destination_root = Dir.mktmpdir("amounts-generator")
13
+ end
14
+
15
+ def teardown
16
+ FileUtils.remove_entry(@destination_root)
17
+ end
18
+
19
+ def test_generates_fiat_initializer
20
+ invoke_generator("fiat")
21
+
22
+ content = File.read(output_file)
23
+
24
+ assert_includes content, 'rails generate amounts:registry fiat'
25
+ assert_includes content, "# Registry overview:"
26
+ assert_includes content, "# Major Fiat Currencies:"
27
+ assert_includes content, "# USD: United States dollar."
28
+ assert_includes content, "# Fact: Uses 2 decimal places for cents."
29
+ assert_includes content, "# Preset default: Uses \"$\" as the default display symbol in an English-oriented UI."
30
+ assert_includes content, "Amount.register :USD"
31
+ assert_includes content, "Amount.register :JPY"
32
+ refute_includes content, "Amount.register :BTC"
33
+ end
34
+
35
+ def test_generates_all_initializer_without_duplicate_symbols
36
+ invoke_generator("all")
37
+
38
+ content = File.read(output_file)
39
+
40
+ assert_equal 1, content.scan(/^\s*Amount\.register :USD,/).size
41
+ assert_equal 1, content.scan(/^\s*Amount\.register :USDC,/).size
42
+ assert_includes content, "Amount.register :GOLD"
43
+ assert_includes content, "Amount.register :BTC"
44
+ end
45
+
46
+ def test_generates_crypto_initializer_without_stale_polygon_entry
47
+ invoke_generator("crypto")
48
+
49
+ content = File.read(output_file)
50
+
51
+ assert_includes content, "# BTC: Bitcoin."
52
+ assert_includes content, "# Source: Ethereum denomination docs: https://ethereum.org/en/developers/docs/intro-to-ether/"
53
+ assert_includes content, "Amount.register :USDT"
54
+ refute_includes content, "Amount.register :MATIC"
55
+ refute_includes content, "Amount.register :POL"
56
+ end
57
+
58
+ def test_static_preset_files_exist
59
+ assert File.exist?(preset_file("fiat"))
60
+ assert File.exist?(preset_file("metals"))
61
+ assert File.exist?(preset_file("crypto"))
62
+ assert File.exist?(preset_file("all"))
63
+ end
64
+
65
+ def test_all_static_preset_file_includes_all_major_sections
66
+ content = File.read(preset_file("all"))
67
+
68
+ assert_includes content, "# Major Fiat Currencies:"
69
+ assert_includes content, "# Precious And Industrial Metals:"
70
+ assert_includes content, "# Large-Cap Crypto Assets:"
71
+ assert_includes content, "Amount.register :USD"
72
+ assert_includes content, "Amount.register :GOLD"
73
+ assert_includes content, "Amount.register :BTC"
74
+ end
75
+
76
+ def test_rejects_unknown_preset
77
+ _stdout, stderr = capture_io do
78
+ invoke_generator("unknown")
79
+ end
80
+ assert_includes stderr, "unknown preset"
81
+ end
82
+
83
+ private
84
+
85
+ def invoke_generator(preset)
86
+ Amount::ActiveRecord::RegistryGenerator.start(
87
+ [preset],
88
+ destination_root: @destination_root,
89
+ shell: Thor::Shell::Basic.new
90
+ )
91
+ end
92
+
93
+ def output_file
94
+ File.join(@destination_root, "config/initializers/amounts.rb")
95
+ end
96
+
97
+ def preset_file(name)
98
+ File.join(
99
+ File.dirname(__FILE__),
100
+ "..",
101
+ "lib/generators/amount/active_record/templates/presets/#{name}.fragment"
102
+ )
103
+ end
104
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amounts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Seb Scholl
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-25 00:00:00.000000000 Z
11
+ date: 2026-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal
@@ -39,7 +39,6 @@ files:
39
39
  - ".rubocop.yml"
40
40
  - CHANGELOG.md
41
41
  - Gemfile
42
- - LICENSE.txt
43
42
  - README.md
44
43
  - Rakefile
45
44
  - bin/console
@@ -50,6 +49,7 @@ files:
50
49
  - lib/amount/active_record/attribute_definition.rb
51
50
  - lib/amount/active_record/migration_methods.rb
52
51
  - lib/amount/active_record/model.rb
52
+ - lib/amount/active_record/railtie.rb
53
53
  - lib/amount/active_record/rspec.rb
54
54
  - lib/amount/active_record/rspec/matchers.rb
55
55
  - lib/amount/active_record/type.rb
@@ -66,27 +66,34 @@ files:
66
66
  - lib/amount/rspec/support.rb
67
67
  - lib/amount/serialization.rb
68
68
  - lib/amount/version.rb
69
+ - lib/amounts.rb
70
+ - lib/generators/amount/active_record/registry_generator.rb
71
+ - lib/generators/amount/active_record/templates/presets/all.fragment
72
+ - lib/generators/amount/active_record/templates/presets/crypto.fragment
73
+ - lib/generators/amount/active_record/templates/presets/fiat.fragment
74
+ - lib/generators/amount/active_record/templates/presets/metals.fragment
75
+ - lib/generators/amount/active_record/templates/registry.rb.tt
69
76
  - test/dummy/app/models/holding.rb
70
77
  - test/dummy/bin/rails
71
78
  - test/dummy/config/application.rb
72
79
  - test/dummy/config/database.yml
73
80
  - test/dummy/config/environment.rb
74
81
  - test/dummy/db/schema.rb
75
- - test/dummy/log/development.log
76
82
  - test/dummy/log/test.log
77
83
  - test/postgresql_integration_test.rb
78
84
  - test/support/amount_test_support.rb
79
85
  - test/test_active_record.rb
80
86
  - test/test_amount.rb
81
87
  - test/test_helper.rb
88
+ - test/test_registry_generator.rb
82
89
  homepage: https://github.com/zarpay/amounts
83
90
  licenses:
84
91
  - MIT
85
92
  metadata:
86
93
  bug_tracker_uri: https://github.com/zarpay/amounts/issues
87
- changelog_uri: https://github.com/zarpay/amounts/blob/main/CHANGELOG.md
88
- documentation_uri: https://github.com/zarpay/amounts#readme
89
- source_code_uri: https://github.com/zarpay/amounts
94
+ changelog_uri: https://github.com/zarpay/amounts/blob/main/gem/CHANGELOG.md
95
+ documentation_uri: https://github.com/zarpay/amounts/tree/main/gem#readme
96
+ source_code_uri: https://github.com/zarpay/amounts/tree/main/gem
90
97
  post_install_message:
91
98
  rdoc_options: []
92
99
  require_paths:
data/LICENSE.txt DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Seb Scholl
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.