shopify-money 2.2.2 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +8 -0
  3. data/.github/workflows/tests.yml +3 -3
  4. data/.gitignore +2 -1
  5. data/.rubocop.yml +36 -0
  6. data/.ruby-version +1 -1
  7. data/Gemfile +4 -1
  8. data/Gemfile.lock +167 -121
  9. data/README.md +10 -4
  10. data/Rakefile +2 -2
  11. data/config/currency_historic.yml +44 -2
  12. data/config/currency_iso.yml +38 -64
  13. data/lib/money/allocator.rb +13 -9
  14. data/lib/money/config.rb +8 -0
  15. data/lib/money/core_extensions.rb +11 -8
  16. data/lib/money/currency/loader.rb +4 -3
  17. data/lib/money/currency.rb +12 -3
  18. data/lib/money/deprecations.rb +22 -4
  19. data/lib/money/errors.rb +1 -0
  20. data/lib/money/helpers.rb +3 -6
  21. data/lib/money/money.rb +58 -35
  22. data/lib/money/null_currency.rb +11 -3
  23. data/lib/money/parser/accounting.rb +1 -0
  24. data/lib/money/parser/fuzzy.rb +16 -23
  25. data/lib/money/parser/locale_aware.rb +3 -6
  26. data/lib/money/parser/simple.rb +2 -1
  27. data/lib/money/rails/job_argument_serializer.rb +0 -1
  28. data/lib/money/railtie.rb +5 -3
  29. data/lib/money/splitter.rb +20 -24
  30. data/lib/money/version.rb +2 -1
  31. data/lib/money.rb +1 -0
  32. data/lib/money_column/active_record_hooks.rb +9 -6
  33. data/lib/money_column/active_record_type.rb +7 -4
  34. data/lib/money_column/railtie.rb +2 -1
  35. data/lib/money_column.rb +1 -0
  36. data/lib/rubocop/cop/money/missing_currency.rb +16 -23
  37. data/lib/rubocop/cop/money/zero_money.rb +7 -13
  38. data/lib/shopify-money.rb +1 -0
  39. data/money.gemspec +6 -6
  40. data/spec/config_spec.rb +14 -0
  41. data/spec/core_extensions_spec.rb +6 -2
  42. data/spec/deprecations_spec.rb +1 -2
  43. data/spec/helpers_spec.rb +2 -5
  44. data/spec/money_spec.rb +49 -14
  45. data/spec/parser/accounting_spec.rb +2 -5
  46. data/spec/parser/fuzzy_spec.rb +7 -16
  47. data/spec/rubocop/cop/money/missing_currency_spec.rb +7 -7
  48. data/spec/rubocop/cop/money/zero_money_spec.rb +2 -2
  49. metadata +19 -42
@@ -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 Sheqel
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: true
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 soberano
2329
- symbol: Bs.S.
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: Fr
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: xts
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: ZK
2612
+ symbol: K
2639
2613
  disambiguate_symbol: ZMW
2640
2614
  alternate_symbols: []
2641
2615
  subunit: Ngwee
@@ -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 + [self.__getobj__])
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, self.subunits].min
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 unless left_over > 0
126
+ break if left_over <= 0
126
127
 
127
128
  max_amount = maximums[index].value * allocation_currency.subunit_to_unit
128
- next unless amount < max_amount
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 { |money| money.is_a?(Money) }.reject(&:no_currency?).map(&:currency).to_a.uniq
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, "operation not permitted for Money objects with different currencies #{currencies.join(', ')}"
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
- :whole_subunits => whole_subunits,
158
- :fractional_subunits => fractional_subunits
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, i| 1 - amount[:fractional_subunits] }.map(&:last)
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>
@@ -20,18 +21,20 @@ class String
20
21
  return Money.new(self, currency)
21
22
  end
22
23
 
24
+ new_value = BigDecimal(self, exception: false)&.round(currency.minor_units)
25
+ unless new_value.nil?
26
+ return Money.new(self, currency)
27
+ end
28
+
29
+ return Money.new(0, currency) if empty?
30
+
23
31
  Money::Parser::Fuzzy.parse(self, currency).tap do |money|
24
- new_value = BigDecimal(self, exception: false)&.round(currency.minor_units)
25
32
  old_value = money.value
26
33
 
27
34
  if new_value != old_value
28
- message = "`\"#{self}\".to_money` will soon behave like `Money.new(\"#{self}\")` and "
29
- message +=
30
- if new_value.nil?
31
- "raise an ArgumentError exception. Use the browser's locale to parse money strings."
32
- else
33
- "return #{new_value} instead of #{old_value}."
34
- end
35
+ message = "`\"#{self}\".to_money` will soon behave like `Money.new(\"#{self}\")` and " \
36
+ "raise an ArgumentError exception. Use the browser's locale to parse money strings."
37
+
35
38
  Money.deprecate(message)
36
39
  end
37
40
  end
@@ -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! YAML.load_file("#{currency_data_path}/currency_historic.yml")
13
- currencies.merge! YAML.load_file("#{currency_data_path}/currency_non_iso.yml")
14
- currencies.merge! YAML.load_file("#{currency_data_path}/currency_iso.yml")
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
 
@@ -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, :iso_numeric, :name, :smallest_denomination, :subunit_symbol,
31
- :subunit_to_unit, :minor_units, :symbol, :disambiguate_symbol, :decimal_mark
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
- [ self.class, iso_code ].hash
63
+ [self.class, iso_code].hash
55
64
  end
56
65
 
57
66
  def compatible?(other)
@@ -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
- external_callstack = caller_locations.reject do |location|
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
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Money
3
4
  class Error < StandardError
4
5
  end
data/lib/money/helpers.rb CHANGED
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'bigdecimal'
3
4
 
4
5
  class Money
5
6
  module Helpers
6
- module_function
7
+ extend self
7
8
 
8
9
  DECIMAL_ZERO = BigDecimal(0).freeze
9
10
  MAX_DECIMAL = 21
@@ -29,11 +30,7 @@ class Money
29
30
  when Rational
30
31
  BigDecimal(num, MAX_DECIMAL)
31
32
  when String
32
- decimal = BigDecimal(num, exception: !Money.config.legacy_deprecations)
33
- return decimal if decimal
34
-
35
- Money.deprecate("using Money.new('#{num}') is deprecated and will raise an ArgumentError in the next major release")
36
- DECIMAL_ZERO
33
+ BigDecimal(num)
37
34
  else
38
35
  raise ArgumentError, "could not parse as decimal #{num.inspect}"
39
36
  end