shopify-money 3.0.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/tests.yml +3 -3
- data/.gitignore +2 -1
- data/.rubocop.yml +36 -0
- data/.ruby-version +1 -1
- data/Gemfile +4 -1
- data/Gemfile.lock +160 -121
- data/Rakefile +2 -2
- data/config/currency_historic.yml +44 -2
- data/config/currency_iso.yml +38 -64
- data/lib/money/allocator.rb +13 -9
- data/lib/money/config.rb +8 -0
- data/lib/money/core_extensions.rb +2 -1
- data/lib/money/currency/loader.rb +4 -3
- data/lib/money/currency.rb +12 -3
- data/lib/money/deprecations.rb +22 -4
- data/lib/money/errors.rb +1 -0
- data/lib/money/helpers.rb +2 -1
- data/lib/money/money.rb +53 -29
- data/lib/money/null_currency.rb +11 -3
- data/lib/money/parser/accounting.rb +1 -0
- data/lib/money/parser/fuzzy.rb +14 -13
- data/lib/money/parser/locale_aware.rb +3 -6
- data/lib/money/parser/simple.rb +2 -1
- data/lib/money/rails/job_argument_serializer.rb +0 -1
- data/lib/money/railtie.rb +5 -3
- data/lib/money/splitter.rb +20 -24
- data/lib/money/version.rb +2 -1
- data/lib/money.rb +1 -0
- data/lib/money_column/active_record_hooks.rb +9 -6
- data/lib/money_column/active_record_type.rb +7 -4
- data/lib/money_column/railtie.rb +2 -1
- data/lib/money_column.rb +1 -0
- data/lib/rubocop/cop/money/missing_currency.rb +16 -23
- data/lib/rubocop/cop/money/zero_money.rb +7 -13
- data/lib/shopify-money.rb +1 -0
- data/money.gemspec +6 -6
- data/spec/config_spec.rb +14 -0
- data/spec/money_spec.rb +38 -0
- data/spec/rubocop/cop/money/missing_currency_spec.rb +7 -7
- data/spec/rubocop/cop/money/zero_money_spec.rb +2 -2
- metadata +18 -42
data/config/currency_iso.yml
CHANGED
@@ -105,7 +105,7 @@ ars:
|
|
105
105
|
subunit: Centavo
|
106
106
|
subunit_to_unit: 100
|
107
107
|
symbol_first: true
|
108
|
-
html_entity: "
|
108
|
+
html_entity: "$"
|
109
109
|
decimal_mark: ","
|
110
110
|
thousands_separator: "."
|
111
111
|
iso_numeric: '032'
|
@@ -225,7 +225,7 @@ bhd:
|
|
225
225
|
priority: 100
|
226
226
|
iso_code: BHD
|
227
227
|
name: Bahraini Dinar
|
228
|
-
symbol:
|
228
|
+
symbol: د.ب
|
229
229
|
alternate_symbols:
|
230
230
|
- BD
|
231
231
|
subunit: Fils
|
@@ -458,7 +458,7 @@ chf:
|
|
458
458
|
symbol_first: true
|
459
459
|
html_entity: ''
|
460
460
|
decimal_mark: "."
|
461
|
-
thousands_separator: "
|
461
|
+
thousands_separator: "'"
|
462
462
|
iso_numeric: '756'
|
463
463
|
smallest_denomination: 5
|
464
464
|
clf:
|
@@ -715,7 +715,7 @@ eur:
|
|
715
715
|
subunit: Cent
|
716
716
|
subunit_to_unit: 100
|
717
717
|
symbol_first: true
|
718
|
-
html_entity: "
|
718
|
+
html_entity: "€"
|
719
719
|
decimal_mark: ","
|
720
720
|
thousands_separator: "."
|
721
721
|
iso_numeric: '978'
|
@@ -771,7 +771,7 @@ gel:
|
|
771
771
|
priority: 100
|
772
772
|
iso_code: GEL
|
773
773
|
name: Georgian Lari
|
774
|
-
symbol:
|
774
|
+
symbol: "₾"
|
775
775
|
alternate_symbols:
|
776
776
|
- lari
|
777
777
|
subunit: Tetri
|
@@ -905,20 +905,6 @@ hnl:
|
|
905
905
|
thousands_separator: ","
|
906
906
|
iso_numeric: '340'
|
907
907
|
smallest_denomination: 5
|
908
|
-
hrk:
|
909
|
-
priority: 100
|
910
|
-
iso_code: HRK
|
911
|
-
name: Croatian Kuna
|
912
|
-
symbol: kn
|
913
|
-
alternate_symbols: []
|
914
|
-
subunit: Lipa
|
915
|
-
subunit_to_unit: 100
|
916
|
-
symbol_first: false
|
917
|
-
html_entity: ''
|
918
|
-
decimal_mark: ","
|
919
|
-
thousands_separator: "."
|
920
|
-
iso_numeric: '191'
|
921
|
-
smallest_denomination: 1
|
922
908
|
htg:
|
923
909
|
priority: 100
|
924
910
|
iso_code: HTG
|
@@ -964,7 +950,7 @@ idr:
|
|
964
950
|
ils:
|
965
951
|
priority: 100
|
966
952
|
iso_code: ILS
|
967
|
-
name: Israeli New
|
953
|
+
name: Israeli New Shekel
|
968
954
|
symbol: "₪"
|
969
955
|
alternate_symbols:
|
970
956
|
- ש״ח
|
@@ -1034,7 +1020,7 @@ isk:
|
|
1034
1020
|
- Íkr
|
1035
1021
|
subunit:
|
1036
1022
|
subunit_to_unit: 1
|
1037
|
-
symbol_first:
|
1023
|
+
symbol_first: false
|
1038
1024
|
html_entity: ''
|
1039
1025
|
decimal_mark: ","
|
1040
1026
|
thousands_separator: "."
|
@@ -1210,7 +1196,7 @@ kzt:
|
|
1210
1196
|
priority: 100
|
1211
1197
|
iso_code: KZT
|
1212
1198
|
name: Kazakhstani Tenge
|
1213
|
-
symbol: "
|
1199
|
+
symbol: "₸"
|
1214
1200
|
alternate_symbols: []
|
1215
1201
|
subunit: Tiyn
|
1216
1202
|
subunit_to_unit: 100
|
@@ -1302,34 +1288,6 @@ lsl:
|
|
1302
1288
|
thousands_separator: ","
|
1303
1289
|
iso_numeric: '426'
|
1304
1290
|
smallest_denomination: 1
|
1305
|
-
ltl:
|
1306
|
-
priority: 100
|
1307
|
-
iso_code: LTL
|
1308
|
-
name: Lithuanian Litas
|
1309
|
-
symbol: Lt
|
1310
|
-
alternate_symbols: []
|
1311
|
-
subunit: Centas
|
1312
|
-
subunit_to_unit: 100
|
1313
|
-
symbol_first: false
|
1314
|
-
html_entity: ''
|
1315
|
-
decimal_mark: "."
|
1316
|
-
thousands_separator: ","
|
1317
|
-
iso_numeric: '440'
|
1318
|
-
smallest_denomination: 1
|
1319
|
-
lvl:
|
1320
|
-
priority: 100
|
1321
|
-
iso_code: LVL
|
1322
|
-
name: Latvian Lats
|
1323
|
-
symbol: Ls
|
1324
|
-
alternate_symbols: []
|
1325
|
-
subunit: Santīms
|
1326
|
-
subunit_to_unit: 100
|
1327
|
-
symbol_first: true
|
1328
|
-
html_entity: ''
|
1329
|
-
decimal_mark: "."
|
1330
|
-
thousands_separator: ","
|
1331
|
-
iso_numeric: '428'
|
1332
|
-
smallest_denomination: 1
|
1333
1291
|
lyd:
|
1334
1292
|
priority: 100
|
1335
1293
|
iso_code: LYD
|
@@ -1620,11 +1578,12 @@ npr:
|
|
1620
1578
|
priority: 100
|
1621
1579
|
iso_code: NPR
|
1622
1580
|
name: Nepalese Rupee
|
1623
|
-
symbol:
|
1581
|
+
symbol: Rs.
|
1624
1582
|
disambiguate_symbol: NPR
|
1625
1583
|
alternate_symbols:
|
1626
1584
|
- Rs
|
1627
1585
|
- रू
|
1586
|
+
- "₨"
|
1628
1587
|
subunit: Paisa
|
1629
1588
|
subunit_to_unit: 100
|
1630
1589
|
symbol_first: true
|
@@ -1681,14 +1640,14 @@ pen:
|
|
1681
1640
|
priority: 100
|
1682
1641
|
iso_code: PEN
|
1683
1642
|
name: Peruvian Sol
|
1684
|
-
symbol: S
|
1643
|
+
symbol: S/
|
1685
1644
|
alternate_symbols: []
|
1686
1645
|
subunit: Céntimo
|
1687
1646
|
subunit_to_unit: 100
|
1688
1647
|
symbol_first: true
|
1689
|
-
html_entity: S/.
|
1690
1648
|
decimal_mark: "."
|
1691
1649
|
thousands_separator: ","
|
1650
|
+
html_entity: S/
|
1692
1651
|
iso_numeric: '604'
|
1693
1652
|
smallest_denomination: 1
|
1694
1653
|
pgk:
|
@@ -1970,6 +1929,20 @@ skk:
|
|
1970
1929
|
thousands_separator: ","
|
1971
1930
|
iso_numeric: '703'
|
1972
1931
|
smallest_denomination: 50
|
1932
|
+
sle:
|
1933
|
+
priority: 100
|
1934
|
+
iso_code: SLE
|
1935
|
+
name: New Leone
|
1936
|
+
symbol: Le
|
1937
|
+
alternate_symbols: []
|
1938
|
+
subunit: Cent
|
1939
|
+
subunit_to_unit: 100
|
1940
|
+
symbol_first: false
|
1941
|
+
html_entity: ''
|
1942
|
+
decimal_mark: "."
|
1943
|
+
thousands_separator: ","
|
1944
|
+
iso_numeric: '925'
|
1945
|
+
smallest_denomination: 1000
|
1973
1946
|
sll:
|
1974
1947
|
priority: 100
|
1975
1948
|
iso_code: SLL
|
@@ -2274,13 +2247,13 @@ uyu:
|
|
2274
2247
|
priority: 100
|
2275
2248
|
iso_code: UYU
|
2276
2249
|
name: Uruguayan Peso
|
2277
|
-
symbol: "$"
|
2250
|
+
symbol: "$U"
|
2278
2251
|
alternate_symbols:
|
2279
2252
|
- "$U"
|
2280
2253
|
subunit: Centésimo
|
2281
2254
|
subunit_to_unit: 100
|
2282
2255
|
symbol_first: true
|
2283
|
-
html_entity: "
|
2256
|
+
html_entity: "$U"
|
2284
2257
|
decimal_mark: ","
|
2285
2258
|
thousands_separator: "."
|
2286
2259
|
iso_numeric: '858'
|
@@ -2289,7 +2262,7 @@ uzs:
|
|
2289
2262
|
priority: 100
|
2290
2263
|
iso_code: UZS
|
2291
2264
|
name: Uzbekistan Som
|
2292
|
-
symbol: '
|
2265
|
+
symbol: so'm
|
2293
2266
|
alternate_symbols:
|
2294
2267
|
- so‘m
|
2295
2268
|
- сўм
|
@@ -2325,9 +2298,10 @@ ved:
|
|
2325
2298
|
ves:
|
2326
2299
|
priority: 100
|
2327
2300
|
iso_code: VES
|
2328
|
-
name: Venezuelan Bolívar
|
2329
|
-
symbol: Bs
|
2330
|
-
alternate_symbols:
|
2301
|
+
name: Venezuelan Bolívar Soberano
|
2302
|
+
symbol: Bs
|
2303
|
+
alternate_symbols:
|
2304
|
+
- Bs.S
|
2331
2305
|
subunit: Céntimo
|
2332
2306
|
subunit_to_unit: 100
|
2333
2307
|
symbol_first: true
|
@@ -2389,7 +2363,7 @@ xaf:
|
|
2389
2363
|
priority: 100
|
2390
2364
|
iso_code: XAF
|
2391
2365
|
name: Central African Cfa Franc
|
2392
|
-
symbol:
|
2366
|
+
symbol: CFA
|
2393
2367
|
disambiguate_symbol: FCFA
|
2394
2368
|
alternate_symbols:
|
2395
2369
|
- FCFA
|
@@ -2576,7 +2550,7 @@ xpt:
|
|
2576
2550
|
smallest_denomination: ''
|
2577
2551
|
xts:
|
2578
2552
|
priority: 100
|
2579
|
-
iso_code:
|
2553
|
+
iso_code: XTS
|
2580
2554
|
name: Codes specifically reserved for testing purposes
|
2581
2555
|
symbol: ''
|
2582
2556
|
alternate_symbols: []
|
@@ -2612,8 +2586,8 @@ zar:
|
|
2612
2586
|
subunit_to_unit: 100
|
2613
2587
|
symbol_first: true
|
2614
2588
|
html_entity: "R"
|
2615
|
-
decimal_mark: "
|
2616
|
-
thousands_separator: "
|
2589
|
+
decimal_mark: ","
|
2590
|
+
thousands_separator: " "
|
2617
2591
|
iso_numeric: '710'
|
2618
2592
|
smallest_denomination: 10
|
2619
2593
|
zmk:
|
@@ -2635,7 +2609,7 @@ zmw:
|
|
2635
2609
|
priority: 100
|
2636
2610
|
iso_code: ZMW
|
2637
2611
|
name: Zambian Kwacha
|
2638
|
-
symbol:
|
2612
|
+
symbol: K
|
2639
2613
|
disambiguate_symbol: ZMW
|
2640
2614
|
alternate_symbols: []
|
2641
2615
|
subunit: Ngwee
|
data/lib/money/allocator.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'delegate'
|
3
4
|
|
4
5
|
class Money
|
@@ -107,7 +108,7 @@ class Money
|
|
107
108
|
# Money.new(100).allocate_max_amounts([Money.new(5), Money.new(2)])
|
108
109
|
# #=> [Money.new(5), Money.new(2)]
|
109
110
|
def allocate_max_amounts(maximums)
|
110
|
-
allocation_currency = extract_currency(maximums + [
|
111
|
+
allocation_currency = extract_currency(maximums + [__getobj__])
|
111
112
|
maximums = maximums.map { |max| max.to_money(allocation_currency) }
|
112
113
|
maximums_total = maximums.reduce(Money.new(0, allocation_currency), :+)
|
113
114
|
|
@@ -116,16 +117,16 @@ class Money
|
|
116
117
|
Money.rational(max_amount, maximums_total)
|
117
118
|
end
|
118
119
|
|
119
|
-
total_allocatable = [maximums_total.subunits,
|
120
|
+
total_allocatable = [maximums_total.subunits, subunits].min
|
120
121
|
|
121
122
|
subunits_amounts, left_over = amounts_from_splits(1, splits, total_allocatable)
|
122
123
|
subunits_amounts.map! { |amount| amount[:whole_subunits] }
|
123
124
|
|
124
125
|
subunits_amounts.each_with_index do |amount, index|
|
125
|
-
break
|
126
|
+
break if left_over <= 0
|
126
127
|
|
127
128
|
max_amount = maximums[index].value * allocation_currency.subunit_to_unit
|
128
|
-
next
|
129
|
+
next if amount >= max_amount
|
129
130
|
|
130
131
|
left_over -= 1
|
131
132
|
subunits_amounts[index] += 1
|
@@ -137,9 +138,12 @@ class Money
|
|
137
138
|
private
|
138
139
|
|
139
140
|
def extract_currency(money_array)
|
140
|
-
currencies = money_array.lazy.select
|
141
|
+
currencies = money_array.lazy.select do |money|
|
142
|
+
money.is_a?(Money)
|
143
|
+
end.reject(&:no_currency?).map(&:currency).to_a.uniq
|
141
144
|
if currencies.size > 1
|
142
|
-
raise ArgumentError,
|
145
|
+
raise ArgumentError,
|
146
|
+
"operation not permitted for Money objects with different currencies #{currencies.join(", ")}"
|
143
147
|
end
|
144
148
|
currencies.first || NULL_CURRENCY
|
145
149
|
end
|
@@ -154,8 +158,8 @@ class Money
|
|
154
158
|
fractional_subunits = (subunits_to_split * ratio / allocations.to_r).to_f - whole_subunits
|
155
159
|
left_over -= whole_subunits
|
156
160
|
{
|
157
|
-
:
|
158
|
-
:
|
161
|
+
whole_subunits: whole_subunits,
|
162
|
+
fractional_subunits: fractional_subunits,
|
159
163
|
}
|
160
164
|
end
|
161
165
|
|
@@ -172,7 +176,7 @@ class Money
|
|
172
176
|
# the next whole number. Similarly, given the input [9.1, 5.5, 3.9] the correct ranking is *still* 2, 1, 0. This
|
173
177
|
# is because 3.9 is nearer to 4 than 9.1 is to 10.
|
174
178
|
def rank_by_nearest(amounts)
|
175
|
-
amounts.each_with_index.sort_by{ |amount,
|
179
|
+
amounts.each_with_index.sort_by { |amount, _i| 1 - amount[:fractional_subunits] }.map(&:last)
|
176
180
|
end
|
177
181
|
end
|
178
182
|
end
|
data/lib/money/config.rb
CHANGED
@@ -21,5 +21,13 @@ class Money
|
|
21
21
|
@legacy_json_format = false
|
22
22
|
@legacy_deprecations = false
|
23
23
|
end
|
24
|
+
|
25
|
+
def without_legacy_deprecations(&block)
|
26
|
+
old_legacy_deprecations = @legacy_deprecations
|
27
|
+
@legacy_deprecations = false
|
28
|
+
yield
|
29
|
+
ensure
|
30
|
+
@legacy_deprecations = old_legacy_deprecations
|
31
|
+
end
|
24
32
|
end
|
25
33
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
# Allows Writing of 100.to_money for +Numeric+ types
|
3
4
|
# 100.to_money => #<Money @cents=10000>
|
4
5
|
# 100.37.to_money => #<Money @cents=10037>
|
@@ -25,7 +26,7 @@ class String
|
|
25
26
|
return Money.new(self, currency)
|
26
27
|
end
|
27
28
|
|
28
|
-
return Money.new(0, currency) if
|
29
|
+
return Money.new(0, currency) if empty?
|
29
30
|
|
30
31
|
Money::Parser::Fuzzy.parse(self, currency).tap do |money|
|
31
32
|
old_value = money.value
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'yaml'
|
3
4
|
|
4
5
|
class Money
|
@@ -9,9 +10,9 @@ class Money
|
|
9
10
|
currency_data_path = File.expand_path("../../../../config", __FILE__)
|
10
11
|
|
11
12
|
currencies = {}
|
12
|
-
currencies.merge!
|
13
|
-
currencies.merge!
|
14
|
-
currencies.merge!
|
13
|
+
currencies.merge!(YAML.load_file("#{currency_data_path}/currency_historic.yml"))
|
14
|
+
currencies.merge!(YAML.load_file("#{currency_data_path}/currency_non_iso.yml"))
|
15
|
+
currencies.merge!(YAML.load_file("#{currency_data_path}/currency_iso.yml"))
|
15
16
|
deep_deduplicate!(currencies)
|
16
17
|
end
|
17
18
|
|
data/lib/money/currency.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "money/currency/loader"
|
3
4
|
|
4
5
|
class Money
|
@@ -27,8 +28,16 @@ class Money
|
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
30
|
-
attr_reader :iso_code,
|
31
|
-
|
31
|
+
attr_reader :iso_code,
|
32
|
+
:iso_numeric,
|
33
|
+
:name,
|
34
|
+
:smallest_denomination,
|
35
|
+
:subunit_symbol,
|
36
|
+
:subunit_to_unit,
|
37
|
+
:minor_units,
|
38
|
+
:symbol,
|
39
|
+
:disambiguate_symbol,
|
40
|
+
:decimal_mark
|
32
41
|
|
33
42
|
def initialize(currency_iso)
|
34
43
|
data = self.class.currencies[currency_iso]
|
@@ -51,7 +60,7 @@ class Money
|
|
51
60
|
end
|
52
61
|
|
53
62
|
def hash
|
54
|
-
[
|
63
|
+
[self.class, iso_code].hash
|
55
64
|
end
|
56
65
|
|
57
66
|
def compatible?(other)
|
data/lib/money/deprecations.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
Money.class_eval do
|
3
4
|
ACTIVE_SUPPORT_DEFINED = defined?(ActiveSupport)
|
5
|
+
DEPRECATION_STACKTRACE_LENGTH = 5
|
4
6
|
|
5
7
|
def self.active_support_deprecator
|
6
8
|
@active_support_deprecator ||= begin
|
@@ -11,12 +13,28 @@ Money.class_eval do
|
|
11
13
|
|
12
14
|
def self.deprecate(message)
|
13
15
|
if ACTIVE_SUPPORT_DEFINED
|
14
|
-
|
15
|
-
location.to_s.include?('gems/shopify-money')
|
16
|
-
end
|
17
|
-
active_support_deprecator.warn("[Shopify/Money] #{message}\n", external_callstack)
|
16
|
+
active_support_deprecator.warn("[Shopify/Money] #{message}\n", caller_stack)
|
18
17
|
else
|
19
18
|
Kernel.warn("DEPRECATION WARNING: [Shopify/Money] #{message}\n")
|
20
19
|
end
|
21
20
|
end
|
21
|
+
|
22
|
+
# :nocov:
|
23
|
+
if Thread.respond_to?(:each_caller_location)
|
24
|
+
def self.caller_stack
|
25
|
+
stack = []
|
26
|
+
Thread.each_caller_location do |location|
|
27
|
+
stack << location unless location.path.include?('gems/shopify-money')
|
28
|
+
break if stack.length == DEPRECATION_STACKTRACE_LENGTH
|
29
|
+
end
|
30
|
+
stack
|
31
|
+
end
|
32
|
+
else
|
33
|
+
def self.caller_stack
|
34
|
+
caller_locations(2, DEPRECATION_STACKTRACE_LENGTH * 2).reject do |location|
|
35
|
+
location.path.include?('gems/shopify-money')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
# :nocov:
|
22
40
|
end
|
data/lib/money/errors.rb
CHANGED
data/lib/money/helpers.rb
CHANGED
data/lib/money/money.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'forwardable'
|
4
|
+
require 'json'
|
3
5
|
|
4
6
|
class Money
|
5
7
|
include Comparable
|
@@ -8,6 +10,7 @@ class Money
|
|
8
10
|
NULL_CURRENCY = NullCurrency.new.freeze
|
9
11
|
|
10
12
|
attr_reader :value, :currency
|
13
|
+
|
11
14
|
def_delegators :@value, :zero?, :nonzero?, :positive?, :negative?, :to_i, :to_f, :hash
|
12
15
|
|
13
16
|
class ReverseOperationProxy
|
@@ -36,11 +39,17 @@ class Money
|
|
36
39
|
|
37
40
|
class << self
|
38
41
|
extend Forwardable
|
39
|
-
|
40
|
-
|
42
|
+
def_delegators :config, :default_currency, :default_currency=, :without_legacy_deprecations
|
43
|
+
|
44
|
+
def config
|
45
|
+
Thread.current[:shopify_money__config] ||= Config.new
|
46
|
+
end
|
47
|
+
|
48
|
+
def config=(config)
|
49
|
+
Thread.current[:shopify_money__config] = config
|
50
|
+
end
|
41
51
|
|
42
52
|
def configure
|
43
|
-
self.config ||= Config.new
|
44
53
|
yield(config) if block_given?
|
45
54
|
end
|
46
55
|
|
@@ -74,6 +83,16 @@ class Money
|
|
74
83
|
new(value, currency)
|
75
84
|
end
|
76
85
|
|
86
|
+
def from_json(string)
|
87
|
+
hash = JSON.parse(string, symbolize_names: true)
|
88
|
+
Money.new(hash.fetch(:value), hash.fetch(:currency))
|
89
|
+
end
|
90
|
+
|
91
|
+
def from_hash(hash)
|
92
|
+
hash = hash.transform_keys(&:to_sym)
|
93
|
+
Money.new(hash.fetch(:value), hash.fetch(:currency))
|
94
|
+
end
|
95
|
+
|
77
96
|
def rational(money1, money2)
|
78
97
|
money1.send(:arithmetic, money2) do
|
79
98
|
factor = money1.currency.subunit_to_unit * money2.currency.subunit_to_unit
|
@@ -94,13 +113,11 @@ class Money
|
|
94
113
|
# I18n.with_locale and ActiveSupport's Time.use_zone. This won't affect
|
95
114
|
# instances being created with explicitly set currency.
|
96
115
|
def with_currency(new_currency)
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
Money.current_currency = old_currency
|
103
|
-
end
|
116
|
+
old_currency = Money.current_currency
|
117
|
+
Money.current_currency = new_currency
|
118
|
+
yield
|
119
|
+
ensure
|
120
|
+
Money.current_currency = old_currency
|
104
121
|
end
|
105
122
|
|
106
123
|
private
|
@@ -116,10 +133,12 @@ class Money
|
|
116
133
|
return amount
|
117
134
|
end
|
118
135
|
|
119
|
-
msg = "Money.new(Money.new(amount, #{amount.currency}), #{currency})
|
136
|
+
msg = "Money.new(Money.new(amount, #{amount.currency}), #{currency}) " \
|
137
|
+
"is changing the currency of an existing money object"
|
138
|
+
|
120
139
|
if Money.config.legacy_deprecations
|
121
140
|
Money.deprecate("#{msg}. A Money::IncompatibleCurrencyError will raise in the next major release")
|
122
|
-
|
141
|
+
Money.new(amount.value, currency)
|
123
142
|
else
|
124
143
|
raise Money::IncompatibleCurrencyError, msg
|
125
144
|
end
|
@@ -184,19 +203,19 @@ class Money
|
|
184
203
|
end
|
185
204
|
end
|
186
205
|
|
187
|
-
def *(
|
188
|
-
raise ArgumentError, "Money objects can only be multiplied by a Numeric" unless
|
206
|
+
def *(other)
|
207
|
+
raise ArgumentError, "Money objects can only be multiplied by a Numeric" unless other.is_a?(Numeric)
|
189
208
|
|
190
|
-
return self if
|
191
|
-
Money.new(value.to_r *
|
209
|
+
return self if other == 1
|
210
|
+
Money.new(value.to_r * other, currency)
|
192
211
|
end
|
193
212
|
|
194
|
-
def /(
|
213
|
+
def /(other)
|
195
214
|
raise "[Money] Dividing money objects can lose pennies. Use #split instead"
|
196
215
|
end
|
197
216
|
|
198
217
|
def inspect
|
199
|
-
"#<#{self.class} value:#{self} currency:#{
|
218
|
+
"#<#{self.class} value:#{self} currency:#{currency}>"
|
200
219
|
end
|
201
220
|
|
202
221
|
def ==(other)
|
@@ -228,8 +247,10 @@ class Money
|
|
228
247
|
return Money.new(value, new_currency)
|
229
248
|
end
|
230
249
|
|
231
|
-
ensure_compatible_currency(
|
232
|
-
|
250
|
+
ensure_compatible_currency(
|
251
|
+
Helpers.value_to_currency(new_currency),
|
252
|
+
"to_money is attempting to change currency of an existing money object from #{currency} to #{new_currency}",
|
253
|
+
)
|
233
254
|
|
234
255
|
self
|
235
256
|
end
|
@@ -250,7 +271,7 @@ class Money
|
|
250
271
|
|
251
272
|
rounded_value = value.round(units)
|
252
273
|
if units == 0
|
253
|
-
|
274
|
+
format("%d", rounded_value)
|
254
275
|
else
|
255
276
|
formatted = rounded_value.to_s("F")
|
256
277
|
decimal_digits = formatted.size - formatted.index(".") - 1
|
@@ -264,7 +285,7 @@ class Money
|
|
264
285
|
alias_method :to_formatted_s, :to_fs
|
265
286
|
|
266
287
|
def to_json(options = nil)
|
267
|
-
if (options.is_a?(Hash) && options
|
288
|
+
if (options.is_a?(Hash) && options[:legacy_format]) || Money.config.legacy_json_format
|
268
289
|
to_s
|
269
290
|
else
|
270
291
|
as_json(options).to_json
|
@@ -272,12 +293,13 @@ class Money
|
|
272
293
|
end
|
273
294
|
|
274
295
|
def as_json(options = nil)
|
275
|
-
if (options.is_a?(Hash) && options
|
296
|
+
if (options.is_a?(Hash) && options[:legacy_format]) || Money.config.legacy_json_format
|
276
297
|
to_s
|
277
298
|
else
|
278
299
|
{ value: to_s(:amount), currency: currency.to_s }
|
279
300
|
end
|
280
301
|
end
|
302
|
+
alias_method :to_h, :as_json
|
281
303
|
|
282
304
|
def abs
|
283
305
|
abs = value.abs
|
@@ -291,7 +313,7 @@ class Money
|
|
291
313
|
Money.new(floor, currency)
|
292
314
|
end
|
293
315
|
|
294
|
-
def round(ndigits=0)
|
316
|
+
def round(ndigits = 0)
|
295
317
|
round = value.round(ndigits)
|
296
318
|
return self if round == value
|
297
319
|
Money.new(round, currency)
|
@@ -352,13 +374,13 @@ class Money
|
|
352
374
|
def clamp(min, max)
|
353
375
|
raise ArgumentError, 'min cannot be greater than max' if min > max
|
354
376
|
|
355
|
-
clamped_value = min if
|
356
|
-
clamped_value = max if
|
377
|
+
clamped_value = min if value < min
|
378
|
+
clamped_value = max if value > max
|
357
379
|
|
358
380
|
if clamped_value.nil?
|
359
381
|
self
|
360
382
|
else
|
361
|
-
Money.new(clamped_value,
|
383
|
+
Money.new(clamped_value, currency)
|
362
384
|
end
|
363
385
|
end
|
364
386
|
|
@@ -367,8 +389,10 @@ class Money
|
|
367
389
|
def arithmetic(other)
|
368
390
|
case other
|
369
391
|
when Money
|
370
|
-
|
371
|
-
"
|
392
|
+
desc = "mathematical operation not permitted for Money objects with different currencies " \
|
393
|
+
"#{other.currency} and #{currency}."
|
394
|
+
|
395
|
+
ensure_compatible_currency(other.currency, desc)
|
372
396
|
yield(other)
|
373
397
|
|
374
398
|
when Numeric, String
|
data/lib/money/null_currency.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class Money
|
3
4
|
# A placeholder currency for instances where no actual currency is available,
|
4
5
|
# as defined by ISO4217. You should rarely, if ever, need to use this
|
@@ -32,9 +33,16 @@ class Money
|
|
32
33
|
# #=> #<Money value:5.00 currency:CAD>
|
33
34
|
#
|
34
35
|
class NullCurrency
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
attr_reader :iso_code,
|
37
|
+
:iso_numeric,
|
38
|
+
:name,
|
39
|
+
:smallest_denomination,
|
40
|
+
:subunit_symbol,
|
41
|
+
:subunit_to_unit,
|
42
|
+
:minor_units,
|
43
|
+
:symbol,
|
44
|
+
:disambiguate_symbol,
|
45
|
+
:decimal_mark
|
38
46
|
|
39
47
|
def initialize
|
40
48
|
@symbol = '$'
|