shopify-money 3.0.0 → 3.0.2
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/.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 +165 -124
- 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 +55 -30
- 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 +18 -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,12 +39,19 @@ 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.dup
|
46
|
+
end
|
47
|
+
|
48
|
+
def config=(config)
|
49
|
+
Thread.current[:shopify_money__config] = config
|
50
|
+
end
|
41
51
|
|
42
52
|
def configure
|
43
|
-
|
44
|
-
yield(config) if block_given?
|
53
|
+
@config ||= Config.new
|
54
|
+
yield(@config) if block_given?
|
45
55
|
end
|
46
56
|
|
47
57
|
def new(value = 0, currency = nil)
|
@@ -74,6 +84,16 @@ class Money
|
|
74
84
|
new(value, currency)
|
75
85
|
end
|
76
86
|
|
87
|
+
def from_json(string)
|
88
|
+
hash = JSON.parse(string, symbolize_names: true)
|
89
|
+
Money.new(hash.fetch(:value), hash.fetch(:currency))
|
90
|
+
end
|
91
|
+
|
92
|
+
def from_hash(hash)
|
93
|
+
hash = hash.transform_keys(&:to_sym)
|
94
|
+
Money.new(hash.fetch(:value), hash.fetch(:currency))
|
95
|
+
end
|
96
|
+
|
77
97
|
def rational(money1, money2)
|
78
98
|
money1.send(:arithmetic, money2) do
|
79
99
|
factor = money1.currency.subunit_to_unit * money2.currency.subunit_to_unit
|
@@ -94,13 +114,11 @@ class Money
|
|
94
114
|
# I18n.with_locale and ActiveSupport's Time.use_zone. This won't affect
|
95
115
|
# instances being created with explicitly set currency.
|
96
116
|
def with_currency(new_currency)
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
Money.current_currency = old_currency
|
103
|
-
end
|
117
|
+
old_currency = Money.current_currency
|
118
|
+
Money.current_currency = new_currency
|
119
|
+
yield
|
120
|
+
ensure
|
121
|
+
Money.current_currency = old_currency
|
104
122
|
end
|
105
123
|
|
106
124
|
private
|
@@ -116,10 +134,12 @@ class Money
|
|
116
134
|
return amount
|
117
135
|
end
|
118
136
|
|
119
|
-
msg = "Money.new(Money.new(amount, #{amount.currency}), #{currency})
|
137
|
+
msg = "Money.new(Money.new(amount, #{amount.currency}), #{currency}) " \
|
138
|
+
"is changing the currency of an existing money object"
|
139
|
+
|
120
140
|
if Money.config.legacy_deprecations
|
121
141
|
Money.deprecate("#{msg}. A Money::IncompatibleCurrencyError will raise in the next major release")
|
122
|
-
|
142
|
+
Money.new(amount.value, currency)
|
123
143
|
else
|
124
144
|
raise Money::IncompatibleCurrencyError, msg
|
125
145
|
end
|
@@ -184,19 +204,19 @@ class Money
|
|
184
204
|
end
|
185
205
|
end
|
186
206
|
|
187
|
-
def *(
|
188
|
-
raise ArgumentError, "Money objects can only be multiplied by a Numeric" unless
|
207
|
+
def *(other)
|
208
|
+
raise ArgumentError, "Money objects can only be multiplied by a Numeric" unless other.is_a?(Numeric)
|
189
209
|
|
190
|
-
return self if
|
191
|
-
Money.new(value.to_r *
|
210
|
+
return self if other == 1
|
211
|
+
Money.new(value.to_r * other, currency)
|
192
212
|
end
|
193
213
|
|
194
|
-
def /(
|
214
|
+
def /(other)
|
195
215
|
raise "[Money] Dividing money objects can lose pennies. Use #split instead"
|
196
216
|
end
|
197
217
|
|
198
218
|
def inspect
|
199
|
-
"#<#{self.class} value:#{self} currency:#{
|
219
|
+
"#<#{self.class} value:#{self} currency:#{currency}>"
|
200
220
|
end
|
201
221
|
|
202
222
|
def ==(other)
|
@@ -228,8 +248,10 @@ class Money
|
|
228
248
|
return Money.new(value, new_currency)
|
229
249
|
end
|
230
250
|
|
231
|
-
ensure_compatible_currency(
|
232
|
-
|
251
|
+
ensure_compatible_currency(
|
252
|
+
Helpers.value_to_currency(new_currency),
|
253
|
+
"to_money is attempting to change currency of an existing money object from #{currency} to #{new_currency}",
|
254
|
+
)
|
233
255
|
|
234
256
|
self
|
235
257
|
end
|
@@ -250,7 +272,7 @@ class Money
|
|
250
272
|
|
251
273
|
rounded_value = value.round(units)
|
252
274
|
if units == 0
|
253
|
-
|
275
|
+
format("%d", rounded_value)
|
254
276
|
else
|
255
277
|
formatted = rounded_value.to_s("F")
|
256
278
|
decimal_digits = formatted.size - formatted.index(".") - 1
|
@@ -264,7 +286,7 @@ class Money
|
|
264
286
|
alias_method :to_formatted_s, :to_fs
|
265
287
|
|
266
288
|
def to_json(options = nil)
|
267
|
-
if (options.is_a?(Hash) && options
|
289
|
+
if (options.is_a?(Hash) && options[:legacy_format]) || Money.config.legacy_json_format
|
268
290
|
to_s
|
269
291
|
else
|
270
292
|
as_json(options).to_json
|
@@ -272,12 +294,13 @@ class Money
|
|
272
294
|
end
|
273
295
|
|
274
296
|
def as_json(options = nil)
|
275
|
-
if (options.is_a?(Hash) && options
|
297
|
+
if (options.is_a?(Hash) && options[:legacy_format]) || Money.config.legacy_json_format
|
276
298
|
to_s
|
277
299
|
else
|
278
300
|
{ value: to_s(:amount), currency: currency.to_s }
|
279
301
|
end
|
280
302
|
end
|
303
|
+
alias_method :to_h, :as_json
|
281
304
|
|
282
305
|
def abs
|
283
306
|
abs = value.abs
|
@@ -291,7 +314,7 @@ class Money
|
|
291
314
|
Money.new(floor, currency)
|
292
315
|
end
|
293
316
|
|
294
|
-
def round(ndigits=0)
|
317
|
+
def round(ndigits = 0)
|
295
318
|
round = value.round(ndigits)
|
296
319
|
return self if round == value
|
297
320
|
Money.new(round, currency)
|
@@ -352,13 +375,13 @@ class Money
|
|
352
375
|
def clamp(min, max)
|
353
376
|
raise ArgumentError, 'min cannot be greater than max' if min > max
|
354
377
|
|
355
|
-
clamped_value = min if
|
356
|
-
clamped_value = max if
|
378
|
+
clamped_value = min if value < min
|
379
|
+
clamped_value = max if value > max
|
357
380
|
|
358
381
|
if clamped_value.nil?
|
359
382
|
self
|
360
383
|
else
|
361
|
-
Money.new(clamped_value,
|
384
|
+
Money.new(clamped_value, currency)
|
362
385
|
end
|
363
386
|
end
|
364
387
|
|
@@ -367,8 +390,10 @@ class Money
|
|
367
390
|
def arithmetic(other)
|
368
391
|
case other
|
369
392
|
when Money
|
370
|
-
|
371
|
-
"
|
393
|
+
desc = "mathematical operation not permitted for Money objects with different currencies " \
|
394
|
+
"#{other.currency} and #{currency}."
|
395
|
+
|
396
|
+
ensure_compatible_currency(other.currency, desc)
|
372
397
|
yield(other)
|
373
398
|
|
374
399
|
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 = '$'
|