shopify-money 1.2.1 → 2.0.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: 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