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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/README.md +26 -1
- data/lib/amount/active_record/railtie.rb +15 -0
- data/lib/amount/display.rb +26 -8
- data/lib/amount/registry.rb +5 -2
- data/lib/amount/serialization.rb +5 -0
- data/lib/amount/version.rb +1 -1
- data/lib/amounts.rb +12 -0
- data/lib/generators/amount/active_record/registry_generator.rb +60 -0
- data/lib/generators/amount/active_record/templates/presets/all.fragment +494 -0
- data/lib/generators/amount/active_record/templates/presets/crypto.fragment +180 -0
- data/lib/generators/amount/active_record/templates/presets/fiat.fragment +163 -0
- data/lib/generators/amount/active_record/templates/presets/metals.fragment +151 -0
- data/lib/generators/amount/active_record/templates/registry.rb.tt +33 -0
- data/test/test_active_record.rb +4 -4
- data/test/test_amount.rb +65 -2
- data/test/test_registry_generator.rb +104 -0
- metadata +14 -7
- data/LICENSE.txt +0 -21
- data/test/dummy/log/development.log +0 -0
|
@@ -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
|
data/test/test_active_record.rb
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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-
|
|
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.
|