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 +4 -4
- data/CHANGELOG.md +16 -1
- data/LICENSE +1 -1
- data/README.md +5 -9
- data/lib/eu_central_bank/rates_document.rb +45 -0
- data/lib/eu_central_bank/version.rb +5 -0
- data/lib/eu_central_bank.rb +57 -45
- metadata +22 -23
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ca70af38e85e1aa52636b0e86ed6a7686b4c5670451da56a01eb880d82b89138
|
|
4
|
+
data.tar.gz: e552f4eaf63c199a3fb4023a434c8f5d2b8fd324c86177a20628aac0d144e3a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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)
|
|
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
|
-
[](https://rubygems.org/gems/eu_central_bank)
|
|
4
|
+
[](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
|
|
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-
|
|
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
|
data/lib/eu_central_bank.rb
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
56
|
+
update_parsed_rates(parse_rates(content))
|
|
46
57
|
end
|
|
47
58
|
|
|
48
59
|
def save_rates_to_s(url=ECB_RATES_URL)
|
|
49
|
-
|
|
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
|
-
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
Nokogiri::XML
|
|
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
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
177
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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(
|
|
212
|
+
set_rate('EUR', 'EUR', 1, with_date ? date : nil)
|
|
186
213
|
end
|
|
214
|
+
end
|
|
187
215
|
|
|
188
|
-
|
|
189
|
-
|
|
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(
|
|
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
|
-
|
|
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:
|
|
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:
|
|
11
|
+
date: 2025-12-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: bigdecimal
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
17
|
+
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
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: '
|
|
26
|
+
version: '0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
28
|
+
name: nokogiri
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
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: '
|
|
44
|
-
- - ">="
|
|
45
|
-
- !ruby/object:Gem::Version
|
|
46
|
-
version: 6.13.6
|
|
40
|
+
version: '1.11'
|
|
47
41
|
- !ruby/object:Gem::Dependency
|
|
48
|
-
name:
|
|
42
|
+
name: money
|
|
49
43
|
requirement: !ruby/object:Gem::Requirement
|
|
50
44
|
requirements:
|
|
51
45
|
- - "~>"
|
|
52
46
|
- !ruby/object:Gem::Version
|
|
53
|
-
version:
|
|
54
|
-
type: :
|
|
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:
|
|
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:
|
|
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.
|
|
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
|