shopify-money 3.2.7 → 4.1.0

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/tests.yml +12 -12
  3. data/.gitignore +3 -0
  4. data/.rubocop.yml +1 -1
  5. data/.ruby-version +1 -1
  6. data/Gemfile +4 -1
  7. data/Gemfile.lock +72 -28
  8. data/README.md +39 -0
  9. data/Rakefile +28 -1
  10. data/Steepfile +20 -0
  11. data/config/currency_iso.yml +1 -4
  12. data/dev.yml +7 -1
  13. data/lib/money/allocator.rb +8 -2
  14. data/lib/money/config.rb +3 -10
  15. data/lib/money/core_extensions.rb +1 -22
  16. data/lib/money/currency/loader.rb +8 -1
  17. data/lib/money/currency.rb +13 -0
  18. data/lib/money/helpers.rb +1 -10
  19. data/lib/money/money.rb +8 -20
  20. data/lib/money/null_currency.rb +0 -1
  21. data/lib/money/version.rb +1 -1
  22. data/lib/money_column/active_record_hooks.rb +5 -13
  23. data/money.gemspec +3 -2
  24. data/rbs_collection.lock.yaml +440 -0
  25. data/rbs_collection.yaml +14 -0
  26. data/sig/core_extensions.rbs +9 -0
  27. data/sig/money/allocator.rbs +31 -0
  28. data/sig/money/config.rbs +33 -0
  29. data/sig/money/converters.rbs +32 -0
  30. data/sig/money/currency.rbs +49 -0
  31. data/sig/money/errors.rbs +9 -0
  32. data/sig/money/helpers.rbs +15 -0
  33. data/sig/money/null_currency.rbs +22 -0
  34. data/sig/money/parser.rbs +49 -0
  35. data/sig/money/splitter.rbs +28 -0
  36. data/sig/money/version.rbs +5 -0
  37. data/sig/money.rbs +103 -0
  38. data/sig/money_column.rbs +71 -0
  39. data/spec/allocator_spec.rb +24 -1
  40. data/spec/config_spec.rb +25 -39
  41. data/spec/core_extensions_spec.rb +5 -19
  42. data/spec/currency/loader_spec.rb +52 -0
  43. data/spec/currency_spec.rb +118 -1
  44. data/spec/deprecations_spec.rb +1 -1
  45. data/spec/helpers_spec.rb +2 -8
  46. data/spec/money_column_spec.rb +7 -37
  47. data/spec/money_spec.rb +7 -75
  48. data/spec/spec_helper.rb +14 -5
  49. metadata +35 -6
  50. data/UPGRADING.md +0 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e07bfd5c8705cab2343063d668fdb4659127ab09519903cc616428e997f49a8
4
- data.tar.gz: 5882ced49ceb284b2f84a77b1c9991c7fc76a5e645af18fbe09d58a6ed13a456
3
+ metadata.gz: cadc7891cca0acc6f6128c570b259a5c1bead3a6a87175ebe3def93d9a618afb
4
+ data.tar.gz: 3fcc02abc7af2a86aba449ed135361a1024a18975430c8075f4b36d36d70c72a
5
5
  SHA512:
6
- metadata.gz: 3f5854c8d9a0f6d43850dfdbda770e5ea8af4ac64852715a934f2bd3043701bb5a6da52390abb8d033b105f7141f1b42c820f31e8e987a9bdf5b421e41fc4a0a
7
- data.tar.gz: 0e64426c69688957315be52bee4b23369a6492e8e2fcf403594387026a7b8a4dea7f77c21f01e137060ad2fb1211d828d092783b61b02f26a2f6c147374901fb
6
+ metadata.gz: 982ccb02e8ad20c3ad3ba9d217e0a53531d370cf32e7664947d859997ee2bc493614908f722fd1dc1ee0f234e114cb4c7e3552c85401d978afac4d462e35344c
7
+ data.tar.gz: 4a5e188fb292ad0ca2afdc2ba3dbcde89507a352d34ac7b7964870fc1584c0f9fd10d1036bd5b78a1971649d107b89f549f3b38a4f9bad823e829ac0c60d29ea
@@ -9,26 +9,26 @@ jobs:
9
9
 
