shopify-money 2.2.0 → 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +17 -8
- data/lib/money/money.rb +33 -22
- data/lib/money/version.rb +1 -1
- data/lib/rubocop/cop/money.rb +0 -1
- data/spec/money_spec.rb +58 -1
- metadata +2 -5
- data/lib/rubocop/cop/money/unsafe_to_money.rb +0 -35
- data/spec/rubocop/cop/money/unsafe_to_money_spec.rb +0 -63
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 75140fb176288187177a79f673b9741b69a4ae96e02b314139318d7ff817b8b9
|
4
|
+
data.tar.gz: d98030d40029dbb686b83b090ab11e43628a194bd2902ba130f5161e860dc839
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e140de24fea5b7c90b5044f4b9732e013187a6cad34c7ea6bd9d8119cd118907c7faeb934ed533085389e3e55f7ba0649697e7563ec87380c6fc62ff9a1c935
|
7
|
+
data.tar.gz: 5a1d77f26c7886fc1a96edee8a920df4262385fce001209d640c9427e986e9d95d2d9a8232fca17a670758f45a7bc219d728386c080606d1f8bc348cb71ba0a3
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
[![tests](https://github.com/Shopify/money/workflows/tests/badge.svg)](https://github.com/Shopify/money/actions?query=workflow%3Atests+branch%3Amain)
|
4
4
|
|
5
5
|
|
6
|
-
money_column expects a DECIMAL(21,3) database field.
|
6
|
+
`money_column` expects a `DECIMAL(21,3)` database field.
|
7
7
|
|
8
8
|
### Features
|
9
9
|
|
@@ -195,17 +195,11 @@ require:
|
|
195
195
|
|
196
196
|
Money/MissingCurrency:
|
197
197
|
Enabled: true
|
198
|
-
#
|
199
|
-
# it can autocorrect this by specifying a default currency.
|
200
|
-
ReplacementCurrency: CAD
|
198
|
+
# ReplacementCurrency: CAD
|
201
199
|
|
202
200
|
Money/ZeroMoney:
|
203
201
|
Enabled: true
|
204
|
-
# Same here:
|
205
202
|
# ReplacementCurrency: CAD
|
206
|
-
|
207
|
-
Money/UnsafeToMoney:
|
208
|
-
Enabled: true
|
209
203
|
```
|
210
204
|
|
211
205
|
## Contributing to money
|
@@ -218,6 +212,21 @@ Money/UnsafeToMoney:
|
|
218
212
|
- Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
219
213
|
- Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
220
214
|
|
215
|
+
### Releasing
|
216
|
+
|
217
|
+
To release a new version of the gem, follow these steps:
|
218
|
+
|
219
|
+
- Audit what has changed since the last version of the gem was released.
|
220
|
+
- Determine what the next version number should be, according to [Semantic Versioning](https://semver.org/).
|
221
|
+
- Open a PR to update the version accordingly in `lib/money/version`.
|
222
|
+
- After getting approval, merge the PR.
|
223
|
+
- [**Publish** a release in Github](https://github.com/Shopify/money/releases/new):
|
224
|
+
- Target the `main` branch with a tag matching the new version, prefixed with `v` (e.g. `v1.2.3`).
|
225
|
+
- Use the "Generate Release Notes" button to help generate the copy for the release. Include **consumer facing changes** in the release notes.
|
226
|
+
- Deploy the new version to Rubygems using [ShipIt](https://shipit.shopify.io/shopify/money/production).
|
227
|
+
- For more information see [the publish a gem vault page](https://vault.shopify.io/page/Publish-a-new-version-of-an-internal-gem~dhbc57d.md)
|
228
|
+
- You are now responsible to [merge the bump PR in core](https://github.com/Shopify/shopify/pulls?q=is%3Aopen+is%3Apr+author%3Aapp%2Fdependabot+shopify-money+)
|
229
|
+
|
221
230
|
## Copyright
|
222
231
|
|
223
232
|
Copyright (c) 2011 Shopify. See LICENSE.txt for
|
data/lib/money/money.rb
CHANGED
@@ -106,7 +106,15 @@ class Money
|
|
106
106
|
private
|
107
107
|
|
108
108
|
def new_from_money(amount, currency)
|
109
|
-
|
109
|
+
currency = Helpers.value_to_currency(currency)
|
110
|
+
|
111
|
+
if amount.no_currency?
|
112
|
+
return Money.new(amount.value, currency)
|
113
|
+
end
|
114
|
+
|
115
|
+
if amount.currency.compatible?(currency)
|
116
|
+
return amount
|
117
|
+
end
|
110
118
|
|
111
119
|
msg = "Money.new is attempting to change currency of an existing money object"
|
112
120
|
if Money.config.legacy_deprecations
|
@@ -216,14 +224,8 @@ class Money
|
|
216
224
|
return Money.new(value, new_currency)
|
217
225
|
end
|
218
226
|
|
219
|
-
|
220
|
-
|
221
|
-
if Money.config.legacy_deprecations
|
222
|
-
Money.deprecate("#{msg}. A Money::IncompatibleCurrencyError will raise in the next major release")
|
223
|
-
else
|
224
|
-
raise Money::IncompatibleCurrencyError, msg
|
225
|
-
end
|
226
|
-
end
|
227
|
+
ensure_compatible_currency(Helpers.value_to_currency(new_currency),
|
228
|
+
"to_money is attempting to change currency of an existing money object from #{currency} to #{new_currency}")
|
227
229
|
|
228
230
|
self
|
229
231
|
end
|
@@ -358,24 +360,33 @@ class Money
|
|
358
360
|
|
359
361
|
private
|
360
362
|
|
361
|
-
def arithmetic(
|
362
|
-
case
|
363
|
+
def arithmetic(other)
|
364
|
+
case other
|
363
365
|
when Money
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
366
|
+
ensure_compatible_currency(other.currency,
|
367
|
+
"mathematical operation not permitted for Money objects with different currencies #{other.currency} and #{currency}.")
|
368
|
+
yield(other)
|
369
|
+
|
370
|
+
when Numeric, String
|
371
|
+
yield(Money.new(other, currency))
|
372
|
+
|
373
|
+
else
|
374
|
+
if Money.config.legacy_deprecations && other.respond_to?(:to_money)
|
375
|
+
Money.deprecate("#{other.inspect} is being implicitly coerced into a Money object. Call `to_money` on this object to transform it into a money explicitly. An TypeError will raise in the next major release")
|
376
|
+
yield(other.to_money(currency))
|
377
|
+
else
|
378
|
+
raise TypeError, "#{other.class.name} can't be coerced into Money"
|
371
379
|
end
|
372
|
-
|
380
|
+
end
|
381
|
+
end
|
373
382
|
|
374
|
-
|
375
|
-
|
383
|
+
def ensure_compatible_currency(other_currency, msg)
|
384
|
+
return if currency.compatible?(other_currency)
|
376
385
|
|
386
|
+
if Money.config.legacy_deprecations
|
387
|
+
Money.deprecate("#{msg}. A Money::IncompatibleCurrencyError will raise in the next major release")
|
377
388
|
else
|
378
|
-
raise
|
389
|
+
raise Money::IncompatibleCurrencyError, msg
|
379
390
|
end
|
380
391
|
end
|
381
392
|
|
data/lib/money/version.rb
CHANGED
data/lib/rubocop/cop/money.rb
CHANGED
data/spec/money_spec.rb
CHANGED
@@ -41,6 +41,16 @@ RSpec.describe "Money" do
|
|
41
41
|
expect(Money.new(1, 'CAD').to_money('CAD')).to eq(Money.new(1, 'CAD'))
|
42
42
|
end
|
43
43
|
|
44
|
+
it "#to_money works with money objects that doesn't have a currency" do
|
45
|
+
money = Money.new(1, Money::NULL_CURRENCY).to_money('USD')
|
46
|
+
expect(money.value).to eq(1)
|
47
|
+
expect(money.currency.to_s).to eq('USD')
|
48
|
+
|
49
|
+
money = Money.new(1, 'USD').to_money(Money::NULL_CURRENCY)
|
50
|
+
expect(money.value).to eq(1)
|
51
|
+
expect(money.currency.to_s).to eq('USD')
|
52
|
+
end
|
53
|
+
|
44
54
|
it "legacy_deprecations #to_money doesn't overwrite the money object's currency" do
|
45
55
|
configure(legacy_deprecations: true) do
|
46
56
|
expect(Money).to receive(:deprecate).with(match(/to_money is attempting to change currency of an existing money object/)).once
|
@@ -70,6 +80,17 @@ RSpec.describe "Money" do
|
|
70
80
|
|
71
81
|
it "can be constructed with a money object" do
|
72
82
|
expect(Money.new(Money.new(1))).to eq(Money.new(1))
|
83
|
+
expect(Money.new(Money.new(1, "USD"), "USD")).to eq(Money.new(1, "USD"))
|
84
|
+
end
|
85
|
+
|
86
|
+
it "can be constructed with a money object with a null currency" do
|
87
|
+
money = Money.new(Money.new(1, Money::NULL_CURRENCY), 'USD')
|
88
|
+
expect(money.value).to eq(1)
|
89
|
+
expect(money.currency.to_s).to eq('USD')
|
90
|
+
|
91
|
+
money = Money.new(Money.new(1, 'USD'), Money::NULL_CURRENCY)
|
92
|
+
expect(money.value).to eq(1)
|
93
|
+
expect(money.currency.to_s).to eq('USD')
|
73
94
|
end
|
74
95
|
|
75
96
|
it "constructor raises when changing currency" do
|
@@ -556,13 +577,49 @@ RSpec.describe "Money" do
|
|
556
577
|
it { expect(cad_10 == usd_10).to(eq(false)) }
|
557
578
|
end
|
558
579
|
|
559
|
-
describe('
|
580
|
+
describe('coerced types') do
|
560
581
|
it { expect(cad_10 <=> 10.00).to(eq(0)) }
|
561
582
|
it { expect(cad_10 > 10.00).to(eq(false)) }
|
562
583
|
it { expect(cad_10 >= 10.00).to(eq(true)) }
|
563
584
|
it { expect(cad_10 == 10.00).to(eq(false)) }
|
564
585
|
it { expect(cad_10 <= 10.00).to(eq(true)) }
|
565
586
|
it { expect(cad_10 < 10.00).to(eq(false)) }
|
587
|
+
it { expect(cad_10 <=>'10.00').to(eq(0)) }
|
588
|
+
it { expect(cad_10 > '10.00').to(eq(false)) }
|
589
|
+
it { expect(cad_10 >= '10.00').to(eq(true)) }
|
590
|
+
it { expect(cad_10 == '10.00').to(eq(false)) }
|
591
|
+
it { expect(cad_10 <= '10.00').to(eq(true)) }
|
592
|
+
it { expect(cad_10 < '10.00').to(eq(false)) }
|
593
|
+
end
|
594
|
+
|
595
|
+
describe('to_money coerced types') do
|
596
|
+
let(:coercible_object) do
|
597
|
+
double("coercible_object").tap do |mock|
|
598
|
+
allow(mock).to receive(:to_money).with(any_args) { |currency| Money.new(10, currency) }
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
it { expect { cad_10 <=> coercible_object }.to(raise_error(TypeError)) }
|
603
|
+
it { expect { cad_10 > coercible_object }.to(raise_error(TypeError)) }
|
604
|
+
it { expect { cad_10 >= coercible_object }.to(raise_error(TypeError)) }
|
605
|
+
it { expect { cad_10 <= coercible_object }.to(raise_error(TypeError)) }
|
606
|
+
it { expect { cad_10 < coercible_object }.to(raise_error(TypeError)) }
|
607
|
+
it { expect { cad_10 + coercible_object }.to(raise_error(TypeError)) }
|
608
|
+
it { expect { cad_10 - coercible_object }.to(raise_error(TypeError)) }
|
609
|
+
|
610
|
+
describe('with legacy_deprecations') do
|
611
|
+
around(:each) do |test|
|
612
|
+
configure(legacy_deprecations: true) { test.run }
|
613
|
+
end
|
614
|
+
|
615
|
+
it { expect(Money).to(receive(:deprecate).once); expect(cad_10 <=> coercible_object).to(eq(0)) }
|
616
|
+
it { expect(Money).to(receive(:deprecate).once); expect(cad_10 > coercible_object).to(eq(false)) }
|
617
|
+
it { expect(Money).to(receive(:deprecate).once); expect(cad_10 >= coercible_object).to(eq(true)) }
|
618
|
+
it { expect(Money).to(receive(:deprecate).once); expect(cad_10 <= coercible_object).to(eq(true)) }
|
619
|
+
it { expect(Money).to(receive(:deprecate).once); expect(cad_10 < coercible_object).to(eq(false)) }
|
620
|
+
it { expect(Money).to(receive(:deprecate).once); expect(cad_10 + coercible_object).to(eq(Money.new(20, 'CAD'))) }
|
621
|
+
it { expect(Money).to(receive(:deprecate).once); expect(cad_10 - coercible_object).to(eq(Money.new(0, 'CAD'))) }
|
622
|
+
end
|
566
623
|
end
|
567
624
|
end
|
568
625
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shopify-money
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify Inc
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-04-
|
11
|
+
date: 2024-04-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -143,7 +143,6 @@ files:
|
|
143
143
|
- lib/money_column/railtie.rb
|
144
144
|
- lib/rubocop/cop/money.rb
|
145
145
|
- lib/rubocop/cop/money/missing_currency.rb
|
146
|
-
- lib/rubocop/cop/money/unsafe_to_money.rb
|
147
146
|
- lib/rubocop/cop/money/zero_money.rb
|
148
147
|
- lib/shopify-money.rb
|
149
148
|
- money.gemspec
|
@@ -164,7 +163,6 @@ files:
|
|
164
163
|
- spec/rails/job_argument_serializer_spec.rb
|
165
164
|
- spec/rails_spec_helper.rb
|
166
165
|
- spec/rubocop/cop/money/missing_currency_spec.rb
|
167
|
-
- spec/rubocop/cop/money/unsafe_to_money_spec.rb
|
168
166
|
- spec/rubocop/cop/money/zero_money_spec.rb
|
169
167
|
- spec/rubocop_helper.rb
|
170
168
|
- spec/schema.rb
|
@@ -212,7 +210,6 @@ test_files:
|
|
212
210
|
- spec/rails/job_argument_serializer_spec.rb
|
213
211
|
- spec/rails_spec_helper.rb
|
214
212
|
- spec/rubocop/cop/money/missing_currency_spec.rb
|
215
|
-
- spec/rubocop/cop/money/unsafe_to_money_spec.rb
|
216
213
|
- spec/rubocop/cop/money/zero_money_spec.rb
|
217
214
|
- spec/rubocop_helper.rb
|
218
215
|
- spec/schema.rb
|
@@ -1,35 +0,0 @@
|
|
1
|
-
|
2
|
-
module RuboCop
|
3
|
-
module Cop
|
4
|
-
module Money
|
5
|
-
# Prevents the use of `to_money` because it has inconsistent behaviour.
|
6
|
-
# Use `Money.new` instead.
|
7
|
-
#
|
8
|
-
# @example
|
9
|
-
# # bad
|
10
|
-
# "2.000".to_money("USD") #<Money value:2000.00 currency:USD>
|
11
|
-
#
|
12
|
-
# # good
|
13
|
-
# Money.new("2.000", "USD") #<Money value:2.00 currency:USD>
|
14
|
-
class UnsafeToMoney < Cop
|
15
|
-
MSG = '`to_money` has inconsistent behaviour. Use `Money.new` instead.'.freeze
|
16
|
-
|
17
|
-
def on_send(node)
|
18
|
-
return unless node.method?(:to_money)
|
19
|
-
return if node.receiver.nil? || node.receiver.is_a?(AST::NumericNode)
|
20
|
-
|
21
|
-
add_offense(node, location: :selector)
|
22
|
-
end
|
23
|
-
|
24
|
-
def autocorrect(node)
|
25
|
-
lambda do |corrector|
|
26
|
-
receiver = node.receiver.source
|
27
|
-
args = node.arguments.map(&:source)
|
28
|
-
args.prepend(receiver)
|
29
|
-
corrector.replace(node.loc.expression, "Money.new(#{args.join(', ')})")
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../../../rubocop_helper'
|
4
|
-
require 'rubocop/cop/money/unsafe_to_money'
|
5
|
-
|
6
|
-
RSpec.describe RuboCop::Cop::Money::UnsafeToMoney do
|
7
|
-
subject(:cop) { described_class.new(config) }
|
8
|
-
|
9
|
-
let(:config) { RuboCop::Config.new }
|
10
|
-
|
11
|
-
context 'with default configuration' do
|
12
|
-
it 'does not register an offense for literal integer' do
|
13
|
-
expect_no_offenses(<<~RUBY)
|
14
|
-
1.to_money
|
15
|
-
RUBY
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'does not register an offense for literal float' do
|
19
|
-
expect_no_offenses(<<~RUBY)
|
20
|
-
1.000.to_money
|
21
|
-
RUBY
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'registers an offense and corrects for Money.new without a currency argument' do
|
25
|
-
expect_offense(<<~RUBY)
|
26
|
-
'2.000'.to_money
|
27
|
-
^^^^^^^^ #{described_class::MSG}
|
28
|
-
RUBY
|
29
|
-
|
30
|
-
expect_correction(<<~RUBY)
|
31
|
-
Money.new('2.000')
|
32
|
-
RUBY
|
33
|
-
end
|
34
|
-
|
35
|
-
it 'registers an offense and corrects for Money.new with a currency argument' do
|
36
|
-
expect_offense(<<~RUBY)
|
37
|
-
'2.000'.to_money('USD')
|
38
|
-
^^^^^^^^ #{described_class::MSG}
|
39
|
-
RUBY
|
40
|
-
|
41
|
-
expect_correction(<<~RUBY)
|
42
|
-
Money.new('2.000', 'USD')
|
43
|
-
RUBY
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'registers an offense and corrects for Money.new with a complex receiver' do
|
47
|
-
expect_offense(<<~RUBY)
|
48
|
-
obj.money.to_money('USD')
|
49
|
-
^^^^^^^^ #{described_class::MSG}
|
50
|
-
RUBY
|
51
|
-
|
52
|
-
expect_correction(<<~RUBY)
|
53
|
-
Money.new(obj.money, 'USD')
|
54
|
-
RUBY
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'does not register an offense for receiver-less calls' do
|
58
|
-
expect_no_offenses(<<~RUBY)
|
59
|
-
a = to_money
|
60
|
-
RUBY
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|