Skip to content

Commit

Permalink
Merge branch 'main' into rbs
Browse files Browse the repository at this point in the history
  • Loading branch information
semmons99 authored Mar 18, 2024
2 parents 84a3a4b + 3a45a68 commit 20d54f8
Show file tree
Hide file tree
Showing 15 changed files with 94 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: ['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
2 changes: 2 additions & 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 All @@ -139,3 +140,4 @@ Yuusuke Takizawa
Zubin Henner
Бродяной Александр
Nicolay Hvidsten
Simon Neutert
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +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 LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ MIT License

Copyright (c) 2005 Tobias Lutke
Copyright (c) 2008 Phusion
Copyright (c) 2023 Shane Emmons
Copyright (c) 2024 Shane Emmons

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
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
54 changes: 29 additions & 25 deletions lib/money/money/allocation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@

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 evenly
# 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 +26,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 +50,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
2 changes: 1 addition & 1 deletion lib/money/money/formatting_rules.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def translate_formatting_rules(rules)
end

def localize_formatting_rules(rules)
if currency.iso_code == "JPY" && I18n.locale == :ja
if currency.iso_code == "JPY" && I18n.locale == :ja && rules[:format] == nil
rules[:symbol] = "円" unless rules[:symbol] == false
rules[:format] = '%n%u'
end
Expand Down
4 changes: 4 additions & 0 deletions money.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Gem::Specification.new do |s|
s.description = "A Ruby Library for dealing with money and currency conversion."
s.license = "MIT"

s.add_dependency "bigdecimal"
s.add_dependency 'i18n', [">= 0.6.4", '<= 2']

s.add_development_dependency "bundler"
Expand All @@ -24,12 +25,15 @@ Gem::Specification.new do |s|
s.add_development_dependency "rbs" if RUBY_VERSION >= "2.7.0"
s.add_development_dependency "typeprof" if RUBY_VERSION >= "2.7.0"

s.required_ruby_version = '>= 3.1'

s.files = `git ls-files -z -- config/* lib/* CHANGELOG.md LICENSE money.gemspec README.md`.split("\x0")
s.require_paths = ["lib"]

if s.respond_to?(:metadata)
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
Loading

0 comments on commit 20d54f8

Please sign in to comment.