From c471834c5acc91247679b125ddbf3826d244b62a Mon Sep 17 00:00:00 2001 From: Shane Emmons Date: Tue, 31 Aug 2010 08:08:56 -0400 Subject: [PATCH] patch for #issue/21, add #export_rate and #import_rates --- lib/money/bank/variable_exchange.rb | 63 ++++++++++++++++++++-- spec/bank/variable_exchange_spec.rb | 81 +++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 4 deletions(-) diff --git a/lib/money/bank/variable_exchange.rb b/lib/money/bank/variable_exchange.rb index 7168b01cf6..141698e919 100644 --- a/lib/money/bank/variable_exchange.rb +++ b/lib/money/bank/variable_exchange.rb @@ -1,7 +1,12 @@ require 'money/bank/base' +require 'json' +require 'yaml' class Money module Bank + # Thrown by VariableExchange#export_rates and VariableExchange#import_rates + # when an unknown rate format is requested. + class UnknownRateFormat < StandardError; end # Class for aiding in exchanging money between different currencies. # By default, the Money class uses an object of this class (accessible through @@ -22,6 +27,8 @@ module Bank # class VariableExchange < Base + RATE_FORMATS = [:json, :ruby, :yaml] + def setup @rates = {} @mutex = Mutex.new @@ -71,12 +78,60 @@ def get_rate(from, to) @mutex.synchronize { @rates[rate_key_for(from, to)] } end + # Return the known rates as a string in the format specified. If +file+ + # is given will also write the string out to the file specified. + # Available formats are +:json+, +:ruby+ and +:yaml+. + # + # Raises +Money::Bank::UnknownRateFormat+ if format is unknown. + def export_rates(format, file=nil) + raise Money::Bank::UnknownRateFormat unless + RATE_FORMATS.include? format + + s = "" + @mutex.synchronize { + s = case format + when :json + @rates.to_json + when :ruby + Marshal.dump(@rates) + when :yaml + @rates.to_yaml + end + + unless file.nil? + File.open(file, "w").write(s) + end + } + s + end + + # Loads rates provided in +s+ given the specified format. Available + # formats are +:json+, +:ruby+ and +:yaml+. + # + # Raises +Money::Bank::UnknownRateFormat+ if format is unknown. + def import_rates(format, s) + raise Money::Bank::UnknownRateFormat unless + RATE_FORMATS.include? format + + @mutex.synchronize { + @rates = case format + when :json + JSON.load(s) + when :ruby + Marshal.load(s) + when :yaml + YAML.load(s) + end + } + self + end + private - # Return the rate hashkey for the given currencies. - def rate_key_for(from, to) - "#{Currency.wrap(from).iso_code}_TO_#{Currency.wrap(to).iso_code}".upcase - end + # Return the rate hashkey for the given currencies. + def rate_key_for(from, to) + "#{Currency.wrap(from).iso_code}_TO_#{Currency.wrap(to).iso_code}".upcase + end end diff --git a/spec/bank/variable_exchange_spec.rb b/spec/bank/variable_exchange_spec.rb index ec988c760a..03da97c855 100644 --- a/spec/bank/variable_exchange_spec.rb +++ b/spec/bank/variable_exchange_spec.rb @@ -1,4 +1,6 @@ require "spec_helper" +require "json" +require "yaml" describe Money::Bank::VariableExchange do @@ -165,6 +167,85 @@ end end + describe '#export_rates' do + before :each do + @bank.set_rate('USD', 'EUR', 1.25) + @bank.set_rate('USD', 'JPY', 2.55) + end + + describe 'with format == :json' do + it 'should return rates formatted as json' do + rates_as_json = '{"USD_TO_EUR":1.25,"USD_TO_JPY":2.55}' + @bank.export_rates(:json).should == rates_as_json + end + end + + describe 'with format == :ruby' do + it 'should return rates formatted as ruby objects' do + rates = {"USD_TO_EUR"=>1.25,"USD_TO_JPY"=>2.55} + @bank.export_rates(:ruby).should == Marshal.dump(rates) + end + end + + describe 'with format == :yaml' do + it 'should return rates formatted as yaml' do + rates_as_yaml = "--- \nUSD_TO_EUR: 1.25\nUSD_TO_JPY: 2.55\n" + @bank.export_rates(:yaml).should == rates_as_yaml + end + end + + describe 'with unknown format' do + it 'should raise `UnknownRateFormat`' do + lambda{@bank.export_rates(:foo)}.should raise_error Money::Bank::UnknownRateFormat + end + end + + describe 'with :file provided' do + it 'should write rates to file' do + f = mock('IO') + File.should_receive(:open).with('null', 'w').and_return(f) + f.should_receive(:write).with('{"USD_TO_EUR":1.25,"USD_TO_JPY":2.55}') + + @bank.export_rates(:json, 'null') + end + end + end + + describe '#import_rates' do + describe 'with format == :json' do + it 'should load the rates provided' do + s = '{"USD_TO_EUR":1.25,"USD_TO_JPY":2.55}' + @bank.import_rates(:json, s) + @bank.get_rate('USD', 'EUR').should == 1.25 + @bank.get_rate('USD', 'JPY').should == 2.55 + end + end + + describe 'with format == :ruby' do + it 'should load the rates provided' do + s = Marshal.dump({"USD_TO_EUR"=>1.25,"USD_TO_JPY"=>2.55}) + @bank.import_rates(:ruby, s) + @bank.get_rate('USD', 'EUR').should == 1.25 + @bank.get_rate('USD', 'JPY').should == 2.55 + end + end + + describe 'with format == :yaml' do + it 'should load the rates provided' do + s = "--- \nUSD_TO_EUR: 1.25\nUSD_TO_JPY: 2.55\n" + @bank.import_rates(:yaml, s) + @bank.get_rate('USD', 'EUR').should == 1.25 + @bank.get_rate('USD', 'JPY').should == 2.55 + end + end + + describe 'with unknown format' do + it 'should raise `UnknownRateFormat`' do + lambda{@bank.import_rates(:foo, "")}.should raise_error Money::Bank::UnknownRateFormat + end + end + end + describe '#rate_key_for' do it 'should accept str/str' do lambda{@bank.send(:rate_key_for, 'USD', 'EUR')}.should_not raise_exception