shopify-money 4.0.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3376d45362e482215d25c4c1e256842d5a5011708ab3775125255e48637f5962
4
- data.tar.gz: 2909f3605931b62b9efcb1799d928aea723fcde9a5f3182f3705a14887c1c2d1
3
+ metadata.gz: cadc7891cca0acc6f6128c570b259a5c1bead3a6a87175ebe3def93d9a618afb
4
+ data.tar.gz: 3fcc02abc7af2a86aba449ed135361a1024a18975430c8075f4b36d36d70c72a
5
5
  SHA512:
6
- metadata.gz: 7bf580f71b0341993e260c1ee10036f46d585ffcb286fa567d0bc72b18e38a531600622f828a7d3895d0df48c0edc7aa3b9b539477f2ac5aacf7cd902ca8ac76
7
- data.tar.gz: e5b249cb19e32a2ceb897cb047382a8fcb27b7408e4675f59a9b951dc8e916777e54038e43b7157d22484a6b443a9ad19273fb2030305f714ab184291742fc56
6
+ metadata.gz: 982ccb02e8ad20c3ad3ba9d217e0a53531d370cf32e7664947d859997ee2bc493614908f722fd1dc1ee0f234e114cb4c7e3552c85401d978afac4d462e35344c
7
+ data.tar.gz: 4a5e188fb292ad0ca2afdc2ba3dbcde89507a352d34ac7b7964870fc1584c0f9fd10d1036bd5b78a1971649d107b89f549f3b38a4f9bad823e829ac0c60d29ea
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shopify-money (4.0.0)
4
+ shopify-money (4.1.0)
5
5
  bigdecimal (>= 3.0)
6
6
 
7
7
  GEM
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
 
@@ -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, :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
@@ -72,6 +72,8 @@ class Money
72
72
  @legacy_json_format = false
73
73
  @experimental_crypto_currencies = false
74
74
  @default_subunit_format = :iso4217
75
+ @experimental_custom_currency_path = nil
76
+ @default_allocation_strategy = :roundrobin
75
77
  end
76
78
  end
77
79
  end
@@ -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/money.rb CHANGED
@@ -128,13 +128,16 @@ class Money
128
128
  raise ArgumentError if value.nan?
129
129
  raise ArgumentError if value.infinite?
130
130
 
131
- @currency = Helpers.value_to_currency(currency)
131
+ @currency = currency
132
132
  @value = BigDecimal(value.round(@currency.minor_units))
133
133
  freeze
134
134
  end
135
135
 
136
136
  def init_with(coder)
137
- 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
+ )
138
141
  end
139
142
 
140
143
  def encode_with(coder)
data/lib/money/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Money
4
- VERSION = "4.0.0"
4
+ VERSION = "4.1.0"
5
5
  end
data/sig/money/config.rbs CHANGED
@@ -7,6 +7,8 @@ class Money
7
7
  attr_accessor legacy_json_format: bool
8
8
  attr_accessor experimental_crypto_currencies: bool
9
9
  attr_accessor default_subunit_format: Symbol
10
+ attr_accessor experimental_custom_currency_path: String?
11
+ attr_accessor default_allocation_strategy: Symbol
10
12
 
11
13
  attr_reader default_currency: (Currency | NullCurrency | nil)
12
14
  def default_currency=: (String | Currency | NullCurrency | nil value) -> (Currency | NullCurrency | nil)
@@ -16,7 +18,7 @@ class Money
16
18
  def self.global: () -> Config
17
19
  def self.current: () -> Config
18
20
  def self.current=: (Config config) -> Config
19
- def self.configure_current: [T] (?currency: (String | Currency | NullCurrency | nil), ?legacy_json_format: bool, ?experimental_crypto_currencies: bool, ?default_subunit_format: Symbol) { () -> T } -> T
21
+ def self.configure_current: [T] (?currency: (String | Currency | NullCurrency | nil), ?legacy_json_format: bool, ?experimental_crypto_currencies: bool, ?default_subunit_format: Symbol, ?experimental_custom_currency_path: String?, ?default_allocation_strategy: Symbol) { () -> T } -> T
20
22
  def self.reset_current: () -> nil
21
23
 
22
24
  private
@@ -7,6 +7,7 @@ class Money
7
7
 
8
8
  def self.load_currencies: () -> Hash[String, untyped]
9
9
  def self.load_crypto_currencies: () -> Hash[String, untyped]
10
+ def self.load_custom_currencies: (String path) -> Hash[String, untyped]
10
11
 
11
12
  private
12
13
 
@@ -31,6 +32,8 @@ class Money
31
32
  def self.find: (String | Symbol currency_iso) -> Currency?
32
33
  def self.currencies: () -> Hash[String, Hash[String, untyped]]
33
34
  def self.crypto_currencies: () -> Hash[String, Hash[String, untyped]]
35
+ def self.custom_currencies: (String path) -> Hash[String, Hash[String, untyped]]
36
+ def self.reset_custom_currencies: () -> nil
34
37
  def self.reset_loaded_currencies: () -> Hash[String, Currency]
35
38
 
36
39
  def initialize: (String currency_iso) -> void
@@ -42,7 +42,7 @@ RSpec.describe "Allocator" do
42
42
 
43
43
  specify "#allocate will use rationals if provided" do
44
44
  splits = [128400,20439,14589,14589,25936].map{ |num| Rational(num, 203953) } # sums to > 1 if converted to float
45
- expect(new_allocator(2.25).allocate(splits)).to eq([Money.new(1.42), Money.new(0.23), Money.new(0.16), Money.new(0.16), Money.new(0.28)])
45
+ expect(new_allocator(2.25).allocate(splits, :roundrobin)).to eq([Money.new(1.42), Money.new(0.23), Money.new(0.16), Money.new(0.16), Money.new(0.28)])
46
46
  end
47
47
 
48
48
  specify "#allocate will convert rationals with high precision" do
@@ -61,6 +61,29 @@ RSpec.describe "Allocator" do
61
61
  expect { new_allocator(0.03).allocate([0.5, 0.5], :bad_strategy_name) }.to raise_error(ArgumentError, "Invalid strategy. Valid options: :roundrobin, :roundrobin_reverse, :nearest")
62
62
  end
63
63
 
64
+ specify "#allocate defaults to :nearest strategy" do
65
+ # With :nearest, the party closest to the next whole subunit gets the leftover.
66
+ monies = new_allocator(1).allocate([0.02, 0.98])
67
+ expect(monies[0]).to eq(Money.new(0.02))
68
+ expect(monies[1]).to eq(Money.new(0.98))
69
+ end
70
+
71
+ specify "#allocate uses the configured default_allocation_strategy" do
72
+ Money.with_config(default_allocation_strategy: :roundrobin) do
73
+ # With :roundrobin, leftover subunits accumulate from the first allocation.
74
+ monies = new_allocator(0.05).allocate([0.33, 0.34, 0.33])
75
+ expect(monies[0]).to eq(Money.new(0.02))
76
+ expect(monies[1]).to eq(Money.new(0.02))
77
+ expect(monies[2]).to eq(Money.new(0.01))
78
+ end
79
+ end
80
+
81
+ specify "#allocate exact splits produce no leftover regardless of strategy" do
82
+ monies = new_allocator(1).allocate([0.02, 0.98], :roundrobin)
83
+ expect(monies[0]).to eq(Money.new(0.02))
84
+ expect(monies[1]).to eq(Money.new(0.98))
85
+ end
86
+
64
87
  specify "#allocate raises an error when splits exceed 100%" do
65
88
  expect { new_allocator(0.03).allocate([0.5, 0.6]) }.to raise_error(ArgumentError, "allocations add to more than 100%")
