shopify-money 1.2.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: de174294a88067d61cfcf9321bc62fc3de2b4c265e089d07ff8bedca30ce782a
4
- data.tar.gz: 9cb65f619cf3ceb3054437c3ede664d9fa949859af7858e1f280d82f983cdace
3
+ metadata.gz: 591c60e355144246085ac254fe82b099c85ab4bf7f1e37672b8082331bd9d0aa
4
+ data.tar.gz: 493384ced59c9a37ebc798b4cb499bd0532ea30243c8248704bf0df6f776ca0f
5
5
  SHA512:
6
- metadata.gz: cfd7348a2506689d46b70eebb613c4ebdb3ef33ad31ae361d130c4348691f90049a66a50920f96c9f39f532bfe3fc0901c939a6d2e0be6d90d6d045165209f37
7
- data.tar.gz: 729391b98fbbfc671726cedd35d6ee13afed28f6b88b98d825eb6cae23cc968de72437fdacf51bf5f68dbaf3aabf4f84d296709764031ce83128a4010012a1b7
6
+ metadata.gz: 1867df0795206ccd5996b1d8471f23f60340ace3e5e685ddbb98f601402a7748aade38263dbc7f12971c1e4368919cb0122c53eacf0b96b96df44189be3ca32e
7
+ data.tar.gz: 9ed8869fce8e66c4e9aee275ac3a28c24db47fd2ae8537f2ff3ee100541f122cd9b8b87e7ca102567b976548eb04b53b06f6c560f2e6e12dc3ca506e0bb87f01
@@ -1,7 +1,7 @@
1
1
  name: tests
2
2
 
3
3
  on: [push, pull_request]
4
-
4
+
5
5
  jobs:
6
6
  build:
7
7
 
@@ -9,7 +9,7 @@ jobs:
9
9
 
10
10
  strategy:
11
11
  matrix:
12
- ruby: ['2.6', '2.7', '3.0', '3.1', '3.2']
12
+ ruby: ['3.0', '3.1', '3.2', '3.3']
13
13
 
14
14
  name: Ruby ${{ matrix.ruby }}
15
15
  steps:
data/README.md CHANGED
@@ -1,20 +1,17 @@
1
1
  # money
2
2
 