10
10
  strategy:
11
11
  matrix:
12
- ruby: ['3.1', '3.2', '3.3', '3.4']
12
+ ruby: ['3.2', '3.3', '3.4', '4.0']
13
13
 
14
14
  name: Ruby ${{ matrix.ruby }}
15
15
  steps:
16
16
  - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
17
17
  - name: Set up Ruby ${{ matrix.ruby }}
18
- uses: ruby/setup-ruby@e34163cd15f4bb403dcd72d98e295997e6a55798 # v1.238.0
18
+ uses: ruby/setup-ruby@4fc31e1c823882afd7ef55985266a526c589de90 # v1.238.0
19
19
  with:
20
20
  ruby-version: ${{ matrix.ruby }}
21
- - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
21
+ bundler-cache: true
22
+ - name: Cache RBS collection
23
+ uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
22
24
  with:
23
- path: vendor/bundle
24
- key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
25
+ path: .gem_rbs_collection
26
+ key: ${{ runner.os }}-rbs-${{ hashFiles('rbs_collection.lock.yaml') }}
25
27
  restore-keys: |
26
- ${{ runner.os }}-gems-
27
- - name: Install dependencies
28
- run: |
29
- gem install bundler
30
- bundle config path vendor/bundle
31
- bundle install --jobs 4 --retry 3
28
+ ${{ runner.os }}-rbs-
32
29
  - name: Run tests
33
30
  run: |
34
- bundle exec rake
31
+ bundle exec rake spec
32
+ - name: Run typecheck
33
+ run: |
34
+ bundle exec rake typecheck
data/.gitignore CHANGED
@@ -49,3 +49,6 @@ pkg
49
49
 
50
50
  .rspec_status
51
51
  .DS_Store
52
+
53
+ # RBS collection (installed via rbs collection install)
54
+ .gem_rbs_collection
data/.rubocop.yml CHANGED
@@ -1,4 +1,4 @@
1
- require:
1
+ plugins:
2
2
  - rubocop-performance
3
3
 
4
4
  inherit_gem:
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.4.2
1
+ 4.0.1
data/Gemfile CHANGED
@@ -3,8 +3,11 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "pry-byebug", require: false
6
+ gem "readline", require: false
6
7
  gem "rubocop", require: false
7
- gem "rubocop-shopify", ">=2.8.0", require: false
8
+ gem "rubocop-shopify", require: false
8
9
  gem "rubocop-performance", require: false
9
10
 
10
11
  gemspec
12
+
13
+ gem "steep", "~> 1.10"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shopify-money (3.2.7)
4
+ shopify-money (4.1.0)
5
5
  bigdecimal (>= 3.0)
6
6
 
7
7
  GEM
@@ -81,13 +81,14 @@ GEM
81
81
  ast (2.4.3)
82
82
  base64 (0.2.0)
83
83
  benchmark (0.4.0)
84
- bigdecimal (3.2.2)
84
+ bigdecimal (3.3.1)
85
85
  builder (3.3.0)
86
86
  byebug (12.0.0)
87
87
  coderay (1.1.3)
88
88
  concurrent-ruby (1.3.5)
89
89
  connection_pool (2.5.0)
90
90
  crass (1.0.6)
91
+ csv (3.3.5)
91
92
  database_cleaner (2.0.2)
92
93
  database_cleaner-active_record (>= 2, < 3)
93
94
  database_cleaner-active_record (2.2.0)
@@ -99,6 +100,13 @@ GEM
99
100
  docile (1.4.1)
100
101
  drb (2.2.1)
101
102
  erubi (1.13.1)