66
89
  end
data/spec/config_spec.rb CHANGED
@@ -72,6 +72,30 @@ RSpec.describe "Money::Config" do
72
72
  end
73
73
  end
74
74
 
75
+ describe 'experimental_custom_currency_path' do
76
+ it 'defaults to nil' do
77
+ expect(Money::Config.new.experimental_custom_currency_path).to eq(nil)
78
+ end
79
+
80
+ it 'can be set to a path' do
81
+ config = Money::Config.new
82
+ config.experimental_custom_currency_path = '/tmp/custom.yml'
83
+ expect(config.experimental_custom_currency_path).to eq('/tmp/custom.yml')
84
+ end
85
+ end
86
+
87
+ describe 'default_allocation_strategy' do
88
+ it 'defaults to :roundrobin' do
89
+ expect(Money::Config.new.default_allocation_strategy).to eq(:roundrobin)
90
+ end
91
+
92
+ it 'can be set to a different strategy' do
93
+ config = Money::Config.new
94
+ config.default_allocation_strategy = :roundrobin
95
+ expect(config.default_allocation_strategy).to eq(:roundrobin)
96
+ end
97
+ end
98
+
75
99
  describe 'legacy_json_format' do
76
100
  it 'defaults to false' do
77
101
  expect(Money::Config.new.legacy_json_format).to eq(false)
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require 'spec_helper'
3
+ require 'tempfile'
3
4
 
4
5
  RSpec.describe Money::Currency::Loader do
5
6
 
@@ -37,4 +38,55 @@ RSpec.describe Money::Currency::Loader do
37
38
  expect(currencies['usdc']['iso_code']).to be_frozen
38
39
  end
39
40
  end
41
+
42
+ describe 'load_custom_currencies' do
43
+ it 'loads custom currencies from the given path' do
44
+ file = custom_currency_file(
45
+ "credits" => {
46
+ "iso_code" => "CREDITS",
47
+ "name" => "Loyalty Points",
48
+ "symbol" => "CR",
49
+ "disambiguate_symbol" => "CR",
50
+ "subunit_to_unit" => 1,
51
+ "smallest_denomination" => 1,
52
+ "decimal_mark" => "."
53
+ }
54
+ )
55
+
56
+ currencies = subject.load_custom_currencies(file.path)
57
+ expect(currencies['credits']['iso_code']).to eq('CREDITS')
58
+ expect(currencies['credits']['name']).to eq('Loyalty Points')
59
+ expect(currencies['credits']['symbol']).to eq('CR')
60
+ expect(currencies['credits']['subunit_to_unit']).to eq(1)
61
+ ensure
62
+ file.unlink
63
+ end
64
+
65
+ it 'raises ArgumentError when file does not exist' do
66
+ expect {
67
+ subject.load_custom_currencies('/nonexistent/path.yml')
68
+ }.to raise_error(ArgumentError, /not found/)
69
+ end
70
+
71
+ it 'returns frozen and deduplicated data' do
72
+ file = custom_currency_file(
73
+ "credits" => {
74
+ "iso_code" => "CREDITS",
75
+ "name" => "Loyalty Points",
76
+ "symbol" => "CR",
77
+ "disambiguate_symbol" => "CR",
78
+ "subunit_to_unit" => 1,
79
+ "smallest_denomination" => 1,
80
+ "decimal_mark" => "."
81
+ }
82
+ )
83
+
84
+ currencies = subject.load_custom_currencies(file.path)
85
+ expect(currencies).to be_frozen
86
+ expect(currencies['credits']).to be_frozen
87
+ expect(currencies['credits']['iso_code']).to be_frozen
88
+ ensure
89
+ file.unlink
90
+ end
91
+ end
40
92
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require 'spec_helper'
3
+ require 'tempfile'
3
4
 
4
5
  RSpec.describe "Currency" do
