Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement Money::Bank and update VariableExchangeBank to be a subclass
of it
- Loading branch information
Showing
5 changed files
with
236 additions
and
80 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
require 'thread' | ||
|
||
class Money | ||
class Bank | ||
class UnknownRate < StandardError; end | ||
|
||
def self.instance | ||
@@singleton | ||
end | ||
|
||
@@singleton = Bank.new | ||
|
||
def initialize(&block) | ||
@rates = {} | ||
@mutex = Mutex.new | ||
@rounding_method = block | ||
end | ||
|
||
def exchange(cents, from_currency, to_currency, &block) | ||
return cents if same_currency?(from_currency, to_currency) | ||
|
||
rate = get_rate(from_currency, to_currency) | ||
unless rate | ||
raise Money::Bank::UnknownRate, "No conversion rate known for '#{from_currency}' -> '#{to_currency}'" | ||
end | ||
_from_currency_ = Currency.wrap(from_currency) | ||
_to_currency_ = Currency.wrap(to_currency) | ||
|
||
_cents_ = cents / (_from_currency_.subunit_to_unit.to_f / _to_currency_.subunit_to_unit.to_f) | ||
|
||
ex = _cents_ * rate | ||
return block.call(ex) if block_given? | ||
return @rounding_method.call(ex) unless @rounding_method.nil? | ||
ex.to_s.to_i | ||
end | ||
|
||
private | ||
|
||
def rate_key_for(from, to) | ||
"#{Currency.wrap(from).iso_code}_TO_#{Currency.wrap(to).iso_code}".upcase | ||
end | ||
|
||
def set_rate(from, to, rate) | ||
@mutex.synchronize{ @rates[rate_key_for(from, to)] = rate } | ||
end | ||
|
||
def get_rate(from, to) | ||
@mutex.synchronize{ @rates[rate_key_for(from, to)] } | ||
end | ||
|
||
def same_currency?(currency1, currency2) | ||
Currency.wrap(currency1) == Currency.wrap(currency2) | ||
end | ||
end | ||
end |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) | ||
require 'money/currency' | ||
require 'money/bank' | ||
|
||
describe Money::Bank do | ||
describe '#new without block' do | ||
before :each do | ||
@bank = Money::Bank.new | ||
end | ||
|
||
describe '#rate_key_for' do | ||
it 'should accept str/str' do | ||
lambda{@bank.send(:rate_key_for, 'USD', 'EUR')}.should_not raise_exception | ||
end | ||
|
||
it 'should accept currency/str' do | ||
lambda{@bank.send(:rate_key_for, Money::Currency.wrap('USD'), 'EUR')}.should_not raise_exception | ||
end | ||
|
||
it 'should accept str/currency' do | ||
lambda{@bank.send(:rate_key_for, 'USD', Money::Currency.wrap('EUR'))}.should_not raise_exception | ||
end | ||
|
||
it 'should accept currency/currency' do | ||
lambda{@bank.send(:rate_key_for, Money::Currency.wrap('USD'), Money::Currency.wrap('EUR'))}.should_not raise_exception | ||
end | ||
|
||
it 'should return a hashkey based on the passed arguments' do | ||
@bank.send(:rate_key_for, 'USD', 'EUR').should == 'USD_TO_EUR' | ||
@bank.send(:rate_key_for, Money::Currency.wrap('USD'), 'EUR').should == 'USD_TO_EUR' | ||
@bank.send(:rate_key_for, 'USD', Money::Currency.wrap('EUR')).should == 'USD_TO_EUR' | ||
@bank.send(:rate_key_for, Money::Currency.wrap('USD'), Money::Currency.wrap('EUR')).should == 'USD_TO_EUR' | ||
end | ||
|
||
it 'should raise an UnknownCurrency exception when an unknown currency is passed' do | ||
lambda{@bank.send(:rate_key_for, 'AAA', 'BBB')}.should raise_exception(Money::Currency::UnknownCurrency) | ||
end | ||
end | ||
|
||
describe '#set_rate' do | ||
it 'should set a rate' do | ||
@bank.send(:set_rate, 'USD', 'EUR', 1.25) | ||
@bank.instance_variable_get(:@rates)['USD_TO_EUR'].should == 1.25 | ||
end | ||
|
||
it 'should raise an UnknownCurrency exception when an unknown currency is passed' do | ||
lambda{@bank.send(:set_rate, 'AAA', 'BBB', 1.25)}.should raise_exception(Money::Currency::UnknownCurrency) | ||
end | ||
end | ||
|
||
describe '#get_rate' do | ||
it 'should return a rate' do | ||
@bank.send(:set_rate, 'USD', 'EUR', 1.25) | ||
@bank.send(:get_rate, 'USD', 'EUR').should == 1.25 | ||
end | ||
|
||
it 'should raise an UnknownCurrency exception when an unknown currency is requested' do | ||
lambda{@bank.send(:get_rate, 'AAA', 'BBB')}.should raise_exception(Money::Currency::UnknownCurrency) | ||
end | ||
end | ||
|
||
describe '#same_currency?' do | ||
it 'should accept str/str' do | ||
lambda{@bank.send(:same_currency?, 'USD', 'EUR')}.should_not raise_exception | ||
end | ||
|
||
it 'should accept currency/str' do | ||
lambda{@bank.send(:same_currency?, Money::Currency.wrap('USD'), 'EUR')}.should_not raise_exception | ||
end | ||
|
||
it 'should accept str/currency' do | ||
lambda{@bank.send(:same_currency?, 'USD', Money::Currency.wrap('EUR'))}.should_not raise_exception | ||
end | ||
|
||
it 'should accept currency/currency' do | ||
lambda{@bank.send(:same_currency?, Money::Currency.wrap('USD'), Money::Currency.wrap('EUR'))}.should_not raise_exception | ||
end | ||
|
||
it 'should return `true` when currencies match' do | ||
@bank.send(:same_currency?, 'USD', 'USD').should == true | ||
@bank.send(:same_currency?, Money::Currency.wrap('USD'), 'USD').should == true | ||
@bank.send(:same_currency?, 'USD', Money::Currency.wrap('USD')).should == true | ||
@bank.send(:same_currency?, Money::Currency.wrap('USD'), Money::Currency.wrap('USD')).should == true | ||
end | ||
|
||
it 'should return `false` when currencies do not match' do | ||
@bank.send(:same_currency?, 'USD', 'EUR').should == false | ||
@bank.send(:same_currency?, Money::Currency.wrap('USD'), 'EUR').should == false | ||
@bank.send(:same_currency?, 'USD', Money::Currency.wrap('EUR')).should == false | ||
@bank.send(:same_currency?, Money::Currency.wrap('USD'), Money::Currency.wrap('EUR')).should == false | ||
end | ||
|
||
it 'should raise an UnknownCurrency exception when an unknown currency is passed' do | ||
lambda{@bank.send(:same_currency?, 'AAA', 'BBB')}.should raise_exception(Money::Currency::UnknownCurrency) | ||
end | ||
end | ||
|
||
describe '#exchange' do | ||
before :each do | ||
@bank.send(:set_rate, 'USD', 'EUR', 1.33) | ||
end | ||
|
||
it 'should accept str/str' do | ||
lambda{@bank.exchange(100, 'USD', 'EUR')}.should_not raise_exception | ||
end | ||
|
||
it 'should accept currency/str' do | ||
lambda{@bank.exchange(100, Money::Currency.wrap('USD'), 'EUR')}.should_not raise_exception | ||
end | ||
|
||
it 'should accept str/currency' do | ||
lambda{@bank.exchange(100, 'USD', Money::Currency.wrap('EUR'))}.should_not raise_exception | ||
end | ||
|
||
it 'should accept currency/currency' do | ||
lambda{@bank.exchange(100, Money::Currency.wrap('USD'), Money::Currency.wrap('EUR'))}.should_not raise_exception | ||
end | ||
|
||
it 'should exchange one currency to another' do | ||
@bank.exchange(100, 'USD', 'EUR').should == 133 | ||
end | ||
|
||
it 'should truncate extra digits' do | ||
@bank.exchange(10, 'USD', 'EUR').should == 13 | ||
end | ||
|
||
it 'should raise an UnknownCurrency exception when an unknown currency is requested' do | ||
lambda{@bank.exchange(100, 'AAA', 'BBB')}.should raise_exception(Money::Currency::UnknownCurrency) | ||
end | ||
|
||
it 'should raise an UnknownRate exception when an unknown rate is requested' do | ||
lambda{@bank.exchange(100, 'USD', 'JPY')}.should raise_exception(Money::Bank::UnknownRate) | ||
end | ||
|
||
it 'should accept a custom truncation method' do | ||
proc = Proc.new{|n| n.ceil} | ||
@bank.exchange(10, 'USD', 'EUR', &proc).should == 14 | ||
end | ||
end | ||
end | ||
|
||
describe '#new with &block' do | ||
before :each do | ||
proc = Proc.new{|n| n.ceil} | ||
@bank = Money::Bank.new(&proc) | ||
@bank.send(:set_rate, 'USD', 'EUR', 1.33) | ||
end | ||
|
||
describe '#exchange' do | ||
it 'should use a stored truncation method' do | ||
@bank.exchange(10, 'USD', 'EUR').should == 14 | ||
end | ||
|
||
it 'should use a custom truncation method over a stored one' do | ||
proc = Proc.new{|n| n.ceil+1} | ||
@bank.exchange(10, 'USD', 'EUR', &proc).should == 15 | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters