russian_central_bank 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cd2cd1ed41bb857dc38b988a175a4b21601c21c8
4
- data.tar.gz: 0827a6b1c1a905acbf96bb714c442f42578d1636
3
+ metadata.gz: 7e8cf3185a50f7bb0d936780cba98e25c4237ac7
4
+ data.tar.gz: add5761d3e996d857e02717df3dce9c2cfeb61b5
5
5
  SHA512:
6
- metadata.gz: f53cb4d06fc04ca5cb83e6fdde9368239096c49880840ea5704d969e91a0f8a28632ffc646b725479b27934e3f530eab485839f6a4516a1ac2adbcb7f355c8be
7
- data.tar.gz: a327bcf332e28e344f47d365cd011ad1c21cf77dc790b89efd5da3face2425c5c472bef32432efcc3945a92028afa09f10fdca7de38e26e5fc280c7fffca3135
6
+ metadata.gz: 7c60395ac140229da981f938d74bc8e55897a36da2aa8034491990a6197f6a74d7e2b7910359f68a218c2fdc9309f4cf66bf980bad08c2796b08836522470e24
7
+ data.tar.gz: eb487d9d5a23cc45df699e90f49abba331e7e2ed166cff25c0a19a69e262ade0289cf0cbaa9c34664d6252610208827338850149925d33263e3c779c4dc782f2
@@ -0,0 +1,44 @@
1
+ version: 2
2
+ jobs:
3
+ build:
4
+ docker:
5
+ - image: circleci/ruby:2.4.2
6
+ environment:
7
+ BUNDLE_JOBS: 3
8
+ BUNDLE_RETRY: 3
9
+ BUNDLE_PATH: vendor/bundle
10
+ steps:
11
+ - checkout
12
+
13
+ # Which version of bundler?
14
+ - run:
15
+ name: Which bundler?
16
+ command: bundle -v
17
+
18
+ # Restore bundle cache
19
+ - restore_cache:
20
+ keys:
21
+ - russian_central_bank-{{ checksum "russian_central_bank.gemspec" }}
22
+ - russian_central_bank
23
+
24
+ - run:
25
+ name: Bundle Install
26
+ command: bundle check || bundle install
27
+
28
+ # Store bundle cache
29
+ - save_cache:
30
+ key: russian_central_bank-{{ checksum "russian_central_bank.gemspec" }}
31
+ paths:
32
+ - vendor/bundle
33
+
34
+ # Run rspec
35
+ - run:
36
+ name: Run rspec
37
+ command: |
38
+ gem install rspec && \
39
+ bundle exec rspec --format progress \
40
+ $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)
41
+
42
+ # Save test results for timing analysis
43
+ - store_test_results:
44
+ path: test_results
data/.gitignore CHANGED
@@ -4,13 +4,8 @@
4
4
  .config
5
5
  .yardoc
6
6
  Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
7
  coverage
10
8
  doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
9
  spec/reports
15
10
  test/tmp
16
11
  test/version_tmp
data/.rubocop.yml ADDED
@@ -0,0 +1,8 @@
1
+ Metrics/BlockLength:
2
+ Enabled: true
3
+ Exclude:
4
+ - spec/**/*
5
+
6
+ Metrics/LineLength:
7
+ Enabled: true
8
+ Max: 120
data/README.md CHANGED
@@ -19,19 +19,20 @@ Or install it yourself as:
19
19
 
20
20
  $ gem install russian_central_bank
21
21
 
22
- NOTE: use 0.x version of `russian_central_bank` for > 6.0 `money` versions
22
+ NOTE: use 0.x version of `russian_central_bank` for `money` versions < 6.0
23
23
 
24
- ##Dependencies
24
+ ## Dependencies
25
25
 