5
6
  CURRENCY_DATA = {
@@ -16,6 +17,20 @@ RSpec.describe "Currency" do
16
17
 
17
18
  let(:currency) { Money::Currency.new('usd') }
18
19
 
20
+ let(:mock_custom_currency) do
21
+ {
22
+ "credits" => {
23
+ "iso_code" => "CREDITS",
24
+ "name" => "Loyalty Points",
25
+ "symbol" => "CR",
26
+ "disambiguate_symbol" => "CR",
27
+ "subunit_to_unit" => 1,
28
+ "smallest_denomination" => 1,
29
+ "decimal_mark" => "."
30
+ }
31
+ }
32
+ end
33
+
19
34
  let(:mock_crypto_currency) do
20
35
  {
21
36
  "usdc" => {
@@ -68,6 +83,80 @@ RSpec.describe "Currency" do
68
83
  expect(Money::Currency.find("USDC")).to be_nil
69
84
  end
70
85
  end
86
+
87
+ it "looks up custom currencies when path is set" do
88
+ file = custom_currency_file(mock_custom_currency)
89
+
90
+ configure(experimental_custom_currency_path: file.path) do
91
+ currency = Money::Currency.new('CREDITS')
92
+ expect(currency.iso_code).to eq('CREDITS')
93
+ expect(currency.symbol).to eq('CR')
94
+ end
95
+ ensure
96
+ file.unlink
97
+ end
98
+
99
+ it "doesn't look up custom currencies when path is nil" do
100
+ expect(Money::Currency.find("CREDITS")).to eq(nil)
101
+ end
102
+
103
+ it "can't override ISO currencies with custom currencies" do
104
+ file = custom_currency_file(
105
+ "usd" => {
106
+ "iso_code" => "USD",
107
+ "name" => "Fake Dollar",
108
+ "symbol" => "FAKE",
109
+ "disambiguate_symbol" => "FAKE",
110
+ "subunit_to_unit" => 1,
111
+ "smallest_denomination" => 1,
112
+ "decimal_mark" => "."
113
+ }
114
+ )
115
+
116
+ configure(experimental_custom_currency_path: file.path) do
117
+ currency = Money::Currency.new('USD')
118
+ expect(currency.name).to eq('United States Dollar')
119
+ expect(currency.symbol).to eq('$')
120
+ end
121
+ ensure
122
+ file.unlink
123
+ end
124
+
125
+ it "can't override crypto currencies with custom currencies" do
126
+ file = custom_currency_file(
127
+ "usdc" => {
128
+ "iso_code" => "USDC",
129
+ "name" => "Fake USDC",
130
+ "symbol" => "FAKE",
131
+ "disambiguate_symbol" => "FAKE",
132
+ "subunit_to_unit" => 1,
133
+ "smallest_denomination" => 1,
134
+ "decimal_mark" => "."
135
+ }
136
+ )
137
+
138
+ configure(experimental_crypto_currencies: true, experimental_custom_currency_path: file.path) do
139
+ currency = Money::Currency.new('USDC')
140
+ expect(currency.name).to eq('USD Coin')
141
+ expect(currency.symbol).to eq('USDC')
142
+ end
143
+ ensure
144
+ file.unlink
145
+ end
146
+
147
+ it "loads custom currencies end-to-end from a YAML file" do
148
+ file = custom_currency_file(mock_custom_currency)
149
+
150
+ configure(experimental_custom_currency_path: file.path) do
151
+ money = Money.new(500, "CREDITS")
152
+ expect(money.currency.iso_code).to eq("CREDITS")
153
+ expect(money.currency.symbol).to eq("CR")
154
+ expect(money.currency.name).to eq("Loyalty Points")
155
+ expect(money.value).to eq(500)
156
+ end
157
+ ensure
158
+ file.unlink
159
+ end
71
160
  end
72
161
 
73
162
  describe ".find" do
@@ -95,6 +184,21 @@ RSpec.describe "Currency" do
95
184
  expect(Money::Currency.find('USDC')).to eq(nil)
96
185
  end
97
186
  end
187
+
188
+ it "returns custom currency when path is set" do
189
+ file = custom_currency_file(mock_custom_currency)
190
+
191
+ configure(experimental_custom_currency_path: file.path) do
192
+ expect(Money::Currency.find('CREDITS')).not_to eq(nil)
193
+ expect(Money::Currency.find('CREDITS').iso_code).to eq('CREDITS')
194
+ end
195
+ ensure
196
+ file.unlink
197
+ end
198
+
199
+ it "returns nil for custom currency when path is not set" do
200
+ expect(Money::Currency.find('CREDITS')).to eq(nil)
201
+ end
98
202
  end
99
203
 
100
204
  describe ".find!" do
@@ -122,6 +226,20 @@ RSpec.describe "Currency" do
122
226
  end
123
227
  end
124
228
 
229
+ describe ".custom_currencies" do
230
+ after { Money::Currency.reset_custom_currencies }
231
+
232
+ it "loads custom currencies from the loader" do
233
+ allow(Money::Currency::Loader).to receive(:load_custom_currencies)
234
+ .with('/tmp/custom.yml')
235
+ .and_return(mock_custom_currency)
236
+
237
+ expect(Money::Currency.custom_currencies('/tmp/custom.yml')).to eq(mock_custom_currency)
238
+ expect(Money::Currency.custom_currencies('/tmp/custom.yml')).to eq(mock_custom_currency) # Second call to verify caching
239
+ expect(Money::Currency::Loader).to have_received(:load_custom_currencies).with('/tmp/custom.yml').once
240
+ end
241
+ end
242
+
125
243
  CURRENCY_DATA.each do |attribute, value|
126
244
  describe "##{attribute}" do
127
245
  it 'returns the correct value' do
data/spec/spec_helper.rb CHANGED
@@ -75,17 +75,27 @@ RSpec::Matchers.define :quack_like do
75
75
  end
76
76
 
77
77
 
78
- def configure(default_currency: nil, legacy_json_format: nil, experimental_crypto_currencies: nil)
78
+ def configure(default_currency: nil, legacy_json_format: nil, experimental_crypto_currencies: nil, experimental_custom_currency_path: nil, default_allocation_strategy: nil)
79
79
  Money::Config.current = Money::Config.new.tap do |config|
80
80
  config.default_currency = default_currency if default_currency
81
81
  config.legacy_json_format! if legacy_json_format
82
- config.experimental_crypto_currencies! if experimental_crypto_currencies
82
+ config.experimental_crypto_currencies = experimental_crypto_currencies unless experimental_crypto_currencies.nil?
83
+ config.experimental_custom_currency_path = experimental_custom_currency_path if experimental_custom_currency_path
84
+ config.default_allocation_strategy = default_allocation_strategy if default_allocation_strategy
83
85
  end
84
- Money::Currency.reset_loaded_currencies if experimental_crypto_currencies == false
85
86
 
86
87
  yield
87
88
  ensure
88
89
  Money::Config.reset_current
90
+ Money::Currency.reset_loaded_currencies
91
+ Money::Currency.reset_custom_currencies if experimental_custom_currency_path
92
+ end
93
+
94
+ def custom_currency_file(currencies_hash)
95
+ file = Tempfile.new(['custom_currencies', '.yml'])
96
+ file.write(currencies_hash.to_yaml)
97
+ file.close
98
+ file
89
99
  end
90
100
 
91
101
  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: 4.0.0
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify Inc
@@ -252,7 +252,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
252
252
  - !ruby/object:Gem::Version
253
253
  version: '0'
254
254
  requirements: []
255
- rubygems_version: 4.0.6
255
+ rubygems_version: 4.0.10
256
256
  specification_version: 4
257
257
  summary: Shopify's money gem
258
258
  test_files: []