103
+ ffi (1.17.3-aarch64-linux-gnu)
104
+ ffi (1.17.3-arm-linux-gnu)
105
+ ffi (1.17.3-arm64-darwin)
106
+ ffi (1.17.3-x86-linux-gnu)
107
+ ffi (1.17.3-x86_64-darwin)
108
+ ffi (1.17.3-x86_64-linux-gnu)
109
+ fileutils (1.7.3)
102
110
  globalid (1.2.1)
103
111
  activesupport (>= 6.1)
104
112
  i18n (1.14.7)
@@ -111,6 +119,9 @@ GEM
111
119
  json (2.13.2)
112
120
  language_server-protocol (3.17.0.5)
113
121
  lint_roller (1.1.0)
122
+ listen (3.9.0)
123
+ rb-fsevent (~> 0.10, >= 0.10.3)
124
+ rb-inotify (~> 0.9, >= 0.9.10)
114
125
  logger (1.7.0)
115
126
  loofah (2.24.0)
116
127
  crass (~> 1.0.2)
@@ -123,8 +134,10 @@ GEM
123
134
  marcel (1.0.4)
124
135
  method_source (1.1.0)
125
136
  mini_mime (1.1.5)
126
- mini_portile2 (2.8.8)
127
- minitest (5.25.5)
137
+ mini_portile2 (2.8.9)
138
+ minitest (6.0.1)
139
+ prism (~> 1.5)
140
+ mutex_m (0.3.0)
128
141
  net-imap (0.5.7)
129
142
  date
130
143
  net-protocol
@@ -135,18 +148,18 @@ GEM
135
148
  net-smtp (0.5.1)
136
149
  net-protocol
137
150
  nio4r (2.7.4)
138
- nokogiri (1.18.8)
151
+ nokogiri (1.19.0)
139
152
  mini_portile2 (~> 2.8.2)
140
153
  racc (~> 1.4)
141
- nokogiri (1.18.8-aarch64-linux-gnu)
154
+ nokogiri (1.19.0-aarch64-linux-gnu)
142
155
  racc (~> 1.4)
143
- nokogiri (1.18.8-arm-linux-gnu)
156
+ nokogiri (1.19.0-arm-linux-gnu)
144
157
  racc (~> 1.4)
145
- nokogiri (1.18.8-arm64-darwin)
158
+ nokogiri (1.19.0-arm64-darwin)
146
159
  racc (~> 1.4)
147
- nokogiri (1.18.8-x86_64-darwin)
160
+ nokogiri (1.19.0-x86_64-darwin)
148
161
  racc (~> 1.4)
149
- nokogiri (1.18.8-x86_64-linux-gnu)
162
+ nokogiri (1.19.0-x86_64-linux-gnu)
150
163
  racc (~> 1.4)
151
164
  ostruct (0.6.1)
152
165
  parallel (1.27.0)
@@ -156,7 +169,7 @@ GEM
156
169
  pp (0.6.2)
157
170
  prettyprint
158
171
  prettyprint (0.2.0)
159
- prism (1.4.0)
172
+ prism (1.9.0)
160
173
  pry (0.15.2)
161
174
  coderay (~> 1.1)
162
175
  method_source (~> 1.0)
@@ -206,8 +219,15 @@ GEM
206
219
  zeitwerk (~> 2.6)
207
220
  rainbow (3.1.1)
208
221
  rake (13.2.1)
222
+ rb-fsevent (0.11.2)
223
+ rb-inotify (0.11.1)
224
+ ffi (~> 1.0)
225
+ rbs (3.9.5)
226
+ logger
209
227
  rdoc (6.13.1)
210
228
  psych (>= 4.0.0)
229
+ readline (0.0.4)
230
+ reline
211
231
  regexp_parser (2.11.0)
212
232
  reline (0.6.1)
213
233
  io-console (~> 0.5)
@@ -224,7 +244,7 @@ GEM
224
244
  diff-lcs (>= 1.2.0, < 2.0)
225
245
  rspec-support (~> 3.13.0)
226
246
  rspec-support (3.13.1)
