eu_central_bank 1.6.1 → 1.7.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
  SHA256:
3
- metadata.gz: 5a531cbda27d58075c18fb0ed04177d5d16872e178046854495fe87c2094678e
4
- data.tar.gz: b87062758ed6a9ef559fbd42a8d3bdf4f59366abd3c201951a01af62bbfb46df
3
+ metadata.gz: b589452c483bac16b40ab1372829694c9f261b9c802381110ed00fcebb73eaa6
4
+ data.tar.gz: 551614b051eb56ef4c10be19a5e796ebe0194287e1c278d374ae12f9aa5a2b9e
5
5
  SHA512:
6
- metadata.gz: fffe386b9fadfa03a24e36e77bb52d66df0c21e9d042e96a74e945b314798ee942938a3e2f9aec29c69aabedd2b3934bca780574112c842764f7708816ddeee9
7
- data.tar.gz: 6f495beaafc197894bbeecb9333f31d4d5b530879f64d162442fc8389793a23f87096100ee5ebc9a978143f350445dbd6e0c6651448dddc870b35e7efac8da04
6
+ metadata.gz: 0774ff56419fa0b24d71d8747259b1464468e3cbdb4b6db52808bf3ab9e6023cdb4a00f92e387df9e27fb9d84a16a933a588f49e7d7355db86704fa0a67ab8d7
7
+ data.tar.gz: 1f2a24bfd29add88ea0c537b2b7be8a245bcd8fdbe8a7bda04474757e3df9b8ebb62271b34e8dadfc029c2918b4d5c80f4a6783f043eb22d9447f29911db2534
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # eu_central_bank changelog
2
2
 
3
+ ## 1.7.0 (Nov 17 2021)
4
+
5
+ * Support historical rates
6
+
3
7
  ## 1.6.1 (Mar 3 2021)
4
8
 
5
9
  * Allow a fallback when loading cached rate files to inhibit network fetch.
@@ -0,0 +1,45 @@
1
+ class EuCentralBank < Money::Bank::VariableExchange
2
+ class RatesDocument < Nokogiri::XML::SAX::Document
3
+ attr_reader :rates
4
+ attr_reader :errors
5
+ attr_reader :updated_at
6
+
7
+ def initialize
8
+ @rates = {}
9
+ @errors = []
10
+ @updated_at = nil
11
+ @current_date = nil
12
+ end
13
+
14
+ def error(msg)
15
+ # TODO: remove this workaround after
16
+ # https://github.com/sparklemotion/nokogiri/pull/1872
17
+ # is released
18
+ @errors << msg
19
+ super
20
+ end
21
+
22
+ def start_element(name, attributes=[])
23
+ return if name != 'Cube' || attributes.empty?
24
+ begin
25
+ first_name, first_value = attributes[0]
26
+ case first_name
27
+ when 'time'
28
+ @current_date = Time.parse(first_value).to_date
29
+ @updated_at ||= @current_date
30
+ @rates[@current_date] = []
31
+ when 'currency'
32
+ currency = first_value
33
+ _, rate = attributes[1]
34
+ @rates[@current_date] << [currency, rate]
35
+ end
36
+ rescue StandardError => e
37
+ raise Nokogiri::XML::XPath::SyntaxError, e.message
38
+ end
39
+ end
40
+
41
+ def end_document
42
+ raise Nokogiri::XML::XPath::SyntaxError if @rates.empty? || @updated_at.nil?
43
+ end
44
+ end
45
+ end
@@ -2,6 +2,7 @@ require 'open-uri'
2
2
  require 'nokogiri'
3
3
  require 'money'
4
4
  require 'money/rates_store/store_with_historical_data_support'
5
+ require 'eu_central_bank/rates_document'
5
6
 
6
7
  class InvalidCache < StandardError ; end
7
8
 
@@ -19,6 +20,9 @@ class EuCentralBank < Money::Bank::VariableExchange
19
20
  CURRENCIES = %w(USD JPY BGN CZK DKK GBP HUF ILS ISK PLN RON SEK CHF NOK HRK RUB TRY AUD BRL CAD CNY HKD IDR INR KRW MXN MYR NZD PHP SGD THB ZAR).map(&:freeze).freeze
20
21
  ECB_RATES_URL = 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'.freeze
21
22
  ECB_90_DAY_URL = 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml'.freeze
23
+ ECB_ALL_HIST_URL = 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.xml'.freeze
24
+
25
+ LEGACY_CURRENCIES = %w(CYP SIT ROL TRL)
22
26
 
23
27
  def initialize(st = Money::RatesStore::StoreWithHistoricalDataSupport.new, &block)
24
28
  super
@@ -29,20 +33,26 @@ class EuCentralBank < Money::Bank::VariableExchange
29
33
  update_parsed_rates(doc(cache, url))
30
34
  end
31
35
 
32
- def update_historical_rates(cache=nil)
33
- update_parsed_historical_rates(doc(cache, ECB_90_DAY_URL))
36
+ def update_historical_rates(cache=nil, all=false)
37
+ url = all ? ECB_ALL_HIST_URL : ECB_90_DAY_URL
38
+ update_parsed_historical_rates(doc(cache, url))
34
39
  end
35
40
 
36
41
  def save_rates(cache, url=ECB_RATES_URL)
37
42
  raise InvalidCache unless cache
38
43
  File.open(cache, "w") do |file|
39
- io = open_url(url);
44
+ io = open_url(url)
40
45
  io.each_line { |line| file.puts line }
