Skip to content

Commit

Permalink
Merge branch 'main' into ruby-stdlib-deprecation-warning
Browse files Browse the repository at this point in the history
  • Loading branch information
semmons99 authored Mar 13, 2024
2 parents f2fd337 + 51497ed commit c473f3e
Show file tree
Hide file tree
Showing 12 changed files with 84 additions and 68 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
ruby-version: ['2.6', '2.7', '3.0', '3.1', '3.2']
ruby-version: ['2.6', '2.7', '3.0', '3.1', '3.2', '3.3']

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
Expand Down
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ Thomas E Enebo
Thomas Weymuth
Ticean Bennett
Tien Nguyen
Till Grosch
Tim Hart
Tim Krins
Tobias Luetke
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@

## Upcoming

-

## 6.19.0

- Change Peruvian Sol (PEN) decimal mark and thousands separator.
- Fix deprecation warning for BigDecimal being moved out from stdlib.
- Improves Precision and Simplifies Allocation Logic
- explicit declaration of i18n locales
- Add space to CHF format
- Update deprecation message to suggest correct formatting.

## 6.18.0

- Add second dobra (STN) from São Tomé and Príncipe
- Add Sierra Leonean (new) leone (SLE) from Sierra Leone
- Correct South African Rand (ZAR) to use comma decimal mark, and space thousands separator
- Use euro symbol as html_entity for euro currency
- Update Georgian Lari symbol
Expand Down
2 changes: 1 addition & 1 deletion config/currency_iso.json
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@
"format": "%u %n",
"html_entity": "",
"decimal_mark": ".",
"thousands_separator": ",",
"thousands_separator": "'",
"iso_numeric": "756",
"smallest_denomination": 5
},
Expand Down
56 changes: 28 additions & 28 deletions lib/money/money/allocation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@

class Money
class Allocation
# Splits a given amount in parts without losing pennies.
# The left-over pennies will be distributed round-robin amongst the parts. This means that
# parts listed first will likely receive more pennies than the ones listed later.
# Splits a given amount in parts. The allocation is based on the parts' proportions
# or evenly if parts are numerically specified.
#
# The results should always add up to the original amount.
#
# The parts can be specified as:
# Numeric — performs the split between a given number of parties evenely
# Array<Numeric> — allocates the amounts proportionally to the given array
# @param amount [Numeric] The total amount to be allocated.
# @param parts [Numeric, Array<Numeric>] Number of parts to split into or an array (proportions for allocation)
# @param whole_amounts [Boolean] Specifies whether to allocate whole amounts only. Defaults to true.
#
# @return [Array<Numeric>] An array containing the allocated amounts.
# @raise [ArgumentError] If parts is empty or not provided.
def self.generate(amount, parts, whole_amounts = true)
parts = if parts.is_a?(Numeric)
Array.new(parts, 1)
Expand All @@ -21,7 +22,12 @@ def self.generate(amount, parts, whole_amounts = true)
parts.dup
end

raise ArgumentError, 'need at least one party' if parts.empty?
raise ArgumentError, 'need at least one part' if parts.empty?

if [amount, *parts].any? { |i| i.is_a?(BigDecimal) || i.is_a?(Float) || i.is_a?(Rational) }
amount = convert_to_big_decimal(amount)
parts.map! { |p| convert_to_big_decimal(p) }
end

result = []
remaining_amount = amount
Expand All @@ -40,29 +46,23 @@ def self.generate(amount, parts, whole_amounts = true)
remaining_amount -= current_split
end

## round-robin allocation of any remaining pennies
if result.size > 0
while remaining_amount != 0
index = 0

amount_to_distribute = [1, remaining_amount.abs].min

if remaining_amount > 0
result[index] += amount_to_distribute
remaining_amount -= amount_to_distribute
else
result[index] -= amount_to_distribute
remaining_amount += amount_to_distribute
end
result
end

index += 1
if index > result.size
index = 0
end
end
# Converts a given number to BigDecimal.
# This method supports inputs of BigDecimal, Rational, and other numeric types by ensuring they are all returned
# as BigDecimal instances for consistent handling.
#
# @param number [Numeric, BigDecimal, Rational] The number to convert.
# @return [BigDecimal] The converted number as a BigDecimal.
def self.convert_to_big_decimal(number)
if number.is_a? BigDecimal
number
elsif number.is_a? Rational
BigDecimal(number.to_f.to_s)
else
BigDecimal(number.to_s)
end

result
end
end
end
1 change: 1 addition & 0 deletions money.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,6 @@ Gem::Specification.new do |s|
s.metadata['changelog_uri'] = 'https://github.com/RubyMoney/money/blob/master/CHANGELOG.md'
s.metadata['source_code_uri'] = 'https://github.com/RubyMoney/money/'
s.metadata['bug_tracker_uri'] = 'https://github.com/RubyMoney/money/issues'
s.metadata['rubygems_mfa_required'] = 'true'
end
end
14 changes: 7 additions & 7 deletions spec/bank/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,25 @@ def setup

