russian_central_bank 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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