227
- rubocop (1.79.1)
247
+ rubocop (1.84.2)
228
248
  json (~> 2.3)
229
249
  language_server-protocol (~> 3.17.0.2)
230
250
  lint_roller (~> 1.1.0)
@@ -232,17 +252,17 @@ GEM
232
252
  parser (>= 3.3.0.2)
233
253
  rainbow (>= 2.2.2, < 4.0)
234
254
  regexp_parser (>= 2.9.3, < 3.0)
235
- rubocop-ast (>= 1.46.0, < 2.0)
255
+ rubocop-ast (>= 1.49.0, < 2.0)
236
256
  ruby-progressbar (~> 1.7)
237
257
  unicode-display_width (>= 2.4.0, < 4.0)
238
- rubocop-ast (1.46.0)
258
+ rubocop-ast (1.49.0)
239
259
  parser (>= 3.3.7.2)
240
- prism (~> 1.4)
241
- rubocop-performance (1.25.0)
260
+ prism (~> 1.7)
261
+ rubocop-performance (1.26.1)
242
262
  lint_roller (~> 1.1)
243
263
  rubocop (>= 1.75.0, < 2.0)
244
- rubocop-ast (>= 1.38.0, < 2.0)
245
- rubocop-shopify (2.17.0)
264
+ rubocop-ast (>= 1.47.1, < 2.0)
265
+ rubocop-shopify (2.18.0)
246
266
  rubocop (~> 1.62)
247
267
  ruby-progressbar (1.13.0)
248
268
  securerandom (0.4.1)
@@ -252,20 +272,41 @@ GEM
252
272
  simplecov_json_formatter (~> 0.1)
253
273
  simplecov-html (0.13.1)
254
274
  simplecov_json_formatter (0.1.4)
255
- sqlite3 (2.6.0-aarch64-linux-gnu)
256
- sqlite3 (2.6.0-arm-linux-gnu)
257
- sqlite3 (2.6.0-arm64-darwin)
258
- sqlite3 (2.6.0-x86-linux-gnu)
259
- sqlite3 (2.6.0-x86_64-darwin)
260
- sqlite3 (2.6.0-x86_64-linux-gnu)
261
- stringio (3.1.6)
275
+ sqlite3 (2.9.0-aarch64-linux-gnu)
276
+ sqlite3 (2.9.0-arm-linux-gnu)
277
+ sqlite3 (2.9.0-arm64-darwin)
278
+ sqlite3 (2.9.0-x86-linux-gnu)
279
+ sqlite3 (2.9.0-x86_64-darwin)
280
+ sqlite3 (2.9.0-x86_64-linux-gnu)
281
+ steep (1.10.0)
282
+ activesupport (>= 5.1)
283
+ concurrent-ruby (>= 1.1.10)
284
+ csv (>= 3.0.9)
285
+ fileutils (>= 1.1.0)
286
+ json (>= 2.1.0)
287
+ language_server-protocol (>= 3.17.0.4, < 4.0)
288
+ listen (~> 3.0)
289
+ logger (>= 1.3.0)
290
+ mutex_m (>= 0.3.0)
291
+ parser (>= 3.1)
292
+ rainbow (>= 2.2.2, < 4.0)
293
+ rbs (~> 3.9)
294
+ securerandom (>= 0.1)
295
+ strscan (>= 1.0.0)
296
+ terminal-table (>= 2, < 5)
297
+ uri (>= 0.12.0)
298
+ stringio (3.2.0)
299
+ strscan (3.1.5)
300
+ terminal-table (4.0.0)
301
+ unicode-display_width (>= 1.1.1, < 4)
262
302
  thor (1.3.2)
263
303
  timeout (0.4.3)
264
304
  tzinfo (2.0.6)
265
305
  concurrent-ruby (~> 1.0)
266
306
  unicode-display_width (3.1.4)
267
307
  unicode-emoji (~> 4.0, >= 4.0.4)