describe "#exchange_with" do
it "is not implemented" do
expect { subject.exchange_with(Money.new(100, 'USD'), 'EUR') }.to raise_exception(NotImplementedError)
expect { subject.exchange_with(Money.new(100, 'USD'), 'EUR') }.to raise_error(NotImplementedError)
end
end

describe "#same_currency?" do
it "accepts str/str" do
expect { subject.send(:same_currency?, 'USD', 'EUR') }.to_not raise_exception
expect { subject.send(:same_currency?, 'USD', 'EUR') }.to_not raise_error
end

it "accepts currency/str" do
expect { subject.send(:same_currency?, Money::Currency.wrap('USD'), 'EUR') }.to_not raise_exception
expect { subject.send(:same_currency?, Money::Currency.wrap('USD'), 'EUR') }.to_not raise_error
end

it "accepts str/currency" do
expect { subject.send(:same_currency?, 'USD', Money::Currency.wrap('EUR')) }.to_not raise_exception
expect { subject.send(:same_currency?, 'USD', Money::Currency.wrap('EUR')) }.to_not raise_error
end

it "accepts currency/currency" do
expect { subject.send(:same_currency?, Money::Currency.wrap('USD'), Money::Currency.wrap('EUR')) }.to_not raise_exception
expect { subject.send(:same_currency?, Money::Currency.wrap('USD'), Money::Currency.wrap('EUR')) }.to_not raise_error
end

it "returns true when currencies match" do
Expand All @@ -67,8 +67,8 @@ def setup
expect(subject.send(:same_currency?, Money::Currency.wrap('USD'), Money::Currency.wrap('EUR'))).to be false
end

it "raises an UnknownCurrency exception when an unknown currency is passed" do
expect { subject.send(:same_currency?, 'AAA', 'BBB') }.to raise_exception(Money::Currency::UnknownCurrency)
it "raises an UnknownCurrency error when an unknown currency is passed" do
expect { subject.send(:same_currency?, 'AAA', 'BBB') }.to raise_error(Money::Currency::UnknownCurrency)
end
end
end
2 changes: 1 addition & 1 deletion spec/bank/single_currency_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
it "raises when called" do
expect {
subject.exchange_with(Money.new(100, 'USD'), 'EUR')
}.to raise_exception(Money::Bank::DifferentCurrencyError, "No exchanging of currencies allowed: 1.00 USD to EUR")
}.to raise_error(Money::Bank::DifferentCurrencyError, "No exchanging of currencies allowed: 1.00 USD to EUR")
end
end
end
20 changes: 10 additions & 10 deletions spec/bank/variable_exchange_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@

describe "#exchange_with" do
it "accepts str" do
expect { bank.exchange_with(Money.new(100, 'USD'), 'EUR') }.to_not raise_exception
expect { bank.exchange_with(Money.new(100, 'USD'), 'EUR') }.to_not raise_error
end

it "accepts currency" do
expect { bank.exchange_with(Money.new(100, 'USD'), Money::Currency.wrap('EUR')) }.to_not raise_exception
expect { bank.exchange_with(Money.new(100, 'USD'), Money::Currency.wrap('EUR')) }.to_not raise_error
end

it "exchanges one currency to another" do
Expand All @@ -48,12 +48,12 @@
expect(bank.exchange_with(Money.new(10, 'USD'), 'EUR')).to eq Money.new(13, 'EUR')
end

it "raises an UnknownCurrency exception when an unknown currency is requested" do
expect { bank.exchange_with(Money.new(100, 'USD'), 'BBB') }.to raise_exception(Money::Currency::UnknownCurrency)
it "raises an UnknownCurrency error when an unknown currency is requested" do
expect { bank.exchange_with(Money.new(100, 'USD'), 'BBB') }.to raise_error(Money::Currency::UnknownCurrency)
end

it "raises an UnknownRate exception when an unknown rate is requested" do
expect { bank.exchange_with(Money.new(100, 'USD'), 'JPY') }.to raise_exception(Money::Bank::UnknownRate)
it "raises an UnknownRate error when an unknown rate is requested" do
expect { bank.exchange_with(Money.new(100, 'USD'), 'JPY') }.to raise_error(Money::Bank::UnknownRate)
end

#it "rounds the exchanged result down" do
Expand Down Expand Up @@ -136,8 +136,8 @@
expect(subject.store.get_rate('USD', 'EUR')).to eq 1.25
end

it "raises an UnknownCurrency exception when an unknown currency is passed" do
expect { subject.set_rate('AAA', 'BBB', 1.25) }.to raise_exception(Money::Currency::UnknownCurrency)
it "raises an UnknownCurrency error when an unknown currency is passed" do
expect { subject.set_rate('AAA', 'BBB', 1.25) }.to raise_error(Money::Currency::UnknownCurrency)
end
end

Expand All @@ -147,8 +147,8 @@
expect(subject.get_rate('USD', 'EUR')).to eq 1.25
end

