Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V3.0.0 #318

Merged
merged 16 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
shopify-money (2.2.2)
shopify-money (3.0.0)

GEM
remote: https://rubygems.org/
Expand Down
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@

gem 'shopify-money'

## Upgrading to v1.0

see instructions and breaking changes: https://github.com/Shopify/money/blob/main/UPGRADING.md

## Usage

``` ruby
Expand Down Expand Up @@ -151,6 +147,16 @@ Money::Currency.new("JPY").minor_units # => 0
Money::Currency.new("MGA").minor_units # => 1
```

### Convert Currency

`Money.new(money * exchange_rate, "JPY")` will raise an exception. The valid alternatives are:

```ruby
Money.new(money.value * exchange_rate, "JPY")
# Or
money.convert_currency(exchange_rate, "JPY")
```

## Money column

Since money internally uses BigDecimal it's logical to use a `decimal` column
Expand Down
16 changes: 8 additions & 8 deletions lib/money/core_extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,20 @@ def to_money(currency = nil)
return Money.new(self, currency)
end

new_value = BigDecimal(self, exception: false)&.round(currency.minor_units)
unless new_value.nil?
return Money.new(self, currency)
end

return Money.new(0, currency) if self.empty?

Money::Parser::Fuzzy.parse(self, currency).tap do |money|
new_value = BigDecimal(self, exception: false)&.round(currency.minor_units)
old_value = money.value

if new_value != old_value
message = "`\"#{self}\".to_money` will soon behave like `Money.new(\"#{self}\")` and "
message +=
if new_value.nil?
"raise an ArgumentError exception. Use the browser's locale to parse money strings."
else
"return #{new_value} instead of #{old_value}."
end
message = "`\"#{self}\".to_money` will soon behave like `Money.new(\"#{self}\")` and " \
"raise an ArgumentError exception. Use the browser's locale to parse money strings."

Money.deprecate(message)
end
end
Expand Down
6 changes: 1 addition & 5 deletions lib/money/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,7 @@ def value_to_decimal(num)
when Rational
BigDecimal(num, MAX_DECIMAL)
when String
decimal = BigDecimal(num, exception: !Money.config.legacy_deprecations)
return decimal if decimal

Money.deprecate("using Money.new('#{num}') is deprecated and will raise an ArgumentError in the next major release")
DECIMAL_ZERO
BigDecimal(num)
else
raise ArgumentError, "could not parse as decimal #{num.inspect}"
end
Expand Down
11 changes: 5 additions & 6 deletions lib/money/money.rb
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ def coerce(other)
[ReverseOperationProxy.new(other), self]
end

def convert_currency(exchange_rate, new_currency)
Money.new(value * exchange_rate, new_currency)
end

def to_money(new_currency = nil)
if new_currency.nil?
return self
Expand Down Expand Up @@ -371,12 +375,7 @@ def arithmetic(other)
yield(Money.new(other, currency))

else
if Money.config.legacy_deprecations && other.respond_to?(:to_money)
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")
yield(other.to_money(currency))
else
raise TypeError, "#{other.class.name} can't be coerced into Money"
end
raise TypeError, "#{other.class.name} can't be coerced into a Money object"
end
end

Expand Down
12 changes: 2 additions & 10 deletions lib/money/parser/fuzzy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,7 @@ def extract_amount_from_string(input, currency, strict)
number = number.to_s.strip

if number.empty?
if Money.config.legacy_deprecations && !strict
Money.deprecate("invalid money strings will raise in the next major release \"#{input}\"")
if !strict
return '0'
else
raise MoneyFormatError, "invalid money string: #{input}"
Expand All @@ -120,9 +119,7 @@ def extract_amount_from_string(input, currency, strict)
return amount.tr(ESCAPED_NON_COMMA_MARKS, '').sub(',', '.')
end

if Money.config.legacy_deprecations && !strict
Money.deprecate("invalid money strings will raise in the next major release \"#{input}\"")
else
if strict
raise MoneyFormatError, "invalid money string: #{input}"
end

Expand Down Expand Up @@ -161,11 +158,6 @@ def last_digits_decimals?(digits, marks, currency)
return true
end

# legacy support for 1.000 USD
if digits.last.size == 3 && digits.first.size <= 3 && currency.minor_units < 3
return false
end

