Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

version 1.0.0

  • Loading branch information...
commit 40b043ddd0f4aad7674678607c58253679eb5bb5 0 parents
@slbug authored
4 .gitignore
@@ -0,0 +1,4 @@
+*.gem
+.bundle
+Gemfile.lock
+pkg/*
3  Gemfile
@@ -0,0 +1,3 @@
+source "http://rubygems.org"
+
+gemspec
11 Rakefile
@@ -0,0 +1,11 @@
+require "bundler/gem_tasks"
+
+begin
+ require 'rspec/core/rake_task'
+ RSpec::Core::RakeTask.new
+rescue LoadError
+ task(:spec){abort "`gem install rspec` to run specs"}
+end
+
+task :default => :spec
+
55 lib/nbrb_currency.rb
@@ -0,0 +1,55 @@
+require 'rubygems'
+require 'open-uri'
+require 'nokogiri'
+require 'money'
+
+class InvalidCache < StandardError ; end
+
+class NbrbCurrency < Money::Bank::VariableExchange
+
+ NBRB_RATES_URL = 'http://nbrb.by/Services/XmlExRates.aspx'
+ CURRENCIES = %w(AUD BGN UAH DKK USD EUR PLN JPY IRR ISK CAD CNY KWD LVL LTL MDL NOK RUB SGD KGS KZT TRY GBP CZK SEK CHF)
+
+ def update_rates(cache=nil)
+ exchange_rates(cache).each do |exchange_rate|
+ rate = exchange_rate.xpath("Rate").text
+ currency = exchange_rate.xpath("CharCode").text
+ scale = exchange_rate.xpath("Scale").text
+ next if currency == "XDR"
+ add_rate(currency, "BYR", (BigDecimal.new(rate) / BigDecimal.new(scale)).to_f)
+ end
+ add_rate("BYR", "BYR", 1)
+ end
+
+ def save_rates(cache)
+ raise InvalidCache if !cache
+ File.open(cache, "w") do |file|
+ io = open(NBRB_RATES_URL) ;
+ io.each_line {|line| file.puts line}
+ end
+ end
+
+ def exchange(cents, from_currency, to_currency)
+ exchange_with(Money.new(cents, from_currency), to_currency)
+ end
+
+ def exchange_with(from, to_currency)
+ rate = get_rate(from.currency, to_currency)
+ unless rate
+ from_base_rate = get_rate(from.currency, "BYR")
+ to_base_rate = get_rate(to_currency, "BYR")
+ rate = (BigDecimal.new(from_base_rate, 8) / BigDecimal.new(to_base_rate, 8)).to_f
+ raise "Rate #{from.currency} - #{to_currency} unknown!" unless rate
+ end
+ Money.new(((Money::Currency.wrap(to_currency).subunit_to_unit.to_f / from.currency.subunit_to_unit.to_f) * from.cents * rate).round, to_currency)
+ end
+
+ protected
+
+ def exchange_rates(cache=nil)
+ rates_source = !!cache ? cache : NBRB_RATES_URL
+ doc = Nokogiri::XML(open(rates_source))
+ doc.xpath('DailyExRates//Currency')
+ end
+
+end
27 nbrb_currency.gemspec
@@ -0,0 +1,27 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+
+Gem::Specification.new do |s|
+ s.name = "nbrb_currency"
+ s.version = "1.0.0"
+ s.platform = Gem::Platform::RUBY
+ s.authors = ["Aleks Grebennik"]
+ s.email = ["aleks.grebennik@gmail.com"]
+ s.homepage = "http://github.com/slbug/nbrb_currency"
+ s.summary = %q{Calculates exchange rates based on rates from National Bank of the Republic of Belarus. Money gem compatible.}
+ s.description = %q{This gem reads exchange rates from the National Bank of the Republic of Belarus website. It uses it to calculates exchange rates. It is compatible with the money gem}
+
+ s.required_rubygems_version = ">= 1.3.6"
+
+ s.add_dependency "nokogiri"
+ s.add_dependency "money", "~> 3.7.1"
+
+ s.add_development_dependency "rspec", ">= 2.0.0"
+ s.add_development_dependency "rr"
+ s.add_development_dependency "shoulda"
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ s.require_paths = ["lib"]
+end
192 spec/exchange_rates.xml
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="windows-1251"?>
+<DailyExRates Date="10/26/2011">
+ <Currency Id="170">
+ <NumCode>036</NumCode>
+ <CharCode>AUD</CharCode>
+ <Scale>1</Scale>
+ <Name>Ŕâńňđŕëčéńęčé äîëëŕđ</Name>
+ <Rate>8920.87</Rate>
+ </Currency>
+ <Currency Id="191">
+ <NumCode>975</NumCode>
+ <CharCode>BGN</CharCode>
+ <Scale>1</Scale>
+ <Name>Áîëăŕđcęčé ëĺâ</Name>
+ <Rate>6059.74</Rate>
+ </Currency>
+ <Currency Id="224">
+ <NumCode>980</NumCode>
+ <CharCode>UAH</CharCode>
+ <Scale>1</Scale>
+ <Name>Ăđčâíŕ</Name>
+ <Rate>1064.87</Rate>
+ </Currency>
+ <Currency Id="36">
+ <NumCode>208</NumCode>
+ <CharCode>DKK</CharCode>
+ <Scale>1</Scale>
+ <Name>Äŕňńęŕ˙ ęđîíŕ</Name>
+ <Rate>1592.06</Rate>
+ </Currency>
+ <Currency Id="145">
+ <NumCode>840</NumCode>
+ <CharCode>USD</CharCode>
+ <Scale>1</Scale>
+ <Name>Äîëëŕđ ŃŘŔ</Name>
+ <Rate>8520</Rate>
+ </Currency>
+ <Currency Id="19">
+ <NumCode>978</NumCode>
+ <CharCode>EUR</CharCode>
+ <Scale>1</Scale>
+ <Name>Ĺâđî</Name>
+ <Rate>11860</Rate>
+ </Currency>
+ <Currency Id="166">
+ <NumCode>960</NumCode>
+ <CharCode>XDR</CharCode>
+ <Scale>1</Scale>
+ <Name>Ĺäčíčöŕ ŃĎÇ (SDR) îň ĚÂÔ</Name>
+ <Rate>13467.99</Rate>
+ </Currency>
+ <Currency Id="219">
+ <NumCode>985</NumCode>
+ <CharCode>PLN</CharCode>
+ <Scale>1</Scale>
+ <Name>Çëîňűé</Name>
+ <Rate>2714.33</Rate>
+ </Currency>
+ <Currency Id="67">
+ <NumCode>392</NumCode>
+ <CharCode>JPY</CharCode>
+ <Scale>10</Scale>
+ <Name>Čĺíŕ</Name>
+ <Rate>1119.43</Rate>
+ </Currency>
+ <Currency Id="264">
+ <NumCode>364</NumCode>
+ <CharCode>IRR</CharCode>
+ <Scale>1000</Scale>
+ <Name>Čđŕíńęčé đčŕë</Name>
+ <Rate>794.63</Rate>
+ </Currency>
+ <Currency Id="58">
+ <NumCode>352</NumCode>
+ <CharCode>ISK</CharCode>
+ <Scale>1</Scale>
+ <Name>Čńëŕíäńęŕ˙ ęđîíŕ</Name>
+ <Rate>74.54</Rate>
+ </Currency>
+ <Currency Id="23">
+ <NumCode>124</NumCode>
+ <CharCode>CAD</CharCode>
+ <Scale>1</Scale>
+ <Name>Ęŕíŕäńęčé äîëëŕđ</Name>
+ <Rate>8494.94</Rate>
+ </Currency>
+ <Currency Id="254">
+ <NumCode>156</NumCode>
+ <CharCode>CNY</CharCode>
+ <Scale>1</Scale>
+ <Name>Ęčňŕéńęčé ţŕíü</Name>
+ <Rate>1339.46</Rate>
+ </Currency>
+ <Currency Id="72">
+ <NumCode>414</NumCode>
+ <CharCode>KWD</CharCode>
+ <Scale>1</Scale>
+ <Name>Ęóâĺéňńęčé äčíŕđ</Name>
+ <Rate>30996.47</Rate>
+ </Currency>
+ <Currency Id="176">
+ <NumCode>428</NumCode>
+ <CharCode>LVL</CharCode>
+ <Scale>1</Scale>
+ <Name>Ëŕňâčéńęčé ëŕň</Name>
+ <Rate>16836.28</Rate>
+ </Currency>
+ <Currency Id="177">
+ <NumCode>440</NumCode>
+ <CharCode>LTL</CharCode>
+ <Scale>1</Scale>
+ <Name>Ëčňîâńęčé ëčň</Name>
+ <Rate>3432.51</Rate>
+ </Currency>
+ <Currency Id="178">
+ <NumCode>498</NumCode>
+ <CharCode>MDL</CharCode>
+ <Scale>1</Scale>
+ <Name>Ěîëäŕâńęčé ëĺé</Name>
+ <Rate>730.39</Rate>
+ </Currency>
+ <Currency Id="101">
+ <NumCode>578</NumCode>
+ <CharCode>NOK</CharCode>
+ <Scale>1</Scale>
+ <Name>Íîđâĺćńęŕ˙ ęđîíŕ</Name>
+ <Rate>1544.71</Rate>
+ </Currency>
+ <Currency Id="190">
+ <NumCode>643</NumCode>
+ <CharCode>RUB</CharCode>
+ <Scale>1</Scale>
+ <Name>Đîńńčéńęčé đóáëü</Name>
+ <Rate>279</Rate>
+ </Currency>
+ <Currency Id="119">
+ <NumCode>702</NumCode>
+ <CharCode>SGD</CharCode>
+ <Scale>1</Scale>
+ <Name>Ńčíăŕďóđcęčé äîëëŕđ</Name>
+ <Rate>6743.71</Rate>
+ </Currency>
+ <Currency Id="223">
+ <NumCode>417</NumCode>
+ <CharCode>KGS</CharCode>
+ <Scale>1</Scale>
+ <Name>Ńîě</Name>
+ <Rate>187.97</Rate>
+ </Currency>
+ <Currency Id="222">
+ <NumCode>398</NumCode>
+ <CharCode>KZT</CharCode>
+ <Scale>1</Scale>
+ <Name>Ňĺíăĺ</Name>
+ <Rate>57.58</Rate>
+ </Currency>
+ <Currency Id="256">
+ <NumCode>949</NumCode>
+ <CharCode>TRY</CharCode>
+ <Scale>1</Scale>
+ <Name>Ňóđĺöęŕ˙ ëčđŕ</Name>
+ <Rate>4714.74</Rate>
+ </Currency>
+ <Currency Id="143">
+ <NumCode>826</NumCode>
+ <CharCode>GBP</CharCode>
+ <Scale>1</Scale>
+ <Name>Ôóíň ńňĺđëčíăîâ</Name>
+ <Rate>13618.37</Rate>
+ </Currency>
+ <Currency Id="171">
+ <NumCode>203</NumCode>
+ <CharCode>CZK</CharCode>
+ <Scale>1</Scale>
+ <Name>×ĺřńęŕ˙ ęđîíŕ</Name>
+ <Rate>475.9</Rate>
+ </Currency>
+ <Currency Id="129">
+ <NumCode>752</NumCode>
+ <CharCode>SEK</CharCode>
+ <Scale>1</Scale>
+ <Name>Řâĺäńęŕ˙ ęđîíŕ</Name>
+ <Rate>1300.6</Rate>
+ </Currency>
+ <Currency Id="130">
+ <NumCode>756</NumCode>
+ <CharCode>CHF</CharCode>
+ <Scale>1</Scale>
+ <Name>Řâĺéöŕđńęčé ôđŕíę</Name>
+ <Rate>9684.57</Rate>
+ </Currency>
+</DailyExRates>
28 spec/exchange_rates.yml
@@ -0,0 +1,28 @@
+currencies:
+ AUD: 8920.87
+ BGN: 6059.74
+ UAH: 1064.87
+ DKK: 1592.06
+ USD: 8520
+ EUR: 11860
+ PLN: 2714.33
+ JPY: 111.943
+ IRR: 0.79463
+ ISK: 74.54
+ CAD: 8494.94
+ CNY: 1339.46
+ KWD: 30996.47
+ LVL: 16836.28
+ LTL: 3432.51
+ MDL: 730.39
+ NOK: 1544.71
+ RUB: 279
+ SGD: 6743.71
+ KGS: 187.97
+ KZT: 57.58
+ TRY: 4714.74
+ GBP: 13618.37
+ CZK: 475.9
+ SEK: 1300.6
+ CHF: 9684.57
+
79 spec/nbrb_currency_spec.rb
@@ -0,0 +1,79 @@
+require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
+require 'yaml'
+
+describe "NbrbCurrency" do
+ before(:each) do
+ @bank = NbrbCurrency.new
+ @cache_path = File.expand_path(File.dirname(__FILE__) + '/exchange_rates.xml')
+ @yml_cache_path = File.expand_path(File.dirname(__FILE__) + '/exchange_rates.yml')
+ @tmp_cache_path = File.expand_path(File.dirname(__FILE__) + '/tmp/exchange_rates.xml')
+ @exchange_rates = YAML.load_file(@yml_cache_path)
+ end
+
+ after(:each) do
+ if File.exists? @tmp_cache_path
+ File.delete @tmp_cache_path
+ end
+ end
+
+ it "should save the xml file from nbrb given a file path" do
+ @bank.save_rates(@tmp_cache_path)
+ File.exists?(@tmp_cache_path).should == true
+ end
+
+ it "should raise an error if an invalid path is given to save_rates" do
+ lambda { @bank.save_rates(nil) }.should raise_exception
+ end
+
+ it "should update itself with exchange rates from nbrb website" do
+ stub(OpenURI::OpenRead).open(NbrbCurrency::NBRB_RATES_URL) {@cache_path}
+ @bank.update_rates
+ NbrbCurrency::CURRENCIES.each do |currency|
+ @bank.get_rate(currency, "BYR").should > 0
+ end
+ end
+
+ it "should update itself with exchange rates from cache" do
+ @bank.update_rates(@cache_path)
+ NbrbCurrency::CURRENCIES.each do |currency|
+ @bank.get_rate(currency, "BYR").should > 0
+ end
+ end
+
+ it "should return the correct exchange rates using exchange" do
+ @bank.update_rates(@cache_path)
+ NbrbCurrency::CURRENCIES.reject{|c| %w{JPY KWD}.include?(c) }.each do |currency|
+ @bank.exchange(100, currency, "BYR").cents.should == (@exchange_rates["currencies"][currency].to_f * 100).round
+ end
+ subunit = Money::Currency.wrap("KWD").subunit_to_unit.to_f
+ @bank.exchange(1000, "KWD", "BYR").cents.should == ((subunit / 1000) * @exchange_rates["currencies"]['KWD'].to_f * 100).round
+ subunit = Money::Currency.wrap("JPY").subunit_to_unit.to_f
+ @bank.exchange(100, "JPY", "BYR").cents.should == ((subunit / 100) * @exchange_rates["currencies"]['JPY'].to_f * 100).round
+ end
+
+ it "should return the correct exchange rates using exchange_with" do
+ @bank.update_rates(@cache_path)
+ NbrbCurrency::CURRENCIES.reject{|c| %w{JPY KWD}.include?(c) }.each do |currency|
+ @bank.exchange_with(Money.new(100, currency), "BYR").cents.should == (@exchange_rates["currencies"][currency].to_f * 100).round
+ @bank.exchange_with(1.to_money(currency), "BYR").cents.should == (@exchange_rates["currencies"][currency].to_f * 100).round
+ end
+ @bank.exchange_with(5000.to_money('JPY'), 'BYR').cents.should == 55971500 # 559715 BYR
+ end
+
+ # in response to #4
+ it "should exchange btc" do
+ Money::Currency::TABLE[:btc] = {
+ :priority => 1,
+ :iso_code => "BTC",
+ :name => "Bitcoin",
+ :symbol => "BTC",
+ :subunit => "Cent",
+ :subunit_to_unit => 1000,
+ :separator => ".",
+ :delimiter => ","
+ }
+ @bank.add_rate("USD", "BTC", 1 / 13.7603)
+ @bank.add_rate("BTC", "USD", 13.7603)
+ @bank.exchange(100, "BTC", "USD").cents.should == 138
+ end
+end
7 spec/spec_helper.rb
@@ -0,0 +1,7 @@
+require 'nbrb_currency'
+require 'shoulda'
+require 'rr'
+
+RSpec.configure do |config|
+ config.mock_with :rr
+end
0  spec/tmp/.gitkeep
No changes.
Please sign in to comment.
Something went wrong with that request. Please try again.