it "raises an UnknownCurrency exception when an unknown currency is passed" do
expect { subject.get_rate('AAA', 'BBB') }.to raise_exception(Money::Currency::UnknownCurrency)
it "raises an UnknownCurrency error when an unknown currency is passed" do
expect { subject.get_rate('AAA', 'BBB') }.to raise_error(Money::Currency::UnknownCurrency)
end

it "delegates options to store, options are a no-op" do
Expand Down
2 changes: 1 addition & 1 deletion spec/currency_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ def to_s
end

it "doesn't create new symbols indefinitely" do
expect { described_class.new("bogus") }.to raise_exception(described_class::UnknownCurrency)
expect { described_class.new("bogus") }.to raise_error(described_class::UnknownCurrency)
expect(Symbol.all_symbols.map{|s| s.to_s}).not_to include("bogus")
end
end
Expand Down
37 changes: 21 additions & 16 deletions spec/money/arithmetic_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -673,19 +673,19 @@
it "raises TypeError dividing by a Money (unless other is a Money)" do
expect {
2 / Money.new(2, 'USD')
}.to raise_exception(TypeError)
}.to raise_error(TypeError)
end

it "raises TypeError subtracting by a Money (unless other is a Money)" do
expect {
2 - Money.new(2, 'USD')
}.to raise_exception(TypeError)
}.to raise_error(TypeError)
end

it "raises TypeError adding by a Money (unless other is a Money)" do
expect {
2 + Money.new(2, 'USD')
}.to raise_exception(TypeError)
}.to raise_error(TypeError)
end

it "allows subtraction from numeric zero" do
Expand All @@ -701,33 +701,33 @@
it "treats multiplication as commutative" do
expect {
2 * Money.new(2, 'USD')
}.to_not raise_exception
}.to_not raise_error
result = 2 * Money.new(2, 'USD')
expect(result).to eq(Money.new(4, 'USD'))
end

it "doesn't work with non-numerics" do
expect {
"2" * Money.new(2, 'USD')
}.to raise_exception(TypeError)
}.to raise_error(TypeError)
end

it "correctly handles <=>" do
expect {
2 < Money.new(2, 'USD')
}.to raise_exception(ArgumentError)
}.to raise_error(ArgumentError)

expect {
2 > Money.new(2, 'USD')
}.to raise_exception(ArgumentError)
}.to raise_error(ArgumentError)

expect {
2 <= Money.new(2, 'USD')
}.to raise_exception(ArgumentError)
}.to raise_error(ArgumentError)

expect {
2 >= Money.new(2, 'USD')
}.to raise_exception(ArgumentError)
}.to raise_error(ArgumentError)

expect(2 <=> Money.new(2, 'USD')).to be_nil
end
Expand All @@ -738,24 +738,23 @@
expect(0.0 >= Money.usd(0)).to eq true
end

it "raises exceptions for all numeric types, not just Integer" do
it "raises errors for all numeric types, not just Integer" do
expect {
2.0 / Money.new(2, 'USD')
}.to raise_exception(TypeError)
}.to raise_error(TypeError)

expect {
Rational(2,3) / Money.new(2, 'USD')
}.to raise_exception(TypeError)
}.to raise_error(TypeError)

expect {
BigDecimal(2) / Money.new(2, 'USD')
}.to raise_exception(TypeError)
}.to raise_error(TypeError)
end
end

%w(+ - / divmod remainder).each do |op|
describe "##{op}" do
subject { ->(other = self.other) { instance.send(op, other) } }
let(:instance) { Money.usd(1) }

context 'when conversions disallowed' do
Expand All @@ -771,12 +770,18 @@

context 'and other is money with different currency' do
let(:other) { Money.gbp(1) }
it { should raise_error Money::Bank::DifferentCurrencyError }

it 'raises Money::Bank::DifferentCurrencyError' do
expect { instance.send(op, other) }.to raise_error Money::Bank::DifferentCurrencyError
end

context 'even for zero' do
let(:instance) { Money.usd(0) }
let(:other) { Money.gbp(0) }
it { should raise_error Money::Bank::DifferentCurrencyError }

it 'raises Money::Bank::DifferentCurrencyError' do
expect { instance.send(op, other) }.to raise_error Money::Bank::DifferentCurrencyError
end
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions spec/money_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@

it "disallows conversions when doing money arithmetic" do
Money.disallow_currency_conversion!
expect { Money.new(100, "USD") + Money.new(100, "EUR") }.to raise_exception(Money::Bank::DifferentCurrencyError)
expect { Money.new(100, "USD") + Money.new(100, "EUR") }.to raise_error(Money::Bank::DifferentCurrencyError)
end
end

Expand Down Expand Up @@ -417,7 +417,7 @@ def expectation.fractional
expect(money.round_to_nearest_cash_value).to eq(-301)
end

it "raises an exception if smallest denomination is not defined" do
it "raises an error if smallest denomination is not defined" do
money = Money.new(100, "XAG")
expect {money.round_to_nearest_cash_value}.to raise_error(Money::UndefinedSmallestDenomination)
end
Expand Down

0 comments on commit c473f3e

Please sign in to comment.