41
46
  end
42
47
  end
43
48
 
49
+ def save_historical_rates(cache, all=false)
50
+ url = all ? ECB_ALL_HIST_URL : ECB_90_DAY_URL
51
+ save_rates(cache, url)
52
+ end
53
+
44
54
  def update_rates_from_s(content)
45
- update_parsed_rates(doc_from_s(content))
55
+ update_parsed_rates(parse_rates(content))
46
56
  end
47
57
 
48
58
  def save_rates_to_s(url=ECB_RATES_URL)
@@ -164,48 +174,53 @@ class EuCentralBank < Money::Bank::VariableExchange
164
174
 
165
175
  def doc(cache, url=ECB_RATES_URL)
166
176
  rates_source = !!cache ? cache : url
167
- Nokogiri::XML(open_url(rates_source)).tap { |doc| doc.xpath('gesmes:Envelope/xmlns:Cube/xmlns:Cube//xmlns:Cube') }
168
- rescue Nokogiri::XML::XPath::SyntaxError
169
- Nokogiri::XML(open_url(url))
177
+ begin
178
+ parse_rates(open_url(rates_source))
179
+ rescue Nokogiri::XML::XPath::SyntaxError
180
+ parse_rates(open_url(url))
181
+ end
170
182
  end
171
183
 
172
- def doc_from_s(content)
173
- Nokogiri::XML(content)
174
- end
184
+ def parse_rates(io)
185
+ doc = ::EuCentralBank::RatesDocument.new
186
+ parser = Nokogiri::XML::SAX::Parser.new(doc)
187
+ parser.parse(io)
175
188
 
176
- def update_parsed_rates(doc)
177
- rates = doc.xpath('gesmes:Envelope/xmlns:Cube/xmlns:Cube//xmlns:Cube')
189
+ unless doc.errors.empty?
190
+ # Temporary workaround for jruby until
191
+ # https://github.com/sparklemotion/nokogiri/pull/1872 gets
192
+ # released and we bump nokogiri version to include it.
193
+ # TLDR: jruby version of SAX parser will mask all the exceptions
194
+ # raised in document so we will raise it here if there were errors.
195
+ raise Nokogiri::XML::XPath::SyntaxError, doc.errors.join("\n")
196
+ end
178
197
 
179
- store.transaction true do
180
- rates.each do |exchange_rate|
181
- rate = BigDecimal(exchange_rate.attribute("rate").value, DECIMAL_PRECISION)
182
- currency = exchange_rate.attribute("currency").value
183
- set_rate("EUR", currency, rate)
198
+ doc
199
+ end
200
+
201
+ def copy_rates(rates_document, with_date = false)
202
+ rates_document.rates.each do |date, rates|
203
+ rates.each do |currency, rate|
204
+ next if LEGACY_CURRENCIES.include?(currency)
205
+ set_rate('EUR', currency, BigDecimal(rate, DECIMAL_PRECISION), with_date ? date : nil)
184
206
  end
185
- set_rate("EUR", "EUR", 1)
207
+ set_rate('EUR', 'EUR', 1, with_date ? date : nil)
186
208
  end
209
+ end
187
210
 
188
- rates_updated_at = doc.xpath('gesmes:Envelope/xmlns:Cube/xmlns:Cube/@time').first.value
189
- @rates_updated_at = Time.parse(rates_updated_at)
190
-
211
+ def update_parsed_rates(rates_document)
212
+ store.transaction true do
213
+ copy_rates(rates_document)
214
+ end
215
+ @rates_updated_at = rates_document.updated_at
191
216
  @last_updated = Time.now
192
217
  end
193
218
 
194
- def update_parsed_historical_rates(doc)
195
- rates = doc.xpath('gesmes:Envelope/xmlns:Cube/xmlns:Cube//xmlns:Cube')
196
-
219
+ def update_parsed_historical_rates(rates_document)
197
220
  store.transaction true do
198
- rates.each do |exchange_rate|
199
- rate = BigDecimal(exchange_rate.attribute("rate").value, DECIMAL_PRECISION)
200
- currency = exchange_rate.attribute("currency").value
201
- date = exchange_rate.parent.attribute("time").value
202
- set_rate("EUR", currency, rate, date)
203
- end
221
+ copy_rates(rates_document, true)
204
222
  end
205
-
206
- rates_updated_at = doc.xpath('gesmes:Envelope/xmlns:Cube/xmlns:Cube/@time').first.value
207
- @historical_rates_updated_at = Time.parse(rates_updated_at)
208
-
223
+ @historical_rates_updated_at = rates_document.updated_at
209
224
  @historical_last_updated = Time.now
210
225
  end
211
226
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eu_central_bank
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.1
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane Emmons
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-03 00:00:00.000000000 Z
11
+ date: 2021-11-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -70,6 +70,7 @@ files:
70
70
  - LICENSE
71
71
  - README.md
72
72
  - lib/eu_central_bank.rb
73
+ - lib/eu_central_bank/rates_document.rb
73
74
  - lib/money/rates_store/store_with_historical_data_support.rb
74
75
  homepage: https://github.com/RubyMoney/eu_central_bank
75
76
  licenses:
@@ -90,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
91
  - !ruby/object:Gem::Version
91
92
  version: '0'
92
93
  requirements: []
93
- rubygems_version: 3.2.3
94
+ rubygems_version: 3.2.22
94
95
  signing_key:
95
96
  specification_version: 4
96
97
  summary: Calculates exchange rates based on rates from european central bank. Money