eu_central_bank 1.6.1 → 2.0.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
  SHA256:
3
- metadata.gz: 5a531cbda27d58075c18fb0ed04177d5d16872e178046854495fe87c2094678e
4
- data.tar.gz: b87062758ed6a9ef559fbd42a8d3bdf4f59366abd3c201951a01af62bbfb46df
3
+ metadata.gz: ca70af38e85e1aa52636b0e86ed6a7686b4c5670451da56a01eb880d82b89138
4
+ data.tar.gz: e552f4eaf63c199a3fb4023a434c8f5d2b8fd324c86177a20628aac0d144e3a8
5
5
  SHA512:
6
- metadata.gz: fffe386b9fadfa03a24e36e77bb52d66df0c21e9d042e96a74e945b314798ee942938a3e2f9aec29c69aabedd2b3934bca780574112c842764f7708816ddeee9
7
- data.tar.gz: 6f495beaafc197894bbeecb9333f31d4d5b530879f64d162442fc8389793a23f87096100ee5ebc9a978143f350445dbd6e0c6651448dddc870b35e7efac8da04
6
+ metadata.gz: 18c56beb798fa5773b2dade4b59eae065abaee2d328532946695d0fa1a7acc2b3f725d4c4d21b5edf86040f766ee8b091588514848c3ab2e2ff258934b2c4669
7
+ data.tar.gz: 2b1de916b41d2304d615824c918faa4fae62d275521fddd842397758c22d80eb4d6a0e0f75ebb8726bf19ff6e8a520b51ebbc2255dd148744bcc238f0049bdf5
data/CHANGELOG.md CHANGED
@@ -1,8 +1,23 @@
1
1
  # eu_central_bank changelog
2
2
 