# The last mark matches the one used by the provided currency to delimiter decimals
currency.decimal_mark == last_mark
end
Expand Down
2 changes: 1 addition & 1 deletion lib/money/version.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# frozen_string_literal: true
class Money
VERSION = "2.2.2"
VERSION = "3.0.0"
end
5 changes: 3 additions & 2 deletions spec/core_extensions_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@

it "#to_money to handle thousands delimiters" do
configure(legacy_deprecations: true) do
expect(Money).to receive(:deprecate).exactly(4).times
expect("29.000".to_money("USD")).to eq(Money.new("29000", "USD"))
expect("29.000".to_money("USD")).to eq(Money.new("29.00", "USD"))

expect(Money).to receive(:deprecate).exactly(3).times
expect("29.000,00".to_money("USD")).to eq(Money.new("29000", "USD"))
expect("29,000".to_money("USD")).to eq(Money.new("29000", "USD"))
expect("29,000.00".to_money("USD")).to eq(Money.new("29000", "USD"))
Expand Down
3 changes: 1 addition & 2 deletions spec/deprecations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

RSpec.describe "deprecations" do
it "has the deprecation_horizon as the next major release" do
allow(Money).to receive(:const_get).with('VERSION').and_return("2.1.0")
expect(Money.active_support_deprecator.deprecation_horizon).to eq("3.0.0")
expect(Money.active_support_deprecator.deprecation_horizon).to eq("4.0.0")
end
end
7 changes: 2 additions & 5 deletions spec/helpers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,8 @@
expect(subject.value_to_decimal(' -1.23 ')).to eq(-amount)
end

it 'invalid string returns zero' do
configure(legacy_deprecations: true) do
expect(Money).to receive(:deprecate).once
expect(subject.value_to_decimal('invalid')).to eq(0)
end
it 'invalid string raises error' do
expect { subject.value_to_decimal('invalid') }.to raise_error(ArgumentError)
end

it 'raises on invalid object' do
Expand Down
25 changes: 11 additions & 14 deletions spec/money_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
expect(Money.new(0, Money::NULL_CURRENCY)).to eq(Money.new(0))
end

it "converts to a new currency" do
expect(Money.new(10, "USD").convert_currency(150, "JPY")).to eq(Money.new(1500, "JPY"))
end

it "returns itself with to_money" do
expect(money.to_money).to eq(money)
expect(amount_money.to_money).to eq(amount_money)
Expand Down Expand Up @@ -104,13 +108,6 @@
end
end

it "legacy_deprecations defaults to 0 when constructed with an invalid string" do
configure(legacy_deprecations: true) do
expect(Money).to receive(:deprecate).once
expect(Money.new('invalid', 'USD')).to eq(Money.new(0.00, 'USD'))
end
end

it "raises when constructed with an invalid string" do
expect{ Money.new('invalid') }.to raise_error(ArgumentError)
end
Expand Down Expand Up @@ -612,13 +609,13 @@
configure(legacy_deprecations: true) { test.run }
end

it { expect(Money).to(receive(:deprecate).once); expect(cad_10 <=> coercible_object).to(eq(0)) }
it { expect(Money).to(receive(:deprecate).once); expect(cad_10 > coercible_object).to(eq(false)) }
it { expect(Money).to(receive(:deprecate).once); expect(cad_10 >= coercible_object).to(eq(true)) }
it { expect(Money).to(receive(:deprecate).once); expect(cad_10 <= coercible_object).to(eq(true)) }
it { expect(Money).to(receive(:deprecate).once); expect(cad_10 < coercible_object).to(eq(false)) }
it { expect(Money).to(receive(:deprecate).once); expect(cad_10 + coercible_object).to(eq(Money.new(20, 'CAD'))) }
it { expect(Money).to(receive(:deprecate).once); expect(cad_10 - coercible_object).to(eq(Money.new(0, 'CAD'))) }
it { expect { cad_10 <=> coercible_object }.to(raise_error(TypeError)) }
it { expect { cad_10 > coercible_object }.to(raise_error(TypeError)) }
it { expect { cad_10 >= coercible_object }.to(raise_error(TypeError)) }
it { expect { cad_10 <= coercible_object }.to(raise_error(TypeError)) }
it { expect { cad_10 < coercible_object }.to(raise_error(TypeError)) }
it { expect { cad_10 + coercible_object }.to(raise_error(TypeError)) }
it { expect { cad_10 - coercible_object }.to(raise_error(TypeError)) }
end
end
end
Expand Down
7 changes: 2 additions & 5 deletions spec/parser/accounting_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@
end