268
- unicode-emoji (4.0.4)
308
+ unicode-emoji (4.2.0)
309
+ uri (1.0.4)
269
310
  useragent (0.16.11)
270
311
  websocket-driver (0.7.7)
271
312
  base64
@@ -287,13 +328,16 @@ DEPENDENCIES
287
328
  ostruct
288
329
  pry-byebug
289
330
  rails (~> 7.2)
331
+ rbs (~> 3.0)
332
+ readline
290
333
  rspec (~> 3.2)
291
334
  rubocop
292
335
  rubocop-performance
293
- rubocop-shopify (>= 2.8.0)
336
+ rubocop-shopify
294
337
  shopify-money!
295
338
  simplecov
296
339
  sqlite3
340
+ steep (~> 1.10)
297
341
 
298
342
  BUNDLED WITH
299
- 2.6.7
343
+ 4.0.6
data/README.md CHANGED
@@ -157,6 +157,23 @@ Money.new(money.value * exchange_rate, "JPY")
157
157
  money.convert_currency(exchange_rate, "JPY")
158
158
  ```
159
159
 
160
+ ### Allocation Strategy
161
+
162
+ By default `allocate` distributes leftover subunits using the `:roundrobin` strategy.
163
+ You can change the default:
164
+
165
+ ``` ruby
166
+ Money.configure do |config|
167
+ config.default_allocation_strategy = :nearest
168
+ end
169
+ ```
170
+
171
+ Or pass a strategy per call:
172
+
173
+ ``` ruby
174
+ Money.new(10.55, "USD").allocate([0.25, 0.5, 0.25], :nearest)
175
+ ```
176
+
160
177
  ### Crypto Currencies
161
178
  To enable support for currencies listed in `crypto.yml` use
162
179
  ``` ruby
@@ -165,6 +182,28 @@ Money.configure do |config|
165
182
  end