3
- [![tests](https://github.com/Shopify/money/workflows/tests/badge.svg)](https://github.com/Shopify/money/actions?query=workflow%3Atests+branch%3Amaster)
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
6
  money_column expects a DECIMAL(21,3) database field.
7
7
 
8
8
  ### Features
9
9
 
10
- - Keeps value in decimal
11
- - Provides a `Money` class which encapsulates all information about an certain
12
- amount of money, such as its value and its currency.
13
- - Provides a `Money::Currency` class which encapsulates all information about
14
- a monetary unit.
15
- - Does NOT provides APIs for exchanging money from one currency to another.
16
- - wont lose pennies during division!
17
- - Money::NullCurrency for no currency support
10
+ - Provides a `Money` class which encapsulates all information about a certain amount of money, such as its value and its currency.
11
+ - Provides a `Money::Currency` class which encapsulates all information about a monetary unit.
12
+ - Represents monetary values as decimals. No need to convert your amounts every time you use them. Easily understand the data in your DB.
13
+ - Does NOT provide APIs for exchanging money from one currency to another.
14
+ - Will not lose pennies during divisions
18
15
 
19
16
  ## Installation
20
17
 
@@ -22,7 +19,7 @@ money_column expects a DECIMAL(21,3) database field.
22
19
 
23
20
  ## Upgrading to v1.0
24
21
 
25
- see instructions and breaking changes: https://github.com/Shopify/money/blob/master/UPGRADING.md
22
+ see instructions and breaking changes: https://github.com/Shopify/money/blob/main/UPGRADING.md
26
23
 
27
24
  ## Usage
28
25
 
@@ -32,7 +29,7 @@ require 'money'
32
29
  # 10.00 USD
33
30
  money = Money.new(10.00, "USD")
34
31
  money.subunits #=> 1000
35
- money.currency #=> Currency.new("USD")
32
+ money.currency #=> Money::Currency.new("USD")
36
33
 
37
34
  # Comparisons
38
35
  Money.new(1000, "USD") == Money.new(1000, "USD") #=> true
@@ -43,13 +40,30 @@ Money.new(1000, "USD") != Money.new(1000, "EUR") #=> true
43
40
  # Arithmetic
44
41
  Money.new(1000, "USD") + Money.new(500, "USD") == Money.new(1500, "USD")
45
42
  Money.new(1000, "USD") - Money.new(200, "USD") == Money.new(800, "USD")
46
- Money.new(1000, "USD") / 5 == Money.new(200, "USD")
47
43
  Money.new(1000, "USD") * 5 == Money.new(5000, "USD")
48
44
 
45
+ m = Money.new(1000, "USD")
46
+ # Splitting money evenly
47
+ m.split(2) == [Money.new(500, "USD"), Money.new(500, "USD")]
48
+ m.split(3).map(&:value) == [333.34, 333.33, 333.33]
49
+ m.calculate_splits(2) == { Money.new(500, "USD") => 2 }
50
+ m.calculate_splits(3) == { Money.new(333.34, "USD") => 1, Money.new(333.33, "USD") =>2 }
51
+
52
+ # Allocating money proportionally
53
+ m.allocate([0.50, 0.25, 0.25]).map(&:value) == [500, 250, 250]
54
+ m.allocate([Rational(2, 3), Rational(1, 3)]).map(&:value) == [666.67, 333.33]
55
+
56
+ ## Allocating up to a cutoff
57
+ m.allocate_max_amounts([500, 300, 200]).map(&:value) == [500, 300, 200]
58
+ m.allocate_max_amounts([500, 300, 300]).map(&:value) == [454.55, 272.73, 272.72]
59
+
60
+ # Clamp
61
+ Money.new(50, "USD").clamp(1, 100) == Money.new(50, "USD")
62
+
49
63
  # Unit to subunit conversions
50
- Money.from_subunits(500, "USD") == Money.new(5, "USD") # 5 USD
51
- Money.from_subunits(5, "JPY") == Money.new(5, "JPY") # 5 JPY
52
- Money.from_subunits(5000, "TND") == Money.new(5, "TND") # 5 TND
64
+ Money.from_subunits(500, "USD") == Money.new(5, "USD") # 5 USD
65
+ Money.from_subunits(5, "JPY") == Money.new(5, "JPY") # 5 JPY
66
+ Money.from_subunits(5000, "TND") == Money.new(5, "TND") # 5 TND
53
67
  ```
54
68
 
55
69
  ## Currency
@@ -171,7 +185,7 @@ Money/UnsafeToMoney:
171
185
 
172
186
  ## Contributing to money
173
187
 
174
- - Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
188
+ - Check out the latest main to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
175
189
  - Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
176
190
  - Fork the project
177
191
  - Start a feature/bugfix branch
data/dev.yml CHANGED
@@ -3,7 +3,7 @@
3
3
  ---
4
4
  name: money
5
5
  up:
6
- - ruby: 3.2.0
6
+ - ruby: 3.3.0
7
7
  - bundler
8
8
  commands:
9
9
  test: bundle exec rspec
@@ -14,6 +14,14 @@ end
14
14
  # '100.37'.to_money => #<Money @cents=10037>
15
15
  class String
16
16
  def to_money(currency = nil)
17
- Money::Parser::Fuzzy.parse(self, currency)
17
+ if Money.config.legacy_deprecations
18
+ Money::Parser::Fuzzy.parse(self, currency).tap do |money|
19
+ message = "`#{self}.to_money` will behave like `Money.new` and raise on the next release. " \
20
+ "To parse user input, do so on the browser and use the user's locale."
21
+ Money.deprecate(message) if money.value != BigDecimal(self, exception: false)
22
+ end
23
+ else
24
+ Money.new(self, currency)
25
+ end
18
26
  end
19
27
  end
@@ -3,7 +3,10 @@ Money.class_eval do
3
3
  ACTIVE_SUPPORT_DEFINED = defined?(ActiveSupport)
4
4
 
5
5
  def self.active_support_deprecator
6
- @active_support_deprecator ||= ActiveSupport::Deprecation.new('1.0.0', 'Shopify/Money')
6
+ @active_support_deprecator ||= begin
7
+ next_major_version = Money::VERSION.split(".").first.to_i + 1
8
+ ActiveSupport::Deprecation.new("#{next_major_version}.0.0", "Shopify/Money")
9
+ end
7
10
  end
8
11
 
9
12
  def self.deprecate(message)
data/lib/money/helpers.rb CHANGED
@@ -16,8 +16,6 @@ class Money
16
16
  def value_to_decimal(num)
17
17
  value =
18
18
  case num
19
- when Money
20
- num.value
21
19
  when BigDecimal
22
20
  num
23
21
  when nil, 0, ''
data/lib/money/money.rb CHANGED
@@ -137,13 +137,8 @@ class Money
137
137
  end
138
138
 
139
139
  def *(numeric)
140
- unless numeric.is_a?(Numeric)
141
- if Money.config.legacy_deprecations
142
- Money.deprecate("Multiplying Money with #{numeric.class.name} is deprecated and will be removed in the next major release.")
143
- else
144
- raise ArgumentError, "Money objects can only be multiplied by a Numeric"
145
- end
146
- end
140
+ raise ArgumentError, "Money objects can only be multiplied by a Numeric" unless numeric.is_a?(Numeric)
141
+
147
142
  return self if numeric == 1
148
143
  Money.new(value.to_r * numeric, currency)
149
144
  end
data/lib/money/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  class Money
3
- VERSION = "1.2.1"
3
+ VERSION = "2.0.0"
4
4
  end
@@ -32,7 +32,8 @@ module RuboCop
32
32
  PATTERN
33
33
 
34
34
  def on_send(node)
35
- money_new(node) do |_amount, currency_arg|
35
+ money_new(node) do |amount, currency_arg|
36
+ return if amount&.splat_type?
36
37
  return if currency_arg
37
38
 
38
39
  add_offense(node, message: 'Money is missing currency argument')
@@ -16,7 +16,7 @@ module RuboCop
16
16
 
17
17
  def on_send(node)
18
18
  return unless node.method?(:to_money)
19
- return if node.receiver.is_a?(AST::NumericNode)
19
+ return if node.receiver.nil? || node.receiver.is_a?(AST::NumericNode)
20
20
 
21
21
  add_offense(node, location: :selector)
22
22
  end
data/money.gemspec CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
22
22
  s.add_development_dependency("database_cleaner", "~> 1.6")
23
23
  s.add_development_dependency("sqlite3", "~> 1.4.2")
24
24
 
25
- s.required_ruby_version = '>= 2.6'
25
+ s.required_ruby_version = '>= 3.0'
26
26
 
27
27
  s.files = `git ls-files`.split($/)
28
28
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
@@ -43,8 +43,26 @@ RSpec.describe String do
43
43
  it_should_behave_like "an object supporting to_money"
44
44
 
45
45
  it "parses an empty string to Money.zero" do
46
- expect(''.to_money).to eq(Money.new(0, Money::NULL_CURRENCY))
47
- expect(' '.to_money).to eq(Money.new(0, Money::NULL_CURRENCY))
46
+ expect("".to_money("USD")).to eq(Money.new(0, "USD"))
47
+
48
+ configure(legacy_deprecations: true) do
49
+ expect(Money).to receive(:deprecate).once
50
+ expect(" ".to_money("CAD")).to eq(Money.new(0, "CAD"))
51
+ end
52
+ end
53
+
54
+ it "#to_money to handle thousands delimiters" do
55
+ configure(legacy_deprecations: true) do
56
+ expect(Money).to receive(:deprecate).exactly(4).times
57
+ expect("29.000".to_money("USD")).to eq(Money.new("29000", "USD"))
58
+ expect("29.000,00".to_money("USD")).to eq(Money.new("29000", "USD"))
59
+ expect("29,000".to_money("USD")).to eq(Money.new("29000", "USD"))
60
+ expect("29,000.00".to_money("USD")).to eq(Money.new("29000", "USD"))
61
+ end
62
+ end
63
+
64
+ it "#to_money should behave like Money.new with three decimal places amounts" do
65
+ expect("29.000".to_money("USD")).to eq(Money.new("29.00", "USD"))
48
66
  end
49
67
  end
50
68
 
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+
4
+ RSpec.describe "deprecations" do
5
+ it "has the deprecation_horizon as the next major release" do
6
+ allow(Money).to receive(:const_get).with('VERSION').and_return("2.0.0")
7
+ expect(Money.active_support_deprecator.deprecation_horizon).to eq("3.0.0")
8
+ end
9
+ end
data/spec/helpers_spec.rb CHANGED
@@ -7,8 +7,8 @@ RSpec.describe Money::Helpers do
7
7
  let (:amount) { BigDecimal('1.23') }
8
8
  let (:money) { Money.new(amount) }
9
9
 
10
- it 'returns the value of a money object' do
11
- expect(subject.value_to_decimal(money)).to eq(amount)
10
+ it 'raises when provided with a money object' do
11
+ expect { subject.value_to_decimal(money) }.to raise_error(ArgumentError)
12
12
  end
13
13
 
14
14
  it 'returns itself if it is already a big decimal' do
data/spec/money_spec.rb CHANGED
@@ -290,13 +290,6 @@ RSpec.describe "Money" do
290
290
  expect(((1.0 / 12) * Money.new(3.3))).to eq(Money.new(0.28))
291
291
  end
292
292
 
293
- it "legacy_deprecations is multipliable by a money object" do
294
- configure(legacy_deprecations: true) do
295
- expect(Money).to receive(:deprecate).once
296
- expect((Money.new(3.3, 'USD') * Money.new(1, 'USD'))).to eq(Money.new(3.3, 'USD'))
297
- end
298
- end
299
-
300
293
  it "raises when multiplied by a money object" do
301
294
  expect{ (Money.new(3.3) * Money.new(1)) }.to raise_error(ArgumentError)
302
295
  end
@@ -26,6 +26,13 @@ RSpec.describe RuboCop::Cop::Money::MissingCurrency do
26
26
  RUBY
27
27
  end
28
28
 
29
+ it 'does not register an offense for Money.new with splat argument' do
30
+ expect_no_offenses(<<~RUBY)
31
+ value_and_currency = [1, 'CAD']
32
+ Money.new(*value_and_currency)
33
+ RUBY
34
+ end
35
+
29
36
  it 'registers an offense and corrects for Money.new without a currency argument' do
30
37
  expect_offense(<<~RUBY)
31
38
  Money.new
@@ -53,5 +53,11 @@ RSpec.describe RuboCop::Cop::Money::UnsafeToMoney do
53
53
  Money.new(obj.money, 'USD')
54
54
  RUBY
55
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
56
62
  end
57
63
  end
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: 1.2.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify Inc
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-20 00:00:00.000000000 Z
11
+ date: 2024-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -149,6 +149,7 @@ files:
149
149
  - spec/core_extensions_spec.rb
150
150
  - spec/currency/loader_spec.rb
151
151
  - spec/currency_spec.rb
152
+ - spec/deprecations_spec.rb
152
153
  - spec/helpers_spec.rb
153
154
  - spec/money_column_spec.rb
154
155
  - spec/money_spec.rb
@@ -178,14 +179,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
178
179
  requirements:
179
180
  - - ">="
180
181
  - !ruby/object:Gem::Version
181
- version: '2.6'
182
+ version: '3.0'
182
183
  required_rubygems_version: !ruby/object:Gem::Requirement
183
184
  requirements:
184
185
  - - ">="
185
186
  - !ruby/object:Gem::Version
186
187
  version: '0'
187
188
  requirements: []
188
- rubygems_version: 3.4.14
189
+ rubygems_version: 3.5.5
189
190
  signing_key:
190
191
  specification_version: 4
191
192
  summary: Shopify's money gem
@@ -195,6 +196,7 @@ test_files:
195
196
  - spec/core_extensions_spec.rb
196
197
  - spec/currency/loader_spec.rb
197
198
  - spec/currency_spec.rb
199
+ - spec/deprecations_spec.rb
198
200
  - spec/helpers_spec.rb
199
201
  - spec/money_column_spec.rb
200
202
  - spec/money_spec.rb