3
+ ## Upcoming release (unreleased)
4
+
5
+ ## 2.0.0 (Dec 10 2025)
6
+
7
+ * **Breaking change**: Require Ruby >= 3.1
8
+ * **Breaking change**: Update Money dependency to 7.0. Make sure to read [the upgrade
9
+ guide](https://github.com/RubyMoney/money/blob/main/UPGRADING-7.0.md).
10
+ * Update YAML loading from compatibility with recent Psych versions.
11
+ * Move Croatian Kuna (HRK) and Russian Ruble (RUB) to legacy currencies
12
+ * Add `EuCentralBank::VERSION`.
13
+
14
+ ## 1.7.0 (Nov 17 2021)
15
+
16
+ * Support historical rates
17
+
3
18
  ## 1.6.1 (Mar 3 2021)
4
19
 
5
- * Allow a fallback when loading cached rate files to inhibit network fetch.
20
+ * Allow a fallback when loading cached rate files to inhibit network fetch.
6
21
 
7
22
  ## 1.5.0 (Dec 7 2019)
8
23
 
data/LICENSE CHANGED
@@ -1,7 +1,7 @@
1
1
  MIT License
2
2
 
3
3
  Copyright (c) 2009 Wong Liang Zan
4
- Copyright (c) 2021 Shane Emmons
4
+ Copyright (c) 2025 Shane Emmons
5
5
 
6
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # eu_central_bank
2
2
 
3
- [![Build Status](https://travis-ci.org/RubyMoney/eu_central_bank.svg?branch=master)](https://travis-ci.org/RubyMoney/eu_central_bank)
3
+ [![Gem Version](https://badge.fury.io/rb/eu_central_bank.svg)](https://rubygems.org/gems/eu_central_bank)
4
+ [![Ruby](https://github.com/RubyMoney/eu_central_bank/actions/workflows/ruby.yml/badge.svg)](https://github.com/RubyMoney/eu_central_bank/actions/workflows/ruby.yml)
4
5
 
5
6
  ## Introduction
6
7
 
@@ -12,15 +13,10 @@ This gem downloads the exchange rates from the European Central Bank. You can ca
12
13
  gem install eu_central_bank
13
14
  ```
14
15
 
15
- In case you're using older ruby (< 2.1) you need nokogiri < 1.6.8, so add this to your `Gemfile`:
16
-
17
- ```
18
- gem 'nokogiri', '1.6.8'
19
- ```
20
-
21
16
  ## Dependencies
22
17
 
23
18
  - nokogiri
19
+ - bigdecimal
24
20
  - money
25
21
 
26
22
  ## Usage
@@ -72,9 +68,9 @@ eu_bank.exchange_with(Money.new(100, "CAD"), "USD") # Money.new(80, "USD")
72
68
 
73
69
  - Fork the project.
74
70
  - Make your feature addition or bug fix.
75
- - Add tests for it. This is important so I don't break it in a future version unintentionally.
71
+ - Add tests for it. This is important so I don't break it in a future version unintentionally.
76
72
  - Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
77
73
 
78
74
  ## Copyright
79
75
 
80
- Copyright (c) 2010-2016 RubyMoney. See LICENSE for details.
76
+ Copyright (c) 2010-2025 RubyMoney. See LICENSE for details.
@@ -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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EuCentralBank < Money::Bank::VariableExchange
4
+ VERSION = "2.0.0"
5
+ end
@@ -2,6 +2,8 @@ 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'
6
+ require 'eu_central_bank/version'
5
7
 
6
8
  class InvalidCache < StandardError ; end
7
9
 
@@ -16,9 +18,12 @@ class EuCentralBank < Money::Bank::VariableExchange
16
18
 
17
19
  SERIALIZER_DATE_SEPARATOR = '_AT_'
18
20
  DECIMAL_PRECISION = 5
19
- 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
21
+ CURRENCIES = %w(USD JPY BGN CZK DKK GBP HUF ILS ISK PLN RON SEK CHF NOK TRY AUD BRL CAD CNY HKD IDR INR KRW MXN MYR NZD PHP SGD THB ZAR).map(&:freeze).freeze
20
22
  ECB_RATES_URL = 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'.freeze
21
23
  ECB_90_DAY_URL = 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml'.freeze
24
+ ECB_ALL_HIST_URL = 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.xml'.freeze
25
+
26
+ LEGACY_CURRENCIES = %w(CYP HRK RUB SIT ROL TRL)
22
27
 
23
28
  def initialize(st = Money::RatesStore::StoreWithHistoricalDataSupport.new, &block)
24
29
  super
@@ -29,24 +34,30 @@ class EuCentralBank < Money::Bank::VariableExchange
29
34
  update_parsed_rates(doc(cache, url))
30
35
  end
31
36
 
32
- def update_historical_rates(cache=nil)
33
- update_parsed_historical_rates(doc(cache, ECB_90_DAY_URL))
37
+ def update_historical_rates(cache=nil, all=false)
38
+ url = all ? ECB_ALL_HIST_URL : ECB_90_DAY_URL
39
+ update_parsed_historical_rates(doc(cache, url))
34
40
  end
35
41
 
36
42
  def save_rates(cache, url=ECB_RATES_URL)
37
43
  raise InvalidCache unless cache
38
44
  File.open(cache, "w") do |file|
39
- io = open_url(url);
45
+ io = URI.open(url)
40
46
  io.each_line { |line| file.puts line }
41
47
  end
42
48
  end
43
49
 
50
+ def save_historical_rates(cache, all=false)
51
+ url = all ? ECB_ALL_HIST_URL : ECB_90_DAY_URL
52
+ save_rates(cache, url)
53
+ end
54
+
44
55
  def update_rates_from_s(content)
45
- update_parsed_rates(doc_from_s(content))
56
+ update_parsed_rates(parse_rates(content))
46
57
  end
47
58
 
48
59
  def save_rates_to_s(url=ECB_RATES_URL)
49
- open_url(url).read
60
+ URI.open(url).read
50
61
  end
51
62
 
52
63
  def exchange(cents, from_currency, to_currency, date=nil)
@@ -139,7 +150,11 @@ class EuCentralBank < Money::Bank::VariableExchange
139
150
  when :ruby
140
151
  Marshal.load(s)
141
152
  when :yaml
142
- YAML.load(s)
153
+ if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0')
154
+ YAML.safe_load(s, permitted_classes: [ BigDecimal ])
155
+ else
156
+ YAML.safe_load(s, [ BigDecimal ], [], true)
157
+ end
143
158
  end
144
159
 
145
160
  data.each do |key, rate|
@@ -164,48 +179,53 @@ class EuCentralBank < Money::Bank::VariableExchange
164
179
 
165
180
  def doc(cache, url=ECB_RATES_URL)
166
181
  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))
182
+ begin
183
+ parse_rates(URI.open(rates_source))
184
+ rescue Nokogiri::XML::XPath::SyntaxError
185
+ parse_rates(URI.open(url))
186
+ end
170
187
  end
171
188
 
172
- def doc_from_s(content)
173
- Nokogiri::XML(content)
174
- end
189
+ def parse_rates(io)
190
+ doc = ::EuCentralBank::RatesDocument.new
191
+ parser = Nokogiri::XML::SAX::Parser.new(doc)
192
+ parser.parse(io)
175
193
 
176
- def update_parsed_rates(doc)
177
- rates = doc.xpath('gesmes:Envelope/xmlns:Cube/xmlns:Cube//xmlns:Cube')
194
+ unless doc.errors.empty?
195
+ # Temporary workaround for jruby until
196
+ # https://github.com/sparklemotion/nokogiri/pull/1872 gets
197
+ # released and we bump nokogiri version to include it.
198
+ # TLDR: jruby version of SAX parser will mask all the exceptions
199
+ # raised in document so we will raise it here if there were errors.
200
+ raise Nokogiri::XML::XPath::SyntaxError, doc.errors.join("\n")
201
+ end
178
202
 
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)
203
+ doc
204
+ end
205
+
206
+ def copy_rates(rates_document, with_date = false)
207
+ rates_document.rates.each do |date, rates|
208
+ rates.each do |currency, rate|
209
+ next if LEGACY_CURRENCIES.include?(currency)
210
+ set_rate('EUR', currency, BigDecimal(rate, DECIMAL_PRECISION), with_date ? date : nil)
184
211
  end
185
- set_rate("EUR", "EUR", 1)
212
+ set_rate('EUR', 'EUR', 1, with_date ? date : nil)
186
213
  end
214
+ end
187
215
 
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
-
216
+ def update_parsed_rates(rates_document)
217
+ store.transaction true do
218
+ copy_rates(rates_document)
219
+ end
220
+ @rates_updated_at = rates_document.updated_at
191
221
  @last_updated = Time.now
192
222
  end
193
223
 
194
- def update_parsed_historical_rates(doc)
195
- rates = doc.xpath('gesmes:Envelope/xmlns:Cube/xmlns:Cube//xmlns:Cube')
196
-
224
+ def update_parsed_historical_rates(rates_document)
197
225
  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
226
+ copy_rates(rates_document, true)
204
227
  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
-
228
+ @historical_rates_updated_at = rates_document.updated_at
209
229
  @historical_last_updated = Time.now
210
230
  end
211
231
 
@@ -218,12 +238,4 @@ class EuCentralBank < Money::Bank::VariableExchange
218
238
  money = (decimal_money * from.cents * rate).round
219
239
  Money.new(money, to_currency)
220
240
  end
221
-
222
- def open_url(url)
223
- if RUBY_VERSION >= '2.5.0'
224
- URI.open(url)
225
- else
226
- open(url)
227
- end
228
- end
229
241
  end
metadata CHANGED
@@ -1,63 +1,57 @@
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: 2.0.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: 2025-12-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: nokogiri
14
+ name: bigdecimal
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.9'
19
+ version: '0'
20
20
  type: :runtime
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.9'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: money
28
+ name: nokogiri
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '6.13'
34
- - - ">="
35
- - !ruby/object:Gem::Version
36
- version: 6.13.6
33
+ version: '1.11'
37
34
  type: :runtime
38
35
  prerelease: false
39
36
  version_requirements: !ruby/object:Gem::Requirement
40
37
  requirements:
41
38
  - - "~>"
42
39
  - !ruby/object:Gem::Version
43
- version: '6.13'
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: 6.13.6
40
+ version: '1.11'
47
41
  - !ruby/object:Gem::Dependency
48
- name: rspec
42
+ name: money
49
43
  requirement: !ruby/object:Gem::Requirement
50
44
  requirements:
51
45
  - - "~>"
52
46
  - !ruby/object:Gem::Version
53
- version: 3.5.0
54
- type: :development
47
+ version: '7.0'
48
+ type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
51
  requirements:
58
52
  - - "~>"
59
53
  - !ruby/object:Gem::Version
60
- version: 3.5.0
54
+ version: '7.0'
61
55
  description: This gem reads exchange rates from the european central bank website.
62
56
  It uses it to calculates exchange rates. It is compatible with the money gem
63
57
  email:
@@ -70,11 +64,16 @@ files:
70
64
  - LICENSE
71
65
  - README.md
72
66
  - lib/eu_central_bank.rb
67
+ - lib/eu_central_bank/rates_document.rb
68
+ - lib/eu_central_bank/version.rb
73
69
  - lib/money/rates_store/store_with_historical_data_support.rb
74
70
  homepage: https://github.com/RubyMoney/eu_central_bank
75
71
  licenses:
76
72
  - MIT
77
- metadata: {}
73
+ metadata:
74
+ changelog_uri: https://github.com/RubyMoney/eu_central_bank/blob/main/CHANGELOG.md
75
+ source_code_uri: https://github.com/RubyMoney/eu_central_bank
76
+ bug_tracker_uri: https://github.com/RubyMoney/eu_central_bank/issues
78
77
  post_install_message:
79
78
  rdoc_options: []
80
79
  require_paths:
@@ -83,14 +82,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
83
82
  requirements:
84
83
  - - ">="
85
84
  - !ruby/object:Gem::Version
86
- version: '0'
85
+ version: 3.1.0
87
86
  required_rubygems_version: !ruby/object:Gem::Requirement
88
87
  requirements:
89
88
  - - ">="
90
89
  - !ruby/object:Gem::Version
91
90
  version: '0'
92
91
  requirements: []
93
- rubygems_version: 3.2.3
92
+ rubygems_version: 3.5.22
94
93
  signing_key:
95
94
  specification_version: 4
96
95
  summary: Calculates exchange rates based on rates from european central bank. Money