26
- * [savon](http://savonrb.com/)
26
+ * [httparty](https://github.com/jnunemaker/httparty)
27
27
  * [money](https://github.com/RubyMoney/money)
28
28
 
29
29
  ## Usage
30
30
 
31
- Regular usage
31
+ ### Regular usage
32
32
 
33
33
  require 'russian_central_bank'
34
34
 
35
+ Money.locale_backend = :currency
35
36
  bank = Money::Bank::RussianCentralBank.new
36
37
 
37
38
  Money.default_bank = bank
@@ -39,14 +40,17 @@ Regular usage
39
40
  # Load today's rates
40
41
  bank.update_rates
41
42
 
42
- # Exchange 100 USD to RUB
43
- Money.new(1000, "USD").exchange_to('RUB')
43
+ # Exchange 1000 USD to RUB
44
+ Money.new(1000_00, "USD").exchange_to('RUB').format # => 64.592,50 ₽
44
45
 
45
- Specific date rates
46
+ # Use indirect exchange rates, USD -> RUB -> EUR
47
+ Money.new(1000_00, "USD").exchange_to('EUR').format # => €888,26
48
+
49
+ ### Specific date rates
46
50
 
47
51
  # Specify rates date
48
- bank.update_rates(Date.today - 3000)
49
- Money.new(1000, "USD").exchange_to('RUB') # makes you feel better
52
+ bank.update_rates(Date.new(2010, 12, 31))
53
+ Money.new(1000_00, "USD").exchange_to('RUB').format # => 30.476,90
50
54
 
51
55
  # Check last rates update
52
56
  bank.rates_updated_at
@@ -54,7 +58,7 @@ Specific date rates
54
58
  # Check on which date rates were updated
55
59
  bank.rates_updated_on
56
60
 
57
- Autoupdate
61
+ ### Autoupdate
58
62
 
59
63
  # Use ttl attribute to enable rates autoupdate
60
64
  bank.ttl = 1.day
@@ -62,6 +66,26 @@ Autoupdate
62
66
  # Check expiration date
63
67
  bank.rates_expired_at
64
68
 
69
+ ### Safe rates fetch
70
+
71
+ There are some cases, when the `cbr.ru` doesn't return HTTP 200.
72
+ To avoid issues in production, you use fallback:
73
+
74
+ ```ruby
75
+ bank = Money::Bank::RussianCentralBank.new
76
+ begin
77
+ bank.update_rates
78
+ rescue Money::Bank::RussianCentralBankFetcher::FetchError => e
79
+ Rails.logger.info "CBR failed: #{e.response}"
80
+
81
+ ## fallback
82
+ Money.default_bank = Money::Bank::VariableExchange.new
83
+
84
+ Money.default_bank.add_rate(:usd, :eur, 1.3)
85
+ Money.default_bank.add_rate(:eur, :usd, 0.7)
86
+ end
87
+ ```
88
+
65
89
  ## Contributing
66
90
 
67
91
  1. Fork it
@@ -0,0 +1,85 @@
1
+ require 'money'
2
+
3
+ class Money
4
+ module Bank
5
+ class RussianCentralBank < Money::Bank::VariableExchange
6
+ attr_reader :rates_updated_at, :rates_updated_on, :rates_expired_at, :ttl
7
+
8
+ def flush_rates
9
+ @store = Money::RatesStore::Memory.new
10
+ end
11
+
12
+ def update_rates(date = Date.today)
13
+ store.transaction do
14
+ update_parsed_rates(exchange_rates(date))
15
+ @rates_updated_at = Time.now
16
+ @rates_updated_on = date
17
+ update_expired_at
18
+ store.send(:index)
19
+ end
20
+ end
21
+
22
+ def add_rate(from, to, rate)
23
+ super(from, to, rate)
24
+ super(to, from, 1.0 / rate)
25
+ end
26
+
27
+ def get_rate(from, to)
28
+ update_rates if rates_expired?
29
+ super || indirect_rate(from, to)
30
+ end
31
+
32
+ def ttl=(value)
33
+ @ttl = value
34
+ update_expired_at
35
+ @ttl
36
+ end
37
+
38
+ def rates_expired?
39
+ rates_expired_at && rates_expired_at <= Time.now
40
+ end
41
+
42
+ private
43
+
44
+ def fetcher
45
+ @fetcher ||= RussianCentralBankFetcher.new
46
+ end
47
+
48
+ def exchange_rates(date)
49
+ fetcher.perform(date)
50
+ end
51
+
52
+ def update_expired_at
53
+ @rates_expired_at = if ttl
54
+ @rates_updated_at ? @rates_updated_at + ttl : Time.now
55
+ else
56
+ nil
57
+ end
58
+ end
59
+
60
+ def indirect_rate(from, to)
61
+ get_rate('RUB', to) / get_rate('RUB', from)
62
+ end
63
+
64
+ def local_currencies
65
+ @local_currencies ||= Money::Currency.table.map { |currency| currency.last[:iso_code] }
66
+ end
67
+
68
+ def update_parsed_rates(rates)
69
+ add_rate('RUB', 'RUB', 1)
70
+ rates.each do |rate|
71
+ begin
72
+ if local_currencies.include?(rate[:code])
73
+ add_rate(
74
+ 'RUB',
75
+ rate[:code],
76
+ 1 / (rate[:value] / rate[:nominal])
77
+ )
78
+ end
79
+ rescue Money::Currency::UnknownCurrency
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,49 @@
1
+ require 'httparty'
2
+
3
+ class Money
4
+ module Bank
5
+ class RussianCentralBankFetcher
6
+ DAILY_RATES_URL = 'http://www.cbr.ru/scripts/XML_daily.asp'.freeze
7
+
8
+ class FetchError < StandardError
9
+ attr_reader :response
10
+
11
+ def initialize(message, response = nil)
12
+ super(message)
13
+ @response = response
14
+ end
15
+ end
16
+
17
+ def perform(date = Date.today)
18
+ response = HTTParty.get(rates_url(date))
19
+ unless response.success?
20
+ raise_fetch_error("cbr.ru respond with #{response.code}", response)
21
+ end
22
+ extract_rates(response.parsed_response)
23
+ rescue HTTParty::Error => e
24
+ raise_fetch_error(e.message)
25
+ end
26
+
27
+ private
28
+
29
+ def raise_fetch_error(message, response = nil)
30
+ raise FetchError.new(message, response)
31
+ end
32
+
33
+ def rates_url(date)
34
+ "#{DAILY_RATES_URL}?date_req=#{date.strftime('%d/%m/%Y')}"
35
+ end
36
+
37
+ def extract_rates(parsed_response)
38
+ rates_arr = parsed_response['ValCurs']['Valute']
39
+ rates_arr.map do |rate|
40
+ {
41
+ code: rate['CharCode'],
42
+ nominal: rate['Nominal'].to_i,
43
+ value: rate['Value'].tr(',', '.').to_f
44
+ }
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -1,83 +1,2 @@
1
- require 'money'
2
- require 'savon'
3
-
4
- class Money
5
- module Bank
6
- class RussianCentralBank < Money::Bank::VariableExchange
7
-
8
- CBR_SERVICE_URL = 'http://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx?WSDL'
9
-
10
- attr_reader :rates_updated_at, :rates_updated_on, :ttl, :rates_expired_at
11
-
12
- def flush_rates
13
- @store = Money::RatesStore::Memory.new
14
- end
15
-
16
- def update_rates(date = Date.today)
17
- store.transaction do
18
- update_parsed_rates(exchange_rates(date))
19
- @rates_updated_at = Time.now
20
- @rates_updated_on = date
21
- update_expired_at
22
- store.send(:index)
23
- end
24
- end
25
-
26
- def add_rate(from, to, rate)
27
- super(from, to, rate)
28
- super(to, from, 1.0 / rate)
29
- end
30
-
31
- def get_rate(from, to)
32
- update_rates if rates_expired?
33
- super || indirect_rate(from, to)
34
- end
35
-
36
- def ttl=(value)
37
- @ttl = value
38
- update_expired_at
39
- @ttl
40
- end
41
-
42
- def rates_expired?
43
- rates_expired_at && rates_expired_at <= Time.now
44
- end
45
-
46
- private
47
-
48
- def update_expired_at
49
- @rates_expired_at = if ttl
50
- @rates_updated_at ? @rates_updated_at + ttl : Time.now
51
- else
52
- nil
53
- end
54
- end
55
-
56
- def indirect_rate(from, to)
57
- from_base_rate = get_rate('RUB', from)
58
- to_base_rate = get_rate('RUB', to)
59
- to_base_rate / from_base_rate
60
- end
61
-
62
- def exchange_rates(date = Date.today)
63
- client = Savon::Client.new wsdl: CBR_SERVICE_URL, log: false, log_level: :error,
64
- follow_redirects: true
65
- response = client.call(:get_curs_on_date, message: { 'On_date' => date.strftime('%Y-%m-%dT%H:%M:%S') })
66
- response.body[:get_curs_on_date_response][:get_curs_on_date_result][:diffgram][:valute_data][:valute_curs_on_date]
67
- end
68
-
69
- def update_parsed_rates(rates)
70
- local_currencies = Money::Currency.table.map { |currency| currency.last[:iso_code] }
71
- add_rate('RUB', 'RUB', 1)
72
- rates.each do |rate|
73
- begin
74
- if local_currencies.include? rate[:vch_code]
75
- add_rate('RUB', rate[:vch_code], 1/ (rate[:vcurs].to_f / rate[:vnom].to_i))
76
- end
77
- rescue Money::Currency::UnknownCurrency
78
- end
79
- end
80
- end
81
- end
82
- end
83
- end
1
+ require 'money/bank/russian_central_bank_fetcher'
2
+ require 'money/bank/russian_central_bank'
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'russian_central_bank'
7
- spec.version = '1.0.1'
7
+ spec.version = '1.1.0'
8
8
  spec.authors = ['Ramil Mustafin']
9
9
  spec.email = ['rommel.rmm@gmail.com']
10
10
  spec.description = 'RussianCentralBank extends Money::Bank::VariableExchange and gives you access to the Central Bank of Russia currency exchange rates.'
@@ -17,10 +17,13 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ['lib']
19
19
 
20
- spec.add_development_dependency 'bundler', '~>1.3'
20
+ spec.add_development_dependency 'bundler'
21
+ spec.add_development_dependency 'pry'
21
22
  spec.add_development_dependency 'rake'
22
23
  spec.add_development_dependency 'rspec', '~>3'
24
+ spec.add_development_dependency 'rubocop'
25
+ spec.add_development_dependency 'webmock', '~>3'
23
26
 
24
- spec.add_dependency 'money', '~> 6.7.0'
25
- spec.add_dependency 'savon', '~>2.0'
27
+ spec.add_dependency 'httparty', '~>0'
28
+ spec.add_dependency 'money', '~> 6'
26
29
  end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Update Rates' do
4
+ let(:bank) { Money::Bank::RussianCentralBank.new }
5
+ let(:rates_xml) { File.read('spec/support/rcb_response.xml') }
6
+ let(:date) { Date.today }
7
+ let(:rates_url) do
8
+ "#{Money::Bank::RussianCentralBankFetcher::DAILY_RATES_URL}?date_req=#{date.strftime('%d/%m/%Y')}"
9
+ end
10
+
11
+ context 'when cbr.ru successfully returns rates' do
12
+ before do
13
+ stub_request(:get, rates_url).to_return(
14
+ status: 200,
15
+ body: rates_xml,
16
+ headers: {
17
+ "content-type"=>["application/xml; charset=windows-1251"]
18
+ }
19
+ )
20
+
21
+ bank.update_rates
22
+ end
23
+
24
+ it 'adds rates', :aggregete_failure do
25
+ expect(bank.get_rate('USD', 'RUB')).to eq(64.4993)
26
+ expect(bank.get_rate('RUB', 'USD')).to eq(0.015504044229937378)
27
+ end
28
+
29
+ it 'adds rates for currencies with nominal > 1', :aggregete_failure do
30
+ expect(bank.get_rate('DKK', 'RUB')).to eq(9.76581)
31
+ expect(bank.get_rate('RUB', 'DKK')).to eq(0.10239806017114812)
32
+ end
33
+ end
34
+
35
+ context 'when cbr.ru request fails' do
36
+ before do
37
+ stub_request(:get, rates_url).to_return(
38
+ status: 503,
39
+ body: '503'
40
+ )
41
+ end
42
+
43
+ it 'raises FetchError' do
44
+ expect { bank.update_rates }.to raise_error(Money::Bank::RussianCentralBankFetcher::FetchError)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe Money::Bank::RussianCentralBankFetcher do
4
+ describe 'perform' do
5
+ subject(:perform) { fetcher.perform(date) }
6
+
7
+ let(:date) { Date.today }
8
+ let(:fetcher) { described_class.new }
9
+ let(:rates_url) do
10
+ "http://www.cbr.ru/scripts/XML_daily.asp?date_req=#{date.strftime('%d/%m/%Y')}"
11
+ end
12
+
13
+ context 'when RCB responds' do
14
+ let(:rcb_response) do
15
+ instance_double(
16
+ 'response',
17
+ parsed_response: parsed_response,
18
+ success?: response_is_success,
19
+ code: response_code
20
+ )
21
+ end
22
+ let(:parsed_response) do
23
+ {
24
+ 'ValCurs' => {
25
+ 'Valute' => [
26
+ {
27
+ 'CharCode' => 'XXX',
28
+ 'Nominal' => '1',
29
+ 'Value' => '100,1'
30
+ }
31
+ ]
32
+ }
33
+ }
34
+ end
35
+
36
+ before do
37
+ allow(HTTParty).to receive(:get).with(rates_url).and_return(rcb_response)
38
+ end
39
+
40
+ context 'and respond is successfull' do
41
+ let(:response_is_success) { true }
42
+ let(:response_code) { 200 }
43
+
44
+ it 'returns a normalized array of rates' do
45
+ expect(perform).to eq(
46
+ [
47
+ {
48
+ code: 'XXX', nominal: 1, value: 100.1
49
+ }
50
+ ]
51
+ )
52
+ end
53
+ end
54
+
55
+ context 'and repsond is not successfull' do
56
+ let(:response_is_success) { false }
57
+ let(:response_code) { 503 }
58
+
59
+ it 'raises FetchError' do
60
+ expect { perform }.to raise_error(Money::Bank::RussianCentralBankFetcher::FetchError)
61
+ end
62
+ end
63
+ end
64
+
65
+ context 'when RCB fails to respond' do
66
+ let(:error_message) { 'RCB failed to response' }
67
+
68
+ before do
69
+ allow(HTTParty).to receive(:get).with(rates_url).and_raise(HTTParty::Error, error_message)
70
+ end
71
+
72
+ it 'rescues an exception and raises FetchError' do
73
+ expect { perform }.to raise_error(Money::Bank::RussianCentralBankFetcher::FetchError, error_message)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,112 @@
1
+ require 'spec_helper'
2
+
3
+ describe Money::Bank::RussianCentralBank do
4
+ subject(:bank) { described_class.new }
5
+
6
+ describe '#update_rates' do
7
+ let(:fetcher) { instance_double(Money::Bank::RussianCentralBankFetcher, perform: rates) }
8
+ let(:rates) do
9
+ [
10
+ { code: 'USD', value: 32.4288, nominal: 1 },
11
+ { code: 'EUR', value: 42.5920, nominal: 1 },
12
+ { code: 'JPY', value: 32.4029, nominal: 100 }
13
+ ]
14
+ end
15
+
16
+ before do
17
+ allow(Money::Bank::RussianCentralBankFetcher).to receive(:new).and_return(fetcher)
18
+
19
+ bank.update_rates
20
+ end
21
+
22
+ it 'should update rates from daily rates service' do
23
+ expect(bank.rates['RUB_TO_USD']).to eq(0.03083678705348332)
24
+ expect(bank.rates['RUB_TO_EUR']).to eq(0.023478587528174305)
25
+ expect(bank.rates['RUB_TO_JPY']).to eq(3.086143524190736)
26
+ end
27
+ end
28
+
29
+ describe '#flush_rates' do
30
+ before do
31
+ bank.add_rate('RUB', 'USD', 0.03)
32
+ end
33
+
34
+ it 'should delete all rates' do
35
+ bank.get_rate('RUB', 'USD')
36
+ bank.flush_rates
37
+ expect(bank.store.send(:index)).to be_empty
38
+ end
39
+ end
40
+
41
+ describe '#get_rate' do
42
+ context 'when getting direct rates' do
43
+ before do
44
+ bank.flush_rates
45
+ bank.add_rate('RUB', 'USD', 0.03)
46
+ bank.add_rate('RUB', 'GBP', 0.02)
47
+ end
48
+
49
+ it 'should get rate from @rates' do
50
+ expect(bank.get_rate('RUB', 'USD')).to eq(0.03)
51
+ end
52
+
53
+ it 'should calculate indirect rates' do
54
+ expect(bank.get_rate('USD', 'GBP')).to eq(0.6666666666666667)
55
+ end
56
+ end
57
+
58
+ context 'when getting indirect rate' do
59
+ let(:indirect_rate) { 4 }
60
+
61
+ before do
62
+ bank.flush_rates
63
+ bank.add_rate('RUB', 'USD', 123)
64
+ bank.add_rate('USD', 'RUB', indirect_rate)
65
+ end
66
+
67
+ it 'gets indirect rate from the last set' do
68
+ expect(bank.get_rate('RUB', 'USD')).to eq(1.0 / indirect_rate)
69
+ end
70
+ end
71
+
72
+ context 'when ttl is not set' do
73
+ before do
74
+ bank.add_rate('RUB', 'USD', 123)
75
+ bank.ttl = nil
76
+ end
77
+
78
+ it 'should not update rates' do
79
+ expect(bank).to_not receive(:update_rates)
80
+ bank.get_rate('RUB', 'USD')
81
+ end
82
+ end
83
+
84
+ context 'when ttl is set' do
85
+ before { bank.add_rate('RUB', 'USD', 123) }
86
+
87
+ context 'and raks are expired' do
88
+ before do
89
+ bank.instance_variable_set('@rates_updated_at', Time.now - 3600)
90
+ bank.ttl = 3600
91
+ end
92
+
93
+ it 'should update rates' do
94
+ expect(bank).to receive(:update_rates)
95
+ bank.get_rate('RUB', 'USD')
96
+ end
97
+ end
98
+
99
+ context 'and ranks are not expired' do
100
+ before do
101
+ bank.instance_variable_set('@rates_updated_at', Time.now - 3000)
102
+ bank.ttl = 3600
103
+ end
104
+
105
+ it 'should not update rates' do
106
+ expect(bank).to_not receive(:update_rates)
107
+ bank.get_rate('RUB', 'USD')
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,11 +1,8 @@
1
1
  require 'russian_central_bank'
2
- require 'savon'
3
- require 'support/helpers'
4
-
2
+ require 'webmock/rspec'
5
3
 
6
4
  RSpec.configure do |config|
7
5
  config.color = true
8
6
  config.tty = true
9
-
10
7
  config.order = :random
11
8
  end
@@ -0,0 +1,53 @@
1
+
2
+ <?xml version="1.0" encoding="windows-1251"?>
3
+ <ValCurs Date="26.03.2019" name="Foreign Currency Market">
4
+ <Valute ID="R01010">
5
+ <NumCode>036</NumCode>
6
+ <CharCode>AUD</CharCode>
7
+ <Nominal>1</Nominal>
8
+ <Name>Австралийский доллар</Name>
9
+ <Value>45,7365</Value>
10
+ </Valute>
11
+ <Valute ID="R01035">
12
+ <NumCode>826</NumCode>
13
+ <CharCode>GBP</CharCode>
14
+ <Nominal>1</Nominal>
15
+ <Name>Фунт стерлингов Соединенного королевства</Name>
16
+ <Value>85,0552</Value>
17
+ </Valute>
18
+ <Valute ID="R01200">
19
+ <NumCode>344</NumCode>
20
+ <CharCode>HKD</CharCode>
21
+ <Nominal>10</Nominal>
22
+ <Name>Гонконгских долларов</Name>
23
+ <Value>82,1815</Value>
24
+ </Valute>
25
+ <Valute ID="R01215">
26
+ <NumCode>208</NumCode>
27
+ <CharCode>DKK</CharCode>
28
+ <Nominal>10</Nominal>
29
+ <Name>Датских крон</Name>
30
+ <Value>97,6581</Value>
31
+ </Valute>
32
+ <Valute ID="R01235">
33
+ <NumCode>840</NumCode>
34
+ <CharCode>USD</CharCode>
35
+ <Nominal>1</Nominal>
36
+ <Name>Доллар США</Name>
37
+ <Value>64,4993</Value>
38
+ </Valute>
39
+ <Valute ID="R01239">
40
+ <NumCode>978</NumCode>
41
+ <CharCode>EUR</CharCode>
42
+ <Nominal>1</Nominal>
43
+ <Name>Евро</Name>
44
+ <Value>72,9229</Value>
45
+ </Valute>
46
+ <Valute ID="R01820">
47
+ <NumCode>392</NumCode>
48
+ <CharCode>JPY</CharCode>
49
+ <Nominal>100</Nominal>
50
+ <Name>Японских иен</Name>
51
+ <Value>58,5958</Value>
52
+ </Valute>
53
+ </ValCurs>
metadata CHANGED
@@ -1,29 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: russian_central_bank
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ramil Mustafin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-08 00:00:00.000000000 Z
11
+ date: 2019-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.3'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.3'
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rake
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -53,33 +67,61 @@ dependencies:
53
67
  - !ruby/object:Gem::Version
54
68
  version: '3'
55
69
  - !ruby/object:Gem::Dependency
56
- name: money
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: httparty
57
99
  requirement: !ruby/object:Gem::Requirement
58
100
  requirements:
59
101
  - - "~>"
60
102
  - !ruby/object:Gem::Version
61
- version: 6.7.0
103
+ version: '0'
62
104
  type: :runtime
63
105
  prerelease: false
64
106
  version_requirements: !ruby/object:Gem::Requirement
65
107
  requirements:
66
108
  - - "~>"
67
109
  - !ruby/object:Gem::Version
68
- version: 6.7.0
110
+ version: '0'
69
111
  - !ruby/object:Gem::Dependency
70
- name: savon
112
+ name: money
71
113
  requirement: !ruby/object:Gem::Requirement
72
114
  requirements:
73
115
  - - "~>"
74
116
  - !ruby/object:Gem::Version
75
- version: '2.0'
117
+ version: '6'
76
118
  type: :runtime
77
119
  prerelease: false
78
120
  version_requirements: !ruby/object:Gem::Requirement
79
121
  requirements:
80
122
  - - "~>"
81
123
  - !ruby/object:Gem::Version
82
- version: '2.0'
124
+ version: '6'
83
125
  description: RussianCentralBank extends Money::Bank::VariableExchange and gives you
84
126
  access to the Central Bank of Russia currency exchange rates.
85
127
  email:
@@ -88,17 +130,22 @@ executables: []
88
130
  extensions: []
89
131
  extra_rdoc_files: []
90
132
  files:
133
+ - ".circleci/config.yml"
91
134
  - ".gitignore"
135
+ - ".rubocop.yml"
92
136
  - Gemfile
93
137
  - LICENSE.txt
94
138
  - README.md
95
139
  - Rakefile
140
+ - lib/money/bank/russian_central_bank.rb
141
+ - lib/money/bank/russian_central_bank_fetcher.rb
96
142
  - lib/russian_central_bank.rb
97
143
  - russian_central_bank.gemspec
98
- - spec/russian_central_bank_spec.rb
144
+ - spec/integration/update_rates_spec.rb
145
+ - spec/lib/money/bank/russian_central_bank_fetcher_spec.rb
146
+ - spec/lib/money/bank/russian_central_bank_spec.rb
99
147
  - spec/spec_helper.rb
100
- - spec/support/daily_rates.yml
101
- - spec/support/helpers.rb
148
+ - spec/support/rcb_response.xml
102
149
  homepage: http://github.com/rmustafin/russian_central_bank
103
150
  licenses:
104
151
  - MIT
@@ -119,12 +166,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
166
  version: '0'
120
167
  requirements: []
121
168
  rubyforge_project:
122
- rubygems_version: 2.4.8
169
+ rubygems_version: 2.6.11
123
170
  signing_key:
124
171
  specification_version: 4
125
172
  summary: Access to Central Bank of Russia currency exchange rates.
126
173
  test_files:
127
- - spec/russian_central_bank_spec.rb
174
+ - spec/integration/update_rates_spec.rb
175
+ - spec/lib/money/bank/russian_central_bank_fetcher_spec.rb
176
+ - spec/lib/money/bank/russian_central_bank_spec.rb
128
177
  - spec/spec_helper.rb
129
- - spec/support/daily_rates.yml
130
- - spec/support/helpers.rb
178
+ - spec/support/rcb_response.xml
@@ -1,108 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- describe 'RussianCentralBank' do
4
- before do
5
- rates_hash = symbolize_keys YAML::load(File.open('spec/support/daily_rates.yml'))
6
- allow_any_instance_of(Savon::Client).to receive_message_chain(:call, :body) { rates_hash }
7
- end
8
-
9
- before :each do
10
- @bank = Money::Bank::RussianCentralBank.new
11
- end
12
-
13
- describe '#update_rates' do
14
- before do
15
- @bank.update_rates
16
- end
17
-
18
- it 'should update rates from daily rates service' do
19
- expect(@bank.rates['RUB_TO_USD']).to eq(0.03083678705348332)
20
- expect(@bank.rates['RUB_TO_EUR']).to eq(0.023478587528174305)
21
- expect(@bank.rates['RUB_TO_JPY']).to eq(3.086143524190736)
22
- end
23
- end
24
-
25
- describe '#flush_rates' do
26
- before do
27
- @bank.add_rate('RUB', 'USD', 0.03)
28
- end
29
-
30
- it 'should delete all rates' do
31
- @bank.get_rate('RUB', 'USD')
32
- @bank.flush_rates
33
- expect(@bank.store.send(:index)).to be_empty
34
- end
35
- end
36
-
37
- describe '#get_rate' do
38
- context 'getting dicrect rates' do
39
- before do
40
- @bank.flush_rates
41
- @bank.add_rate('RUB', 'USD', 0.03)
42
- @bank.add_rate('RUB', 'GBP', 0.02)
43
- end
44
-
45
- it 'should get rate from @rates' do
46
- expect(@bank.get_rate('RUB', 'USD')).to eq(0.03)
47
- end
48
-
49
- it 'should calculate indirect rates' do
50
- expect(@bank.get_rate('USD', 'GBP')).to eq(0.6666666666666667)
51
- end
52
- end
53
-
54
- context 'getting indirect rate' do
55
- let(:indirect_rate) { 4 }
56
-
57
- before do
58
- @bank.flush_rates
59
- @bank.add_rate('RUB', 'USD', 123)
60
- @bank.add_rate('USD', 'RUB', indirect_rate)
61
- end
62
-
63
- it 'gets indirect rate from the last set' do
64
- expect(@bank.get_rate('RUB', 'USD')).to eq(1.0 / indirect_rate)
65
- end
66
- end
67
-
68
- context "when ttl is not set" do
69
- before do
70
- @bank.add_rate('RUB', 'USD', 123)
71
- @bank.ttl = nil
72
- end
73
-
74
- it "should not update rates" do
75
- expect(@bank).to_not receive(:update_rates)
76
- @bank.get_rate('RUB', 'USD')
77
- end
78
- end
79
-
80
- context "when ttl is set" do
81
- before { @bank.add_rate('RUB', 'USD', 123) }
82
-
83
- context "and raks are expired" do
84
- before do
85
- @bank.instance_variable_set('@rates_updated_at', Time.now - 3600)
86
- @bank.ttl = 3600
87
- end
88
-
89
- it "should update rates" do
90
- expect(@bank).to receive(:update_rates)
91
- @bank.get_rate('RUB', 'USD')
92
- end
93
- end
94
-
95
- context "and ranks are not expired" do
96
- before do
97
- @bank.instance_variable_set('@rates_updated_at', Time.now - 3000)
98
- @bank.ttl = 3600
99
- end
100
-
101
- it "should not update rates" do
102
- expect(@bank).to_not receive(:update_rates)
103
- @bank.get_rate('RUB', 'USD')
104
- end
105
- end
106
- end
107
- end
108
- end
@@ -1,8 +0,0 @@
1
- get_curs_on_date_response:
2
- get_curs_on_date_result:
3
- diffgram:
4
- valute_data:
5
- valute_curs_on_date:
6
- - {vname: "US Dollar", vnom: "1", vcurs: "32.4288", vcode: "840", vch_code: "USD"}
7
- - {vname: "Euro", vnom: "1", vcurs: "42.5920", vcode: "978", vch_code: "EUR"}
8
- - {vname: "Japanese yen", vnom: "100", vcurs: "32.4029", vcode: "392", vch_code: "JPY"}
@@ -1,15 +0,0 @@
1
- def symbolize_keys(hash)
2
- hash.inject({}){|result, (key, value)|
3
- new_key = case key
4
- when String then key.to_sym
5
- else key
6
- end
7
- new_value = case value
8
- when Hash then symbolize_keys(value)
9
- when Array then value.map{ |v| v.is_a?(Hash) ? symbolize_keys(v) : v }
10
- else value
11
- end
12
- result[new_key] = new_value
13
- result
14
- }
15
- end