166
183
  ```
167
184
 
185
+ ### Custom Currencies
186
+ To load custom currencies from a YAML file:
187
+ ``` ruby
188
+ Money.configure do |config|
189
+ config.experimental_custom_currency_path = Rails.root.join("config/custom_currencies.yml")
190
+ end
191
+ ```
192
+
193
+ ``` yaml
194
+ # config/custom_currencies.yml
195
+ credits:
196
+ iso_code: "CREDITS"
197
+ name: "Loyalty Points"
198
+ symbol: "CR"
199
+ disambiguate_symbol: "CR"
200
+ subunit_to_unit: 1
201
+ smallest_denomination: 1
202
+ decimal_mark: "."
203
+ ```
204
+
205
+ Custom currencies are looked up after ISO and crypto currencies, so they cannot shadow built-in currencies.
206
+
168
207
  ### Converters
169
208
  The Money gem provides a flexible converter system for handling different subunit formats. This is particularly useful when working with payment providers or APIs that have their own conventions for handling currency subunits.
170
209
 
data/Rakefile CHANGED
@@ -30,7 +30,34 @@ RSpec::Core::RakeTask.new(:rcov) do |spec|
30
30
  spec.rcov = true
31
31
  end
32
32
 
33
- task default: :spec
33
+ namespace :rbs do
34
+ desc "Install RBS collection"
35
+ task :install do
36
+ sh "rbs collection install > /dev/null"
37
+ end
38
+
39
+ desc "Validate RBS type signatures"
40
+ task validate: :install do
41
+ sh "rbs -I sig validate"
42
+ end
43
+
44
+ desc "List RBS files"
45
+ task :list do
46
+ sh "find sig -name '*.rbs'"
47
+ end
48
+ end
49
+
50
+ namespace :steep do
51
+ desc "Type check Ruby code against RBS signatures"
52
+ task check: "rbs:install" do
53
+ sh "steep check --jobs 2"
54
+ end
55
+ end
56
+
57
+ desc "Run all type checks (RBS validation + Steep type checking)"
58
+ task typecheck: ["rbs:validate", "steep:check"]
59
+
60
+ task default: [:spec, :typecheck]
34
61
 
35
62
  require 'rake/task'
36
63
  RDoc::Task.new do |rdoc|
data/Steepfile ADDED
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ target :lib do
4
+ signature "sig"
5
+
6
+ check "lib"
7
+ ignore "lib/rubocop/**/*.rb" # RuboCop cops use metaprogramming DSL
8
+ ignore "lib/money/railtie.rb" # Rails types
9
+ ignore "lib/money/deprecations.rb" # Uses class_eval
10
+ ignore "lib/money/rails/**/*.rb" # Rails-specific code
11
+ ignore "lib/money_column/railtie.rb" # Rails types
12
+ ignore "lib/money_column/active_record_hooks.rb" # Heavy metaprogramming with define_method
13
+
14
+ library "bigdecimal"
15
+ library "json"
16
+ library "forwardable"
17
+ library "yaml"
18
+
19
+ configure_code_diagnostics(Steep::Diagnostic::Ruby.lenient)
20
+ end
@@ -420,7 +420,6 @@ cad:
420
420
  alternate_symbols:
421
421
  - C$
422
422
  - CAD$
423
- subunit_symbol: "¢"
424
423
  subunit: Cent
425
424
  subunit_to_unit: 100
426
425
  symbol_first: true
@@ -758,7 +757,6 @@ gbp:
758
757
  name: British Pound
759
758
  symbol: "£"
760
759
  alternate_symbols: []
761
- subunit_symbol: p
762
760
  subunit: Penny
763
761
  subunit_to_unit: 100
764
762
  symbol_first: true
@@ -1339,7 +1337,7 @@ mga:
1339
1337
  symbol: Ar
1340
1338
  alternate_symbols: []
1341
1339
  subunit: Iraimbilanja
1342
- subunit_to_unit: 5
1340
+ subunit_to_unit: 1
1343
1341
  symbol_first: true
1344
1342
  html_entity: ''
1345
1343
  decimal_mark: "."
@@ -2235,7 +2233,6 @@ usd:
2235
2233
  alternate_symbols:
2236
2234
  - US$
2237
2235
  subunit: Cent
2238
- subunit_symbol: "¢"
2239
2236
  subunit_to_unit: 100
2240
2237
  symbol_first: true
2241
2238
  html_entity: "$"
data/dev.yml CHANGED
@@ -2,8 +2,14 @@
2
2
  # describe the required dependencies
3
3
  ---
4
4
  name: money
5
+
6
+ dev.edition: 2024
7
+ nix: true
8
+
5
9
  up:
10
+ - packages:
11
+ - libyaml
6
12
  - ruby
7
13
  - bundler
8
14
  commands:
9
- test: bundle exec rspec
15
+ test: bundle exec rake
@@ -33,6 +33,8 @@ class Money
33
33
  # - `:roundrobin_reverse`: leftover subunits will be accumulated starting from the last allocation right to left
34
34
  # - `:nearest`: leftover subunits will by given first to the party closest to the next whole subunit
35
35
  #
36
+ # The default strategy can be changed via `Money::Config.current.default_allocation_strategy`.
37
+ #
36
38
  # @example
37
39
  # Money.new(5, "USD").allocate([0.50, 0.25, 0.25])
38
40
  # #=> [#<Money value:2.50 currency:USD>, #<Money value:1.25 currency:USD>, #<Money value:1.25 currency:USD>]
@@ -57,12 +59,16 @@ class Money
57
59
  # Money.new(10.55, "USD").allocate([0.25, 0.5, 0.25], :nearest)
58
60
  # #=> [#<Money value:2.64 currency:USD>, #<Money value:5.27 currency:USD>, #<Money value:2.64 currency:USD>]
59
61
 
60
- def allocate(splits, strategy = :roundrobin)
62
+ def allocate(splits, strategy = nil)
61
63
  if splits.empty?
62
64
  raise ArgumentError, 'at least one split must be provided'
63
65
  end
64
66
 
65
- splits.map!(&:to_r)
67
+ strategy ||= Money::Config.current.default_allocation_strategy
68
+
69
+ # Float#to_r preserves float imprecision (0.98.to_r != 98/100).
70
+ # Rationalize gives the clean fraction (0.98.rationalize == 49/50).
71
+ splits.map!(&:rationalize)
66
72
  allocations = splits.inject(0, :+)
67
73
 
68
74
  if (allocations - ONE) > Float::EPSILON
data/lib/money/config.rb CHANGED
@@ -41,7 +41,7 @@ class Money
41
41
  end
42
42
  end
43
43
 
44
- attr_accessor :legacy_json_format, :legacy_deprecations, :experimental_crypto_currencies, :default_subunit_format
44
+ attr_accessor :legacy_json_format, :experimental_crypto_currencies, :default_subunit_format, :experimental_custom_currency_path, :default_allocation_strategy
45
45
 
46
46
  attr_reader :default_currency
47
47
  alias_method :currency, :default_currency
@@ -59,14 +59,6 @@ class Money
59
59
  end
60
60
  alias_method :currency=, :default_currency=
61
61
 
62
- def legacy_default_currency!
63
- @default_currency ||= Money::NULL_CURRENCY
64
- end
65
-
66
- def legacy_deprecations!
67
- @legacy_deprecations = true
68
- end
69
-
70
62
  def legacy_json_format!
71
63
  @legacy_json_format = true
72
64
  end
@@ -78,9 +70,10 @@ class Money
78
70
  def initialize
79
71
  @default_currency = nil
80
72
  @legacy_json_format = false
81
- @legacy_deprecations = false
82
73
  @experimental_crypto_currencies = false
83
74
  @default_subunit_format = :iso4217
75
+ @experimental_custom_currency_path = nil
76
+ @default_allocation_strategy = :roundrobin
84
77
  end
85
78
  end
86
79
  end
@@ -16,27 +16,6 @@ end
16
16
  class String
17
17
  def to_money(currency = nil)
18
18
  currency = Money::Helpers.value_to_currency(currency)
19
-
20
- unless Money::Config.current.legacy_deprecations
21
- return Money.new(self, currency)
22
- end
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
-
31
- Money::Parser::Fuzzy.parse(self, currency).tap do |money|
32
- old_value = money.value
33
-
34
- if new_value != old_value
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
-
38
- Money.deprecate(message)
39
- end
40
- end
19
+ Money.new(self, currency)
41
20
  end
42
21
  end
@@ -5,7 +5,7 @@ require 'yaml'
5
5
  class Money
6
6
  class Currency
7
7
  module Loader
8
- CURRENCY_DATA_PATH = File.expand_path("../../../config", __dir__)
8
+ CURRENCY_DATA_PATH = File.expand_path("../../../config", __dir__ || raise("__dir__ is nil"))
9
9
 
10
10
  class << self
11
11
  def load_currencies
@@ -22,6 +22,13 @@ class Money
22
22
  deep_deduplicate!(currencies)
23
23
  end
24
24
 
25
+ def load_custom_currencies(path)
26
+ raise ArgumentError, "Custom currency file not found: #{path}" unless File.exist?(path)
27
+ data = YAML.safe_load_file(path, permitted_classes: [])
28
+ raise ArgumentError, "Custom currency file must contain a YAML hash" unless data.is_a?(Hash)
29
+ deep_deduplicate!(data)
30
+ end
31
+
25
32
  private
26
33
 
27
34
  def deep_deduplicate!(data)
@@ -31,6 +31,15 @@ class Money
31
31
  @@crypto_currencies ||= Loader.load_crypto_currencies
32
32
  end
33
33
 
34
+ def custom_currencies(path)
35
+ @@custom_currencies_cache ||= {}
36
+ @@custom_currencies_cache[path] ||= Loader.load_custom_currencies(path)
37
+ end
38
+
39
+ def reset_custom_currencies
40
+ @@custom_currencies_cache = nil
41
+ end
42
+
34
43
  def reset_loaded_currencies
35
44
  @@loaded_currencies = {}
36
45
  end
@@ -52,6 +61,10 @@ class Money
52
61
  if data.nil? && Money::Config.current.experimental_crypto_currencies
53
62
  data = self.class.crypto_currencies[currency_iso]
54
63
  end
64
+ if data.nil?
65
+ custom_path = Money::Config.current.experimental_custom_currency_path
66
+ data = self.class.custom_currencies(custom_path)[currency_iso] if custom_path
67
+ end
55
68
 
56
69
  raise UnknownCurrency, "Invalid iso4217 currency '#{currency_iso}'" unless data
57
70
  @symbol = data['symbol']
data/lib/money/helpers.rb CHANGED
@@ -44,16 +44,7 @@ class Money
44
44
  when 'xxx', 'XXX'
45
45
  Money::NULL_CURRENCY
46
46
  when String
47
- begin
48
- Currency.find!(currency)
49
- rescue Money::Currency::UnknownCurrency => error
50
- if Money::Config.current.legacy_deprecations
51
- Money.deprecate(error.message)
52
- Money::NULL_CURRENCY
53
- else
54
- raise error
55
- end
56
- end
47
+ Currency.find!(currency)
57
48
  else
58
49
  raise ArgumentError, "could not parse as currency #{currency.inspect}"
59
50
  end
data/lib/money/money.rb CHANGED
@@ -41,10 +41,6 @@ class Money
41
41
  extend Forwardable
42
42
  def_delegators :'Money::Config.global', :default_currency, :default_currency=
43
43
 
44
- def without_legacy_deprecations(&block)
45
- with_config(legacy_deprecations: false, &block)
46
- end
47
-
48
44
  def with_config(**configs, &block)
49
45
  Money::Config.configure_current(**configs, &block)
50
46
  end
@@ -124,28 +120,24 @@ class Money
124
120
  msg = "Money.new(Money.new(amount, #{amount.currency}), #{currency}) " \
125
121
  "is changing the currency of an existing money object"
126
122
 
127
- if Money::Config.current.legacy_deprecations
128
- Money.deprecate("#{msg}. A Money::IncompatibleCurrencyError will raise in the next major release")
129
- Money.new(amount.value, currency)
130
- else
131
- raise Money::IncompatibleCurrencyError, msg
132
- end
123
+ raise Money::IncompatibleCurrencyError, msg
133
124
  end
134
125
  end
135
126
 
136
127
  def initialize(value, currency)
137
128
  raise ArgumentError if value.nan?
138
- if value.infinite?
139
- Money.deprecate("Initializing Money with infinity is deprecated and will raise an ArgumentError in v4")
140
- end
129
+ raise ArgumentError if value.infinite?
141
130
 
142
- @currency = Helpers.value_to_currency(currency)
131
+ @currency = currency
143
132
  @value = BigDecimal(value.round(@currency.minor_units))
144
133
  freeze
145
134
  end
146
135
 
147
136
  def init_with(coder)
148
- initialize(Helpers.value_to_decimal(coder['value']), coder['currency'])
137
+ initialize(
138
+ Helpers.value_to_decimal(coder['value']),
139
+ Helpers.value_to_currency(coder['currency']),
140
+ )
149
141
  end
150
142
 
151
143
  def encode_with(coder)
@@ -394,11 +386,7 @@ class Money
394
386
  def ensure_compatible_currency(other_currency, msg)
395
387
  return if currency.compatible?(other_currency)
396
388
 
397
- if Money::Config.current.legacy_deprecations
398
- Money.deprecate("#{msg}. A Money::IncompatibleCurrencyError will raise in the next major release")
399
- else
400
- raise Money::IncompatibleCurrencyError, msg
401
- end
389
+ raise Money::IncompatibleCurrencyError, msg
402
390
  end
403
391
 
404
392
  def calculated_currency(other)