shopify-money 3.1.2 → 3.2.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.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +4 -0
- data/.github/workflows/tests.yml +3 -3
- data/Gemfile.lock +1 -1
- data/lib/money/config.rb +56 -9
- data/lib/money/core_extensions.rb +1 -1
- data/lib/money/currency.rb +8 -1
- data/lib/money/helpers.rb +2 -2
- data/lib/money/money.rb +29 -32
- data/lib/money/version.rb +1 -1
- data/lib/money_column/active_record_hooks.rb +1 -1
- data/spec/config_spec.rb +48 -20
- data/spec/money_spec.rb +27 -1
- data/spec/spec_helper.rb +6 -6
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c927d4e18e7362a5ad3ddfb642785343d4f504f804b61e0bdac87f410a323c6e
|
4
|
+
data.tar.gz: 6eaa57e295c85d1d94e271a8cda1a7fa0f702198878e6087b25b17ea651bf38a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76a3752201eec5bceba4252233a754114de80c3e4e223af5fb2ee9f52b86c280436d55b52457b0d972451392b394aaca40c1240d47535e6c9b24d59f0f0e6dbb
|
7
|
+
data.tar.gz: e4ec1cd96dd48a5ca060cf773843f834c97a8be50eb0d0b55e390556966d439d5aab597a2af9ab139a7008a714fb7c9f20168dac081436b2fb5091e1be602a7d
|
data/.github/dependabot.yml
CHANGED
data/.github/workflows/tests.yml
CHANGED
@@ -13,12 +13,12 @@ jobs:
|
|
13
13
|
|
14
14
|
name: Ruby ${{ matrix.ruby }}
|
15
15
|
steps:
|
16
|
-
- uses: actions/checkout@v4
|
16
|
+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
17
17
|
- name: Set up Ruby ${{ matrix.ruby }}
|
18
|
-
uses: ruby/setup-ruby@v1
|
18
|
+
uses: ruby/setup-ruby@e34163cd15f4bb403dcd72d98e295997e6a55798 # v1.238.0
|
19
19
|
with:
|
20
20
|
ruby-version: ${{ matrix.ruby }}
|
21
|
-
- uses: actions/cache@v4
|
21
|
+
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
22
22
|
with:
|
23
23
|
path: vendor/bundle
|
24
24
|
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
|
data/Gemfile.lock
CHANGED
data/lib/money/config.rb
CHANGED
@@ -2,7 +2,62 @@
|
|
2
2
|
|
3
3
|
class Money
|
4
4
|
class Config
|
5
|
-
|
5
|
+
CONFIG_THREAD = :shopify_money__configs
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def global
|
9
|
+
@config ||= new
|
10
|
+
end
|
11
|
+
|
12
|
+
def current
|
13
|
+
thread_local_config[Fiber.current.object_id] ||= global.dup
|
14
|
+
end
|
15
|
+
|
16
|
+
def current=(config)
|
17
|
+
thread_local_config[Fiber.current.object_id] = config
|
18
|
+
end
|
19
|
+
|
20
|
+
def configure_current(**configs, &block)
|
21
|
+
old_config = current.dup
|
22
|
+
current.tap do |config|
|
23
|
+
configs.each do |k, v|
|
24
|
+
config.public_send("#{k}=", v)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
yield
|
28
|
+
ensure
|
29
|
+
self.current = old_config
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset_current
|
33
|
+
thread_local_config.delete(Fiber.current.object_id)
|
34
|
+
Thread.current[CONFIG_THREAD] = nil if thread_local_config.empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def thread_local_config
|
40
|
+
Thread.current[CONFIG_THREAD] ||= {}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_accessor :legacy_json_format, :legacy_deprecations, :experimental_crypto_currencies
|
45
|
+
|
46
|
+
attr_reader :default_currency
|
47
|
+
alias_method :currency, :default_currency
|
48
|
+
|
49
|
+
def default_currency=(value)
|
50
|
+
@default_currency =
|
51
|
+
case value
|
52
|
+
when String
|
53
|
+
Currency.find!(value)
|
54
|
+
when Money::Currency, Money::NullCurrency, nil
|
55
|
+
value
|
56
|
+
else
|
57
|
+
raise ArgumentError, "Invalid currency"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
alias_method :currency=, :default_currency=
|
6
61
|
|
7
62
|
def legacy_default_currency!
|
8
63
|
@default_currency ||= Money::NULL_CURRENCY
|
@@ -26,13 +81,5 @@ class Money
|
|
26
81
|
@legacy_deprecations = false
|
27
82
|
@experimental_crypto_currencies = false
|
28
83
|
end
|
29
|
-
|
30
|
-
def without_legacy_deprecations(&block)
|
31
|
-
old_legacy_deprecations = @legacy_deprecations
|
32
|
-
@legacy_deprecations = false
|
33
|
-
yield
|
34
|
-
ensure
|
35
|
-
@legacy_deprecations = old_legacy_deprecations
|
36
|
-
end
|
37
84
|
end
|
38
85
|
end
|
data/lib/money/currency.rb
CHANGED
@@ -30,6 +30,10 @@ class Money
|
|
30
30
|
def crypto_currencies
|
31
31
|
@@crypto_currencies ||= Loader.load_crypto_currencies
|
32
32
|
end
|
33
|
+
|
34
|
+
def reset_loaded_currencies
|
35
|
+
@@loaded_currencies = {}
|
36
|
+
end
|
33
37
|
end
|
34
38
|
|
35
39
|
attr_reader :iso_code,
|
@@ -45,7 +49,10 @@ class Money
|
|
45
49
|
|
46
50
|
def initialize(currency_iso)
|
47
51
|
data = self.class.currencies[currency_iso]
|
48
|
-
|
52
|
+
if data.nil? && Money::Config.current.experimental_crypto_currencies
|
53
|
+
data = self.class.crypto_currencies[currency_iso]
|
54
|
+
end
|
55
|
+
|
49
56
|
raise UnknownCurrency, "Invalid iso4217 currency '#{currency_iso}'" unless data
|
50
57
|
@symbol = data['symbol']
|
51
58
|
@disambiguate_symbol = data['disambiguate_symbol'] || data['symbol']
|
data/lib/money/helpers.rb
CHANGED
@@ -44,7 +44,7 @@ class Money
|
|
44
44
|
when Money::Currency, Money::NullCurrency
|
45
45
|
currency
|
46
46
|
when nil, ''
|
47
|
-
default = Money.
|
47
|
+
default = Money::Config.current.currency
|
48
48
|
raise(Money::Currency::UnknownCurrency, 'missing currency') if default.nil? || default == ''
|
49
49
|
value_to_currency(default)
|
50
50
|
when 'xxx', 'XXX'
|
@@ -53,7 +53,7 @@ class Money
|
|
53
53
|
begin
|
54
54
|
Currency.find!(currency)
|
55
55
|
rescue Money::Currency::UnknownCurrency => error
|
56
|
-
if Money.
|
56
|
+
if Money::Config.current.legacy_deprecations
|
57
57
|
Money.deprecate(error.message)
|
58
58
|
Money::NULL_CURRENCY
|
59
59
|
else
|
data/lib/money/money.rb
CHANGED
@@ -39,19 +39,37 @@ class Money
|
|
39
39
|
|
40
40
|
class << self
|
41
41
|
extend Forwardable
|
42
|
-
def_delegators :
|
42
|
+
def_delegators :'Money::Config.global', :default_currency, :default_currency=
|
43
|
+
|
44
|
+
def without_legacy_deprecations(&block)
|
45
|
+
with_config(legacy_deprecations: false, &block)
|
46
|
+
end
|
47
|
+
|
48
|
+
def with_config(**configs, &block)
|
49
|
+
Money::Config.configure_current(**configs, &block)
|
50
|
+
end
|
43
51
|
|
44
52
|
def config
|
45
|
-
|
53
|
+
Money::Config.global
|
46
54
|
end
|
47
55
|
|
48
|
-
def
|
49
|
-
|
56
|
+
def configure(&block)
|
57
|
+
Money::Config.global.tap(&block)
|
50
58
|
end
|
51
59
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
60
|
+
def current_currency
|
61
|
+
Money::Config.current.currency
|
62
|
+
end
|
63
|
+
|
64
|
+
def current_currency=(value)
|
65
|
+
Money::Config.current.currency = value
|
66
|
+
end
|
67
|
+
|
68
|
+
def with_currency(currency, &block)
|
69
|
+
if currency.nil?
|
70
|
+
currency = current_currency
|
71
|
+
end
|
72
|
+
with_config(currency: currency, &block)
|
55
73
|
end
|
56
74
|
|
57
75
|
def new(value = 0, currency = nil)
|
@@ -101,26 +119,6 @@ class Money
|
|
101
119
|
end
|
102
120
|
end
|
103
121
|
|
104
|
-
def current_currency
|
105
|
-
Thread.current[:money_currency]
|
106
|
-
end
|
107
|
-
|
108
|
-
def current_currency=(currency)
|
109
|
-
Thread.current[:money_currency] = currency
|
110
|
-
end
|
111
|
-
|
112
|
-
# Set Money.default_currency inside the supplied block, resets it to
|
113
|
-
# the previous value when done to prevent leaking state. Similar to
|
114
|
-
# I18n.with_locale and ActiveSupport's Time.use_zone. This won't affect
|
115
|
-
# instances being created with explicitly set currency.
|
116
|
-
def with_currency(new_currency)
|
117
|
-
old_currency = Money.current_currency
|
118
|
-
Money.current_currency = new_currency
|
119
|
-
yield
|
120
|
-
ensure
|
121
|
-
Money.current_currency = old_currency
|
122
|
-
end
|
123
|
-
|
124
122
|
private
|
125
123
|
|
126
124
|
def new_from_money(amount, currency)
|
@@ -137,7 +135,7 @@ class Money
|
|
137
135
|
msg = "Money.new(Money.new(amount, #{amount.currency}), #{currency}) " \
|
138
136
|
"is changing the currency of an existing money object"
|
139
137
|
|
140
|
-
if Money.
|
138
|
+
if Money::Config.current.legacy_deprecations
|
141
139
|
Money.deprecate("#{msg}. A Money::IncompatibleCurrencyError will raise in the next major release")
|
142
140
|
Money.new(amount.value, currency)
|
143
141
|
else
|
@@ -145,7 +143,6 @@ class Money
|
|
145
143
|
end
|
146
144
|
end
|
147
145
|
end
|
148
|
-
configure
|
149
146
|
|
150
147
|
def initialize(value, currency)
|
151
148
|
raise ArgumentError if value.nan?
|
@@ -286,7 +283,7 @@ class Money
|
|
286
283
|
alias_method :to_formatted_s, :to_fs
|
287
284
|
|
288
285
|
def to_json(options = nil)
|
289
|
-
if (options.is_a?(Hash) && options[:legacy_format]) || Money.
|
286
|
+
if (options.is_a?(Hash) && options[:legacy_format]) || Money::Config.current.legacy_json_format
|
290
287
|
to_s
|
291
288
|
else
|
292
289
|
as_json(options).to_json
|
@@ -294,7 +291,7 @@ class Money
|
|
294
291
|
end
|
295
292
|
|
296
293
|
def as_json(options = nil)
|
297
|
-
if (options.is_a?(Hash) && options[:legacy_format]) || Money.
|
294
|
+
if (options.is_a?(Hash) && options[:legacy_format]) || Money::Config.current.legacy_json_format
|
298
295
|
to_s
|
299
296
|
else
|
300
297
|
{ value: to_s(:amount), currency: currency.to_s }
|
@@ -407,7 +404,7 @@ class Money
|
|
407
404
|
def ensure_compatible_currency(other_currency, msg)
|
408
405
|
return if currency.compatible?(other_currency)
|
409
406
|
|
410
|
-
if Money.
|
407
|
+
if Money::Config.current.legacy_deprecations
|
411
408
|
Money.deprecate("#{msg}. A Money::IncompatibleCurrencyError will raise in the next major release")
|
412
409
|
else
|
413
410
|
raise Money::IncompatibleCurrencyError, msg
|
data/lib/money/version.rb
CHANGED
@@ -60,7 +60,7 @@ module MoneyColumn
|
|
60
60
|
currency = options[:currency] || try(options[:currency_column])
|
61
61
|
if currency && !money.currency.compatible?(Money::Helpers.value_to_currency(currency))
|
62
62
|
msg = "[money_column] currency mismatch between #{currency} and #{money.currency} in column #{column}."
|
63
|
-
if Money.
|
63
|
+
if Money::Config.current.legacy_deprecations
|
64
64
|
Money.deprecate(msg)
|
65
65
|
else
|
66
66
|
raise MoneyColumn::CurrencyReadOnlyError, msg
|
data/spec/config_spec.rb
CHANGED
@@ -3,27 +3,52 @@ require 'spec_helper'
|
|
3
3
|
|
4
4
|
RSpec.describe "Money::Config" do
|
5
5
|
describe 'thread safety' do
|
6
|
-
it 'does not share the same config across
|
6
|
+
it 'does not share the same config across fibers' do
|
7
7
|
configure(legacy_deprecations: false, default_currency: 'USD') do
|
8
|
-
expect(Money.
|
9
|
-
expect(Money.
|
10
|
-
|
11
|
-
|
12
|
-
Money.
|
13
|
-
|
14
|
-
|
8
|
+
expect(Money::Config.current.legacy_deprecations).to eq(false)
|
9
|
+
expect(Money::Config.current.default_currency.to_s).to eq('USD')
|
10
|
+
|
11
|
+
fiber = Fiber.new do
|
12
|
+
Money::Config.current.legacy_deprecations!
|
13
|
+
Money::Config.current.default_currency = "EUR"
|
14
|
+
|
15
|
+
expect(Money::Config.current.legacy_deprecations).to eq(true)
|
16
|
+
expect(Money::Config.current.default_currency.to_s).to eq("EUR")
|
17
|
+
|
18
|
+
:fiber_completed
|
15
19
|
end
|
16
|
-
|
17
|
-
expect(
|
18
|
-
|
20
|
+
# run the fiber
|
21
|
+
expect(fiber.resume).to eq(:fiber_completed)
|
22
|
+
|
23
|
+
# Verify main fiber's config was not affected
|
24
|
+
expect(Money::Config.current.legacy_deprecations).to eq(false)
|
25
|
+
expect(Money::Config.current.default_currency.to_s).to eq('USD')
|
19
26
|
end
|
20
27
|
end
|
28
|
+
|
29
|
+
it 'isolates configuration between threads' do
|
30
|
+
expect(Money::Config.current.legacy_deprecations).to eq(false)
|
31
|
+
expect(Money::Config.current.default_currency).to eq(Money::Currency.find!('CAD'))
|
32
|
+
|
33
|
+
thread = Thread.new do
|
34
|
+
Money::Config.current.legacy_deprecations!
|
35
|
+
Money::Config.current.default_currency = "EUR"
|
36
|
+
|
37
|
+
expect(Money::Config.current.legacy_deprecations).to eq(true)
|
38
|
+
expect(Money::Config.current.default_currency).to eq(Money::Currency.find!("EUR"))
|
39
|
+
end
|
40
|
+
|
41
|
+
thread.join
|
42
|
+
|
43
|
+
expect(Money::Config.current.legacy_deprecations).to eq(false)
|
44
|
+
expect(Money::Config.current.default_currency).to eq(Money::Currency.find!('CAD'))
|
45
|
+
end
|
21
46
|
end
|
22
47
|
|
23
48
|
describe 'legacy_deprecations' do
|
24
49
|
it "respects the default currency" do
|
25
50
|
configure(default_currency: 'USD', legacy_deprecations: true) do
|
26
|
-
expect(Money.default_currency).to eq("USD")
|
51
|
+
expect(Money::Config.current.default_currency.to_s).to eq("USD")
|
27
52
|
end
|
28
53
|
end
|
29
54
|
|
@@ -33,7 +58,7 @@ RSpec.describe "Money::Config" do
|
|
33
58
|
|
34
59
|
it 'legacy_deprecations returns true when opting in to v1' do
|
35
60
|
configure(legacy_deprecations: true) do
|
36
|
-
expect(Money.
|
61
|
+
expect(Money::Config.current.legacy_deprecations).to eq(true)
|
37
62
|
end
|
38
63
|
end
|
39
64
|
|
@@ -45,30 +70,33 @@ RSpec.describe "Money::Config" do
|
|
45
70
|
|
46
71
|
it 'legacy_deprecations defaults to NULL_CURRENCY' do
|
47
72
|
configure(legacy_default_currency: true) do
|
48
|
-
expect(Money.
|
73
|
+
expect(Money::Config.current.default_currency).to eq(Money::NULL_CURRENCY)
|
49
74
|
end
|
50
75
|
end
|
51
76
|
end
|
52
77
|
|
53
78
|
describe 'default_currency' do
|
54
79
|
it 'defaults to nil' do
|
55
|
-
|
56
|
-
expect(Money.config.default_currency).to eq(nil)
|
57
|
-
end
|
80
|
+
expect(Money::Config.new.default_currency).to eq(nil)
|
58
81
|
end
|
59
82
|
|
60
83
|
it 'can be set to a new currency' do
|
61
84
|
configure(default_currency: 'USD') do
|
62
|
-
expect(Money.
|
85
|
+
expect(Money::Config.current.default_currency.to_s).to eq('USD')
|
63
86
|
end
|
64
87
|
end
|
88
|
+
|
89
|
+
it 'raises ArgumentError for invalid currency' do
|
90
|
+
config = Money::Config.new
|
91
|
+
expect { config.default_currency = 123 }.to raise_error(ArgumentError, "Invalid currency")
|
92
|
+
end
|
65
93
|
end
|
66
|
-
|
94
|
+
|
67
95
|
describe 'experimental_crypto_currencies' do
|
68
96
|
it 'defaults to false' do
|
69
97
|
expect(Money::Config.new.experimental_crypto_currencies).to eq(false)
|
70
98
|
end
|
71
|
-
|
99
|
+
|
72
100
|
it 'can be set to true' do
|
73
101
|
config = Money::Config.new
|
74
102
|
config.experimental_crypto_currencies = true
|
data/spec/money_spec.rb
CHANGED
@@ -20,6 +20,15 @@ RSpec.describe "Money" do
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
it ".configure the config" do
|
24
|
+
config = Money::Config.new
|
25
|
+
allow(Money::Config).to receive(:global).and_return(config)
|
26
|
+
|
27
|
+
expect {
|
28
|
+
Money.configure { |c| c.default_currency = "USD" }
|
29
|
+
}.to change { config.default_currency }.from(nil).to(Money::Currency.find!("USD"))
|
30
|
+
end
|
31
|
+
|
23
32
|
it ".zero has no currency" do
|
24
33
|
expect(Money.new(0, Money::NULL_CURRENCY).currency).to be_a(Money::NullCurrency)
|
25
34
|
end
|
@@ -1095,7 +1104,7 @@ RSpec.describe "Money" do
|
|
1095
1104
|
end
|
1096
1105
|
end
|
1097
1106
|
|
1098
|
-
describe '
|
1107
|
+
describe '.with_currency' do
|
1099
1108
|
it "allows setting the implicit default currency for a block scope" do
|
1100
1109
|
money = nil
|
1101
1110
|
Money.with_currency('CAD') do
|
@@ -1114,6 +1123,15 @@ RSpec.describe "Money" do
|
|
1114
1123
|
expect(money.currency.iso_code).to eq('USD')
|
1115
1124
|
end
|
1116
1125
|
|
1126
|
+
it "accepts nil as currency" do
|
1127
|
+
money = nil
|
1128
|
+
Money.with_currency(nil) do
|
1129
|
+
money = Money.new(1.00)
|
1130
|
+
end
|
1131
|
+
# uses the default currency
|
1132
|
+
expect(money.currency.iso_code).to eq('CAD')
|
1133
|
+
end
|
1134
|
+
|
1117
1135
|
context "with .default_currency set" do
|
1118
1136
|
around(:each) { |test| configure(default_currency: Money::Currency.new('EUR')) { test.run }}
|
1119
1137
|
|
@@ -1159,4 +1177,12 @@ RSpec.describe "Money" do
|
|
1159
1177
|
expect(money.currency.iso_code).to eq('EUR')
|
1160
1178
|
end
|
1161
1179
|
end
|
1180
|
+
|
1181
|
+
describe ".current_currency" do
|
1182
|
+
it "gets and sets the current currency via Config.current" do
|
1183
|
+
Money.current_currency = "USD"
|
1184
|
+
expect(Money.default_currency.iso_code).to eq("CAD")
|
1185
|
+
expect(Money.current_currency.iso_code).to eq("USD")
|
1186
|
+
end
|
1187
|
+
end
|
1162
1188
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -54,6 +54,8 @@ RSpec.configure do |config|
|
|
54
54
|
DatabaseCleaner.cleaning do
|
55
55
|
example.run
|
56
56
|
end
|
57
|
+
ensure
|
58
|
+
Money::Config.reset_current
|
57
59
|
end
|
58
60
|
end
|
59
61
|
|
@@ -73,20 +75,18 @@ end
|
|
73
75
|
|
74
76
|
|
75
77
|
def configure(default_currency: nil, legacy_json_format: nil, legacy_deprecations: nil, legacy_default_currency: nil, experimental_crypto_currencies: nil)
|
76
|
-
|
77
|
-
Money::Currency.class_variable_set(:@@loaded_currencies, {})
|
78
|
-
old_config = Money.config
|
79
|
-
Money.config = Money::Config.new.tap do |config|
|
78
|
+
Money::Config.current = Money::Config.new.tap do |config|
|
80
79
|
config.default_currency = default_currency if default_currency
|
81
80
|
config.legacy_json_format! if legacy_json_format
|
82
81
|
config.legacy_deprecations! if legacy_deprecations
|
83
82
|
config.legacy_default_currency! if legacy_default_currency
|
84
83
|
config.experimental_crypto_currencies! if experimental_crypto_currencies
|
85
84
|
end
|
85
|
+
Money::Currency.reset_loaded_currencies if experimental_crypto_currencies == false
|
86
|
+
|
86
87
|
yield
|
87
88
|
ensure
|
88
|
-
Money::
|
89
|
-
Money.config = old_config
|
89
|
+
Money::Config.reset_current
|
90
90
|
end
|
91
91
|
|
92
92
|
def yaml_load(yaml)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shopify-money
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify Inc
|
@@ -203,7 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
203
203
|
- !ruby/object:Gem::Version
|
204
204
|
version: '0'
|
205
205
|
requirements: []
|
206
|
-
rubygems_version: 3.6.
|
206
|
+
rubygems_version: 3.6.9
|
207
207
|
specification_version: 4
|
208
208
|
summary: Shopify's money gem
|
209
209
|
test_files: []
|