shopify-money 3.1.2 → 3.2.1
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 +9 -8
- data/README.md +39 -0
- data/lib/money/config.rb +57 -9
- data/lib/money/converters/converter.rb +24 -0
- data/lib/money/converters/factory.rb +25 -0
- data/lib/money/converters/iso4217_converter.rb +12 -0
- data/lib/money/converters/legacy_dollars_converter.rb +12 -0
- data/lib/money/converters/stripe_converter.rb +37 -0
- data/lib/money/core_extensions.rb +1 -1
- data/lib/money/currency.rb +8 -1
- data/lib/money/helpers.rb +2 -8
- data/lib/money/money.rb +33 -55
- data/lib/money/version.rb +1 -1
- data/lib/money.rb +8 -3
- data/lib/money_column/active_record_hooks.rb +19 -4
- data/money.gemspec +2 -0
- data/spec/config_spec.rb +48 -20
- data/spec/converters_spec.rb +84 -0
- data/spec/money_column_spec.rb +15 -0
- data/spec/money_spec.rb +76 -1
- data/spec/spec_helper.rb +6 -6
- metadata +22 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d16632a569c75054c496e3d341e83ffb2ce08e25177b0c12a344c9b1c5fa23c2
|
4
|
+
data.tar.gz: 43d4b4e5f1c8b79c2d366dbad9077056cdd00744a86035ff58931e192b8fbff7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a15a1d6761fd8bed35cedf19ebec19b03aa7a8f10eb03c52004ce6fb166158feab2bba29a41f7fb005cf49339d39361f572c3636e716dd80dbd0a2a702db1476
|
7
|
+
data.tar.gz: e2b3760497b1d8c4947ae5adea3592a01e625e41d4d916a8155c0d18809358a040e63b897edd5942030ec8652e7e39487adb5b965023b991c8e100fa4f637c73
|
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
@@ -1,7 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
shopify-money (3.1
|
4
|
+
shopify-money (3.2.1)
|
5
|
+
bigdecimal (>= 3.0)
|
5
6
|
|
6
7
|
GEM
|
7
8
|
remote: https://rubygems.org/
|
@@ -107,8 +108,8 @@ GEM
|
|
107
108
|
pp (>= 0.6.0)
|
108
109
|
rdoc (>= 4.0.0)
|
109
110
|
reline (>= 0.4.2)
|
110
|
-
json (2.
|
111
|
-
language_server-protocol (3.17.0.
|
111
|
+
json (2.12.2)
|
112
|
+
language_server-protocol (3.17.0.5)
|
112
113
|
lint_roller (1.1.0)
|
113
114
|
logger (1.7.0)
|
114
115
|
loofah (2.24.0)
|
@@ -148,8 +149,8 @@ GEM
|
|
148
149
|
nokogiri (1.18.7-x86_64-linux-gnu)
|
149
150
|
racc (~> 1.4)
|
150
151
|
ostruct (0.6.1)
|
151
|
-
parallel (1.
|
152
|
-
parser (3.3.
|
152
|
+
parallel (1.27.0)
|
153
|
+
parser (3.3.8.0)
|
153
154
|
ast (~> 2.4.1)
|
154
155
|
racc
|
155
156
|
pp (0.6.2)
|
@@ -223,7 +224,7 @@ GEM
|
|
223
224
|
diff-lcs (>= 1.2.0, < 2.0)
|
224
225
|
rspec-support (~> 3.13.0)
|
225
226
|
rspec-support (3.13.1)
|
226
|
-
rubocop (1.75.
|
227
|
+
rubocop (1.75.7)
|
227
228
|
json (~> 2.3)
|
228
229
|
language_server-protocol (~> 3.17.0.2)
|
229
230
|
lint_roller (~> 1.1.0)
|
@@ -234,14 +235,14 @@ GEM
|
|
234
235
|
rubocop-ast (>= 1.44.0, < 2.0)
|
235
236
|
ruby-progressbar (~> 1.7)
|
236
237
|
unicode-display_width (>= 2.4.0, < 4.0)
|
237
|
-
rubocop-ast (1.44.
|
238
|
+
rubocop-ast (1.44.1)
|
238
239
|
parser (>= 3.3.7.2)
|
239
240
|
prism (~> 1.4)
|
240
241
|
rubocop-performance (1.25.0)
|
241
242
|
lint_roller (~> 1.1)
|
242
243
|
rubocop (>= 1.75.0, < 2.0)
|
243
244
|
rubocop-ast (>= 1.38.0, < 2.0)
|
244
|
-
rubocop-shopify (2.
|
245
|
+
rubocop-shopify (2.17.0)
|
245
246
|
rubocop (~> 1.62)
|
246
247
|
ruby-progressbar (1.13.0)
|
247
248
|
securerandom (0.4.1)
|
data/README.md
CHANGED
@@ -165,6 +165,45 @@ Money.configure do |config|
|
|
165
165
|
end
|
166
166
|
```
|
167
167
|
|
168
|
+
### Converters
|
169
|
+
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
|
+
|
171
|
+
#### Built-in Converters
|
172
|
+
- `:iso4217` (default) - Uses the standard ISO 4217 subunit definitions
|
173
|
+
- `:stripe` - Uses Stripe's special cases for certain currencies
|
174
|
+
- `:legacy_dollar` - Always uses 100 as the subunit_to_unit value
|
175
|
+
|
176
|
+
#### Using Converters
|
177
|
+
```ruby
|
178
|
+
# Convert to subunits using ISO4217 format (default)
|
179
|
+
Money.new(1.00, 'USD').subunits # => 100
|
180
|
+
Money.new(1.00, 'ISK').subunits # => 1
|
181
|
+
|
182
|
+
# Convert to subunits using Stripe format
|
183
|
+
Money.new(1.00, 'ISK').subunits(format: :stripe) # => 100
|
184
|
+
|
185
|
+
# Convert from subunits
|
186
|
+
Money.from_subunits(100, 'ISK', format: :stripe) # => Money.new(1.00, 'ISK')
|
187
|
+
```
|
188
|
+
|
189
|
+
#### Custom Converters
|
190
|
+
You can create your own converter by subclassing `Money::Converters::Converter`:
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
class MyCustomConverter < Money::Converters::Converter
|
194
|
+
def subunit_to_unit(currency)
|
195
|
+
# Your custom logic here
|
196
|
+
1000
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Register your converter
|
201
|
+
Money::Converters.register(:my_format, MyCustomConverter)
|
202
|
+
|
203
|
+
# Use your converter
|
204
|
+
Money.new(1.00, 'USD').subunits(format: :my_format) # => 1000
|
205
|
+
```
|
206
|
+
|
168
207
|
## Money column
|
169
208
|
|
170
209
|
Since money internally uses BigDecimal it's logical to use a `decimal` column
|
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, :default_subunit_format
|
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
|
@@ -25,14 +80,7 @@ class Money
|
|
25
80
|
@legacy_json_format = false
|
26
81
|
@legacy_deprecations = false
|
27
82
|
@experimental_crypto_currencies = false
|
28
|
-
|
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
|
83
|
+
@default_subunit_format = :iso4217
|
36
84
|
end
|
37
85
|
end
|
38
86
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Money
|
4
|
+
module Converters
|
5
|
+
class Converter
|
6
|
+
def to_subunits(money)
|
7
|
+
raise ArgumentError, "money cannot be nil" if money.nil?
|
8
|
+
(money.value * subunit_to_unit(money.currency)).to_i
|
9
|
+
end
|
10
|
+
|
11
|
+
def from_subunits(subunits, currency)
|
12
|
+
currency = Helpers.value_to_currency(currency)
|
13
|
+
value = Helpers.value_to_decimal(subunits) / subunit_to_unit(currency)
|
14
|
+
Money.new(value, currency)
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def subunit_to_unit(currency)
|
20
|
+
raise NotImplementedError, "subunit_to_unit method must be implemented in subclasses"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Money
|
4
|
+
module Converters
|
5
|
+
class << self
|
6
|
+
def subunit_converters
|
7
|
+
@subunit_converters ||= {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def register(key, klass)
|
11
|
+
subunit_converters[key.to_sym] = klass
|
12
|
+
end
|
13
|
+
|
14
|
+
def for(format)
|
15
|
+
format ||= Money::Config.current.default_subunit_format
|
16
|
+
|
17
|
+
if (klass = subunit_converters[format.to_sym])
|
18
|
+
klass.new
|
19
|
+
else
|
20
|
+
raise(ArgumentError, "unknown format: '#{format}'")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Money
|
4
|
+
module Converters
|
5
|
+
class Iso4217Converter < Converter
|
6
|
+
def subunit_to_unit(currency)
|
7
|
+
currency.subunit_to_unit
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
Money::Converters.register(:iso4217, Money::Converters::Iso4217Converter)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Money
|
4
|
+
module Converters
|
5
|
+
class LegacyDollarsConverter < Converter
|
6
|
+
def subunit_to_unit(currency)
|
7
|
+
100
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
Money::Converters.register(:legacy_dollar, Money::Converters::LegacyDollarsConverter)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Money
|
4
|
+
module Converters
|
5
|
+
class StripeConverter < Iso4217Converter
|
6
|
+
SUBUNIT_TO_UNIT = {
|
7
|
+
# https://docs.stripe.com/currencies#special-cases
|
8
|
+
'ISK' => 100,
|
9
|
+
'UGX' => 100,
|
10
|
+
'HUF' => 100,
|
11
|
+
'TWD' => 100,
|
12
|
+
# https://docs.stripe.com/currencies#zero-decimal
|
13
|
+
'BIF' => 1,
|
14
|
+
'CLP' => 1,
|
15
|
+
'DJF' => 1,
|
16
|
+
'GNF' => 1,
|
17
|
+
'JPY' => 1,
|
18
|
+
'KMF' => 1,
|
19
|
+
'KRW' => 1,
|
20
|
+
'MGA' => 1,
|
21
|
+
'PYG' => 1,
|
22
|
+
'RWF' => 1,
|
23
|
+
'VND' => 1,
|
24
|
+
'VUV' => 1,
|
25
|
+
'XAF' => 1,
|
26
|
+
'XOF' => 1,
|
27
|
+
'XPF' => 1,
|
28
|
+
'USDC' => 1_000_000,
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
def subunit_to_unit(currency)
|
32
|
+
SUBUNIT_TO_UNIT.fetch(currency.iso_code, super)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
Money::Converters.register(:stripe, Money::Converters::StripeConverter)
|
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
@@ -9,12 +9,6 @@ class Money
|
|
9
9
|
DECIMAL_ZERO = BigDecimal(0).freeze
|
10
10
|
MAX_DECIMAL = 21
|
11
11
|
|
12
|
-
STRIPE_SUBUNIT_OVERRIDE = {
|
13
|
-
'ISK' => 100,
|
14
|
-
'UGX' => 100,
|
15
|
-
'USDC' => 1_000_000,
|
16
|
-
}.freeze
|
17
|
-
|
18
12
|
def value_to_decimal(num)
|
19
13
|
value =
|
20
14
|
case num
|
@@ -44,7 +38,7 @@ class Money
|
|
44
38
|
when Money::Currency, Money::NullCurrency
|
45
39
|
currency
|
46
40
|
when nil, ''
|
47
|
-
default = Money.
|
41
|
+
default = Money::Config.current.currency
|
48
42
|
raise(Money::Currency::UnknownCurrency, 'missing currency') if default.nil? || default == ''
|
49
43
|
value_to_currency(default)
|
50
44
|
when 'xxx', 'XXX'
|
@@ -53,7 +47,7 @@ class Money
|
|
53
47
|
begin
|
54
48
|
Currency.find!(currency)
|
55
49
|
rescue Money::Currency::UnknownCurrency => error
|
56
|
-
if Money.
|
50
|
+
if Money::Config.current.legacy_deprecations
|
57
51
|
Money.deprecate(error.message)
|
58
52
|
Money::NULL_CURRENCY
|
59
53
|
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
|
54
|
+
end
|
55
|
+
|
56
|
+
def configure(&block)
|
57
|
+
Money::Config.global.tap(&block)
|
46
58
|
end
|
47
59
|
|
48
|
-
def
|
49
|
-
|
60
|
+
def current_currency
|
61
|
+
Money::Config.current.currency
|
62
|
+
end
|
63
|
+
|
64
|
+
def current_currency=(value)
|
65
|
+
Money::Config.current.currency = value
|
50
66
|
end
|
51
67
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
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)
|
@@ -69,19 +87,8 @@ class Money
|
|
69
87
|
end
|
70
88
|
alias_method :from_amount, :new
|
71
89
|
|
72
|
-
def from_subunits(subunits, currency_iso, format:
|
73
|
-
|
74
|
-
|
75
|
-
subunit_to_unit_value = if format == :iso4217
|
76
|
-
currency.subunit_to_unit
|
77
|
-
elsif format == :stripe
|
78
|
-
Helpers::STRIPE_SUBUNIT_OVERRIDE.fetch(currency.iso_code, currency.subunit_to_unit)
|
79
|
-
else
|
80
|
-
raise ArgumentError, "unknown format #{format}"
|
81
|
-
end
|
82
|
-
|
83
|
-
value = Helpers.value_to_decimal(subunits) / subunit_to_unit_value
|
84
|
-
new(value, currency)
|
90
|
+
def from_subunits(subunits, currency_iso, format: nil)
|
91
|
+
Converters.for(format).from_subunits(subunits, currency_iso)
|
85
92
|
end
|
86
93
|
|
87
94
|
def from_json(string)
|
@@ -101,26 +108,6 @@ class Money
|
|
101
108
|
end
|
102
109
|
end
|
103
110
|
|
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
111
|
private
|
125
112
|
|
126
113
|
def new_from_money(amount, currency)
|
@@ -137,7 +124,7 @@ class Money
|
|
137
124
|
msg = "Money.new(Money.new(amount, #{amount.currency}), #{currency}) " \
|
138
125
|
"is changing the currency of an existing money object"
|
139
126
|
|
140
|
-
if Money.
|
127
|
+
if Money::Config.current.legacy_deprecations
|
141
128
|
Money.deprecate("#{msg}. A Money::IncompatibleCurrencyError will raise in the next major release")
|
142
129
|
Money.new(amount.value, currency)
|
143
130
|
else
|
@@ -145,7 +132,6 @@ class Money
|
|
145
132
|
end
|
146
133
|
end
|
147
134
|
end
|
148
|
-
configure
|
149
135
|
|
150
136
|
def initialize(value, currency)
|
151
137
|
raise ArgumentError if value.nan?
|
@@ -163,16 +149,8 @@ class Money
|
|
163
149
|
coder['currency'] = @currency.iso_code
|
164
150
|
end
|
165
151
|
|
166
|
-
def subunits(format:
|
167
|
-
|
168
|
-
@currency.subunit_to_unit
|
169
|
-
elsif format == :stripe
|
170
|
-
Helpers::STRIPE_SUBUNIT_OVERRIDE.fetch(@currency.iso_code, @currency.subunit_to_unit)
|
171
|
-
else
|
172
|
-
raise ArgumentError, "unknown format #{format}"
|
173
|
-
end
|
174
|
-
|
175
|
-
(@value * subunit_to_unit_value).to_i
|
152
|
+
def subunits(format: nil)
|
153
|
+
Converters.for(format).to_subunits(self)
|
176
154
|
end
|
177
155
|
|
178
156
|
def no_currency?
|
@@ -286,7 +264,7 @@ class Money
|
|
286
264
|
alias_method :to_formatted_s, :to_fs
|
287
265
|
|
288
266
|
def to_json(options = nil)
|
289
|
-
if (options.is_a?(Hash) && options[:legacy_format]) || Money.
|
267
|
+
if (options.is_a?(Hash) && options[:legacy_format]) || Money::Config.current.legacy_json_format
|
290
268
|
to_s
|
291
269
|
else
|
292
270
|
as_json(options).to_json
|
@@ -294,7 +272,7 @@ class Money
|
|
294
272
|
end
|
295
273
|
|
296
274
|
def as_json(options = nil)
|
297
|
-
if (options.is_a?(Hash) && options[:legacy_format]) || Money.
|
275
|
+
if (options.is_a?(Hash) && options[:legacy_format]) || Money::Config.current.legacy_json_format
|
298
276
|
to_s
|
299
277
|
else
|
300
278
|
{ value: to_s(:amount), currency: currency.to_s }
|
@@ -407,7 +385,7 @@ class Money
|
|
407
385
|
def ensure_compatible_currency(other_currency, msg)
|
408
386
|
return if currency.compatible?(other_currency)
|
409
387
|
|
410
|
-
if Money.
|
388
|
+
if Money::Config.current.legacy_deprecations
|
411
389
|
Money.deprecate("#{msg}. A Money::IncompatibleCurrencyError will raise in the next major release")
|
412
390
|
else
|
413
391
|
raise Money::IncompatibleCurrencyError, msg
|
data/lib/money/version.rb
CHANGED
data/lib/money.rb
CHANGED
@@ -1,19 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'money/version'
|
4
|
-
require_relative 'money/parser/fuzzy'
|
5
|
-
require_relative 'money/helpers'
|
6
4
|
require_relative 'money/currency'
|
7
5
|
require_relative 'money/null_currency'
|
8
6
|
require_relative 'money/allocator'
|
9
7
|
require_relative 'money/splitter'
|
10
8
|
require_relative 'money/config'
|
11
9
|
require_relative 'money/money'
|
10
|
+
require_relative 'money/converters/factory'
|
11
|
+
require_relative 'money/converters/converter'
|
12
|
+
require_relative 'money/converters/iso4217_converter'
|
13
|
+
require_relative 'money/converters/stripe_converter'
|
14
|
+
require_relative 'money/converters/legacy_dollars_converter'
|
12
15
|
require_relative 'money/errors'
|
13
16
|
require_relative 'money/deprecations'
|
17
|
+
require_relative 'money/parser/fuzzy'
|
14
18
|
require_relative 'money/parser/accounting'
|
15
|
-
require_relative 'money/parser/locale_aware'
|
16
19
|
require_relative 'money/parser/simple'
|
20
|
+
require_relative 'money/parser/locale_aware'
|
21
|
+
require_relative 'money/helpers'
|
17
22
|
require_relative 'money/core_extensions'
|
18
23
|
require_relative 'money_column' if defined?(ActiveRecord)
|
19
24
|
require_relative 'money/railtie' if defined?(Rails::Railtie)
|
@@ -57,10 +57,9 @@ module MoneyColumn
|
|
57
57
|
end
|
58
58
|
|
59
59
|
if options[:currency_read_only]
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
if Money.config.legacy_deprecations
|
60
|
+
unless compatible_currency?(money, options)
|
61
|
+
msg = "Cannot update #{column}: Attempting to write a money with currency #{money.currency} to a record with currency #{currency}. If you do want to change the currency, either remove `currency_read_only` or update the record's currency manually"
|
62
|
+
if Money::Config.current.legacy_deprecations
|
64
63
|
Money.deprecate(msg)
|
65
64
|
else
|
66
65
|
raise MoneyColumn::CurrencyReadOnlyError, msg
|
@@ -73,6 +72,22 @@ module MoneyColumn
|
|
73
72
|
self[column] = money.value
|
74
73
|
end
|
75
74
|
|
75
|
+
def compatible_currency?(money, options)
|
76
|
+
currency_column = options[:currency_column]
|
77
|
+
currency = options[:currency] ||
|
78
|
+
@money_raw_new_attributes[currency_column.to_sym] ||
|
79
|
+
try(currency_column)
|
80
|
+
|
81
|
+
currency.nil? || money.currency.compatible?(Money::Helpers.value_to_currency(currency))
|
82
|
+
end
|
83
|
+
|
84
|
+
def _assign_attributes(new_attributes)
|
85
|
+
@money_raw_new_attributes = new_attributes.symbolize_keys
|
86
|
+
super
|
87
|
+
ensure
|
88
|
+
@money_raw_new_attributes = nil
|
89
|
+
end
|
90
|
+
|
76
91
|
module ClassMethods
|
77
92
|
attr_reader :money_column_options
|
78
93
|
|
data/money.gemspec
CHANGED
@@ -16,6 +16,8 @@ Gem::Specification.new do |s|
|
|
16
16
|
|
17
17
|
s.metadata['allowed_push_host'] = "https://rubygems.org"
|
18
18
|
|
19
|
+
s.add_dependency("bigdecimal", ">= 3.0")
|
20
|
+
|
19
21
|
s.add_development_dependency("bundler")
|
20
22
|
s.add_development_dependency("database_cleaner", "~> 2.0")
|
21
23
|
s.add_development_dependency("ostruct")
|
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
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
RSpec.describe Money::Converters do
|
5
|
+
let(:usd) { Money::Currency.find!('USD') }
|
6
|
+
let(:ugx) { Money::Currency.find!('UGX') }
|
7
|
+
|
8
|
+
describe '.for' do
|
9
|
+
it 'returns Iso4217Converter for :iso4217' do
|
10
|
+
expect(Money::Converters.for(:iso4217)).to be_a(Money::Converters::Iso4217Converter)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns StripeConverter for :stripe' do
|
14
|
+
expect(Money::Converters.for(:stripe)).to be_a(Money::Converters::StripeConverter)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'returns LegacyDollarsConverter for :legacy_dollar' do
|
18
|
+
expect(Money::Converters.for(:legacy_dollar)).to be_a(Money::Converters::LegacyDollarsConverter)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'raises ArgumentError for unknown format' do
|
22
|
+
expect { Money::Converters.for(:unknown) }.to raise_error(ArgumentError, /unknown format/)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'registering a custom converter' do
|
27
|
+
class DummyConverter < Money::Converters::Converter
|
28
|
+
def subunit_to_unit(currency); 42; end
|
29
|
+
end
|
30
|
+
|
31
|
+
class InvalidConverter < Money::Converters::Converter
|
32
|
+
# Intentionally not implementing subunit_to_unit
|
33
|
+
end
|
34
|
+
|
35
|
+
after { Money::Converters.subunit_converters.delete(:dummy) }
|
36
|
+
|
37
|
+
it 'registers and uses a custom converter' do
|
38
|
+
Money::Converters.register(:dummy, DummyConverter)
|
39
|
+
converter = Money::Converters.for(:dummy)
|
40
|
+
expect(converter).to be_a(DummyConverter)
|
41
|
+
expect(converter.to_subunits(Money.new(1, 'USD'))).to eq(42)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'raises NotImplementedError when subunit_to_unit is not implemented' do
|
45
|
+
Money::Converters.register(:invalid, InvalidConverter)
|
46
|
+
converter = Money::Converters.for(:invalid)
|
47
|
+
expect { converter.to_subunits(Money.new(1, 'USD')) }.to raise_error(NotImplementedError, "subunit_to_unit method must be implemented in subclasses")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe Money::Converters::Iso4217Converter do
|
52
|
+
let(:converter) { described_class.new }
|
53
|
+
it 'uses currency.subunit_to_unit' do
|
54
|
+
expect(converter.to_subunits(Money.new(1, usd))).to eq(100)
|
55
|
+
expect(converter.from_subunits(100, usd)).to eq(Money.new(1, usd))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe Money::Converters::StripeConverter do
|
60
|
+
let(:converter) { described_class.new }
|
61
|
+
|
62
|
+
it 'uses Stripe special cases' do
|
63
|
+
expect(converter.to_subunits(Money.new(1, ugx))).to eq(100)
|
64
|
+
expect(converter.from_subunits(100, ugx)).to eq(Money.new(1, ugx))
|
65
|
+
expect(converter.to_subunits(Money.new(1, usd))).to eq(100)
|
66
|
+
expect(converter.from_subunits(100, usd)).to eq(Money.new(1, usd))
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'handles USDC if present' do
|
70
|
+
configure(experimental_crypto_currencies: true) do
|
71
|
+
expect(converter.to_subunits(Money.new(1, "usdc"))).to eq(1_000_000)
|
72
|
+
expect(converter.from_subunits(1_000_000, "usdc")).to eq(Money.new(1, "usdc"))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe Money::Converters::LegacyDollarsConverter do
|
78
|
+
let(:converter) { described_class.new }
|
79
|
+
it 'always uses 100 as subunit_to_unit' do
|
80
|
+
expect(converter.to_subunits(Money.new(1, usd))).to eq(100)
|
81
|
+
expect(converter.from_subunits(100, usd)).to eq(Money.new(1, usd))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/spec/money_column_spec.rb
CHANGED
@@ -412,4 +412,19 @@ RSpec.describe 'MoneyColumn' do
|
|
412
412
|
expect(record.price.currency.to_s).to eq('GBP')
|
413
413
|
end
|
414
414
|
end
|
415
|
+
|
416
|
+
describe 'updating amount and currency simultaneously' do
|
417
|
+
let(:record) { MoneyWithReadOnlyCurrency.create!(currency: "CAD") }
|
418
|
+
|
419
|
+
it 'allows updating both amount and currency at the same time' do
|
420
|
+
record.update!(
|
421
|
+
price: Money.new(10, 'USD'),
|
422
|
+
currency: 'USD'
|
423
|
+
)
|
424
|
+
record.reload
|
425
|
+
expect(record.price.value).to eq(10)
|
426
|
+
expect(record.price.currency.to_s).to eq('USD')
|
427
|
+
expect(record.currency).to eq('USD')
|
428
|
+
end
|
429
|
+
end
|
415
430
|
end
|
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
|
@@ -158,6 +167,13 @@ RSpec.describe "Money" do
|
|
158
167
|
expect(Money.new("999999999999999999.99", "USD").to_s).to eq("999999999999999999.99")
|
159
168
|
end
|
160
169
|
|
170
|
+
it "to_fs formats with correct decimal places" do
|
171
|
+
expect(amount_money.to_fs).to eq("1.23")
|
172
|
+
expect(non_fractional_money.to_fs).to eq("1")
|
173
|
+
expect(Money.new(1.2345, 'USD').to_fs).to eq("1.23")
|
174
|
+
expect(Money.new(1.2345, 'BHD').to_fs).to eq("1.235")
|
175
|
+
end
|
176
|
+
|
161
177
|
it "to_fs raises ArgumentError on unsupported style" do
|
162
178
|
expect{ money.to_fs(:some_weird_style) }.to raise_error(ArgumentError)
|
163
179
|
end
|
@@ -749,6 +765,12 @@ RSpec.describe "Money" do
|
|
749
765
|
expect(Money.new(1, 'UGX').subunits(format: :stripe)).to eq(100)
|
750
766
|
end
|
751
767
|
|
768
|
+
it 'overrides the subunit_to_unit amount for USDC' do
|
769
|
+
configure(experimental_crypto_currencies: true) do
|
770
|
+
expect(Money.from_subunits(500000, "USDC", format: :stripe)).to eq(Money.new(0.50, 'USDC'))
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
752
774
|
it 'fallbacks to the default subunit_to_unit amount if no override is specified' do
|
753
775
|
expect(Money.new(1, 'USD').subunits(format: :stripe)).to eq(100)
|
754
776
|
end
|
@@ -1095,7 +1117,7 @@ RSpec.describe "Money" do
|
|
1095
1117
|
end
|
1096
1118
|
end
|
1097
1119
|
|
1098
|
-
describe '
|
1120
|
+
describe '.with_currency' do
|
1099
1121
|
it "allows setting the implicit default currency for a block scope" do
|
1100
1122
|
money = nil
|
1101
1123
|
Money.with_currency('CAD') do
|
@@ -1114,6 +1136,15 @@ RSpec.describe "Money" do
|
|
1114
1136
|
expect(money.currency.iso_code).to eq('USD')
|
1115
1137
|
end
|
1116
1138
|
|
1139
|
+
it "accepts nil as currency" do
|
1140
|
+
money = nil
|
1141
|
+
Money.with_currency(nil) do
|
1142
|
+
money = Money.new(1.00)
|
1143
|
+
end
|
1144
|
+
# uses the default currency
|
1145
|
+
expect(money.currency.iso_code).to eq('CAD')
|
1146
|
+
end
|
1147
|
+
|
1117
1148
|
context "with .default_currency set" do
|
1118
1149
|
around(:each) { |test| configure(default_currency: Money::Currency.new('EUR')) { test.run }}
|
1119
1150
|
|
@@ -1159,4 +1190,48 @@ RSpec.describe "Money" do
|
|
1159
1190
|
expect(money.currency.iso_code).to eq('EUR')
|
1160
1191
|
end
|
1161
1192
|
end
|
1193
|
+
|
1194
|
+
describe ".current_currency" do
|
1195
|
+
it "gets and sets the current currency via Config.current" do
|
1196
|
+
Money.current_currency = "USD"
|
1197
|
+
expect(Money.default_currency.iso_code).to eq("CAD")
|
1198
|
+
expect(Money.current_currency.iso_code).to eq("USD")
|
1199
|
+
end
|
1200
|
+
end
|
1201
|
+
|
1202
|
+
describe 'from_subunits' do
|
1203
|
+
it 'creates money from subunits using ISO4217 format' do
|
1204
|
+
expect(Money.from_subunits(100, 'USD')).to eq(Money.new(1.00, 'USD'))
|
1205
|
+
expect(Money.from_subunits(10, 'JPY')).to eq(Money.new(10, 'JPY'))
|
1206
|
+
end
|
1207
|
+
|
1208
|
+
it 'creates money from subunits using custom format' do
|
1209
|
+
expect(Money.from_subunits(100, 'USD', format: :iso4217)).to eq(Money.new(1.00, 'USD'))
|
1210
|
+
expect(Money.from_subunits(100, 'USD', format: :stripe)).to eq(Money.new(1.00, 'USD'))
|
1211
|
+
end
|
1212
|
+
|
1213
|
+
it 'raises error for unknown format' do
|
1214
|
+
expect {
|
1215
|
+
Money.from_subunits(100, 'USD', format: :unknown)
|
1216
|
+
}.to raise_error(ArgumentError, /unknown format/)
|
1217
|
+
end
|
1218
|
+
end
|
1219
|
+
|
1220
|
+
describe 'subunits' do
|
1221
|
+
it 'converts money to subunits using ISO4217 format' do
|
1222
|
+
expect(Money.new(1.00, 'USD').subunits).to eq(100)
|
1223
|
+
expect(Money.new(10, 'JPY').subunits).to eq(10)
|
1224
|
+
end
|
1225
|
+
|
1226
|
+
it 'converts money to subunits using custom format' do
|
1227
|
+
expect(Money.new(1.00, 'USD').subunits(format: :iso4217)).to eq(100)
|
1228
|
+
expect(Money.new(1.00, 'USD').subunits(format: :stripe)).to eq(100)
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
it 'raises error for unknown format' do
|
1232
|
+
expect {
|
1233
|
+
Money.new(1.00, 'USD').subunits(format: :unknown)
|
1234
|
+
}.to raise_error(ArgumentError, /unknown format/)
|
1235
|
+
end
|
1236
|
+
end
|
1162
1237
|
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.1
|
4
|
+
version: 3.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify Inc
|
@@ -9,6 +9,20 @@ bindir: bin
|
|
9
9
|
cert_chain: []
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: bigdecimal
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '3.0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '3.0'
|
12
26
|
- !ruby/object:Gem::Dependency
|
13
27
|
name: bundler
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -137,6 +151,11 @@ files:
|
|
137
151
|
- lib/money.rb
|
138
152
|
- lib/money/allocator.rb
|
139
153
|
- lib/money/config.rb
|
154
|
+
- lib/money/converters/converter.rb
|
155
|
+
- lib/money/converters/factory.rb
|
156
|
+
- lib/money/converters/iso4217_converter.rb
|
157
|
+
- lib/money/converters/legacy_dollars_converter.rb
|
158
|
+
- lib/money/converters/stripe_converter.rb
|
140
159
|
- lib/money/core_extensions.rb
|
141
160
|
- lib/money/currency.rb
|
142
161
|
- lib/money/currency/loader.rb
|
@@ -164,6 +183,7 @@ files:
|
|
164
183
|
- money.gemspec
|
165
184
|
- spec/allocator_spec.rb
|
166
185
|
- spec/config_spec.rb
|
186
|
+
- spec/converters_spec.rb
|
167
187
|
- spec/core_extensions_spec.rb
|
168
188
|
- spec/currency/loader_spec.rb
|
169
189
|
- spec/currency_spec.rb
|
@@ -203,7 +223,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
203
223
|
- !ruby/object:Gem::Version
|
204
224
|
version: '0'
|
205
225
|
requirements: []
|
206
|
-
rubygems_version: 3.6.
|
226
|
+
rubygems_version: 3.6.9
|
207
227
|
specification_version: 4
|
208
228
|
summary: Shopify's money gem
|
209
229
|
test_files: []
|