it "parses an invalid string to $0" do
configure(legacy_deprecations: true) do
expect(Money).to receive(:deprecate).once
expect(@parser.parse("no money", 'USD')).to eq(Money.new(0, 'USD'))
end
expect(@parser.parse("no money", 'USD')).to eq(Money.new(0, 'USD'))
end

it "parses a single digit integer string" do
Expand Down Expand Up @@ -122,7 +119,7 @@

it "parses thousands amount" do
Money.with_currency(Money::NULL_CURRENCY) do
expect(@parser.parse("1.000")).to eq(Money.new(1000.00))
expect(@parser.parse("1.000")).to eq(Money.new(1.00))
end
end

Expand Down
23 changes: 7 additions & 16 deletions spec/parser/fuzzy_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,8 @@
end

it "parses an invalid string when not strict" do
configure(legacy_deprecations: true) do
expect(Money).to receive(:deprecate).twice
expect(@parser.parse("no money", 'USD')).to eq(Money.new(0, 'USD'))
expect(@parser.parse("1..", 'USD')).to eq(Money.new(1, 'USD'))
end
expect(@parser.parse("no money", 'USD')).to eq(Money.new(0, 'USD'))
expect(@parser.parse("1..", 'USD')).to eq(Money.new(1, 'USD'))
end

it "parses raise with an invalid string and strict option" do
Expand Down Expand Up @@ -140,7 +137,7 @@
end

it "parses no currency amount" do
expect(@parser.parse("1.000", Money::NULL_CURRENCY)).to eq(Money.new(1000, Money::NULL_CURRENCY))
expect(@parser.parse("1.000", Money::NULL_CURRENCY)).to eq(Money.new(1, Money::NULL_CURRENCY))
end

it "parses amount with more than 3 decimals correctly" do
Expand All @@ -152,10 +149,7 @@
end

it "parses amount with multiple inconsistent thousands delimiters" do
configure(legacy_deprecations: true) do
expect(Money).to receive(:deprecate).once
expect(@parser.parse("1.1.11.111", 'USD')).to eq(Money.new(1_111_111, 'USD'))
end
expect(@parser.parse("1.1.11.111", 'USD')).to eq(Money.new(1_111_111, 'USD'))
end

it "parses raises with multiple inconsistent thousands delimiters and strict option" do
Expand Down Expand Up @@ -226,10 +220,7 @@
end

it "parses amount with multiple inconsistent thousands delimiters" do
configure(legacy_deprecations: true) do
expect(Money).to receive(:deprecate).once
expect(@parser.parse("1,1,11,111", 'USD')).to eq(Money.new(1_111_111, 'USD'))
end
expect(@parser.parse("1,1,11,111", 'USD')).to eq(Money.new(1_111_111, 'USD'))
end

it "parses raises with multiple inconsistent thousands delimiters and strict option" do
Expand All @@ -255,7 +246,7 @@
describe "no decimal currency" do
it "parses thousands correctly" do
expect(@parser.parse("1,111", "JPY")).to eq(Money.new(1_111, 'JPY'))
expect(@parser.parse("1.111", "JPY")).to eq(Money.new(1_111, 'JPY'))
expect(@parser.parse("1.111", "JPY")).to eq(Money.new(1, 'JPY'))
expect(@parser.parse("1 111", "JPY")).to eq(Money.new(1_111, 'JPY'))
expect(@parser.parse("1111,111", "JPY")).to eq(Money.new(1_111_111, 'JPY'))
end
Expand All @@ -270,7 +261,7 @@
describe "two decimal currency" do
it "parses thousands correctly" do
expect(@parser.parse("1,111", "USD")).to eq(Money.new(1_111, 'USD'))
expect(@parser.parse("1.111", "USD")).to eq(Money.new(1_111, 'USD'))
expect(@parser.parse("1.111", "USD")).to eq(Money.new(1.11, 'USD'))
expect(@parser.parse("1 111", "USD")).to eq(Money.new(1_111, 'USD'))
expect(@parser.parse("1111,111", "USD")).to eq(Money.new(1_111_111, 'USD'))
end
Expand Down
Loading