money-currencylayer-bank 0.5.3 → 0.5.7
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 +5 -5
- data/Gemfile +3 -1
- data/LICENSE +1 -1
- data/README.md +8 -3
- data/lib/money/bank/currencylayer_bank.rb +86 -39
- data/test/currencylayer_bank_test.rb +10 -4
- data/test/test_helper.rb +2 -0
- metadata +9 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b3c0fb9028cd1aeffa15e39f1ac11611a98ebc98c4388b3d7e15c3926f186ae2
|
4
|
+
data.tar.gz: 4cd0872bd7ddf5957a80b344a781656532b98c77b4eaf69f1d5b36eb6b4b4a48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cddb985e9d2f028674acff2e4f5605ff9d504396abbcec1e63cadadc3ea7a9620517e9af28d622fccf4729a70e0500918b6f9d218e38d01d42dd1fb91cc00581
|
7
|
+
data.tar.gz: b138d9a8d0ca0fa1ece3d4c576088ad7fe6e023b96fd3d21537e048764df887c871e9eebc2cfe8c425bca682aa20e63d2e1a38ebc0259611ee2a57f8bfb9e7a3
|
data/Gemfile
CHANGED
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -40,6 +40,7 @@ See more about Currencylayer product plans on https://currencylayer.com/product.
|
|
40
40
|
* average response time < 20ms
|
41
41
|
* supports caching currency rates
|
42
42
|
* calculates every pair rate calculating inverse rate or using base currency rate
|
43
|
+
* supports multiple server instances, thread safe
|
43
44
|
|
44
45
|
## Installation
|
45
46
|
|
@@ -89,7 +90,7 @@ mclb.secure_connection = true
|
|
89
90
|
# Define cache (string or pathname)
|
90
91
|
mclb.cache = 'path/to/file/cache'
|
91
92
|
|
92
|
-
# Set money default bank to
|
93
|
+
# Set money default bank to Currencylayer bank
|
93
94
|
Money.default_bank = mclb
|
94
95
|
~~~
|
95
96
|
|
@@ -104,7 +105,7 @@ mclb.source
|
|
104
105
|
# Expires rates if the expiration time is reached.
|
105
106
|
mclb.expire_rates!
|
106
107
|
|
107
|
-
#
|
108
|
+
# Returns true if the expiration time is reached.
|
108
109
|
mclb.expired?
|
109
110
|
|
110
111
|
# Get the API source url.
|
@@ -112,6 +113,9 @@ mclb.source_url
|
|
112
113
|
|
113
114
|
# Get the rates timestamp of the last API request.
|
114
115
|
mclb.rates_timestamp
|
116
|
+
|
117
|
+
# Get the rates timestamp of loaded rates in memory.
|
118
|
+
moxb.rates_mem_timestamp
|
115
119
|
~~~
|
116
120
|
|
117
121
|
### How to exchange
|
@@ -184,6 +188,7 @@ bundle exec rake
|
|
184
188
|
## Other Implementations
|
185
189
|
|
186
190
|
* Gem [currencylayer](https://github.com/askuratovsky/currencylayer)
|
191
|
+
* Gem [money-openexchangerates-bank](https://github.com/phlegx/money-openexchangerates-bank)
|
187
192
|
* Gem [money-open-exchange-rates](https://github.com/spk/money-open-exchange-rates)
|
188
193
|
* Gem [money-historical-bank](https://github.com/atwam/money-historical-bank)
|
189
194
|
* Gem [eu_central_bank](https://github.com/RubyMoney/eu_central_bank)
|
@@ -207,4 +212,4 @@ bundle exec rake
|
|
207
212
|
|
208
213
|
The MIT License
|
209
214
|
|
210
|
-
Copyright (c)
|
215
|
+
Copyright (c) 2017 Phlegx Systems OG
|
@@ -1,10 +1,25 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
require 'open-uri'
|
3
5
|
require 'money'
|
4
6
|
require 'json'
|
5
7
|
|
6
8
|
# Money gem class
|
7
9
|
class Money
|
10
|
+
# Build in memory rates store
|
11
|
+
module RatesStore
|
12
|
+
# Memory class
|
13
|
+
class Memory
|
14
|
+
# Add method to reset the build in memory store
|
15
|
+
# @param [Hash] rt Optional initial exchange rate data.
|
16
|
+
# @return [Object] store.
|
17
|
+
def reset!(rt = {})
|
18
|
+
transaction { @index = rt }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
8
23
|
# https://github.com/RubyMoney/money#exchange-rate-stores
|
9
24
|
module Bank
|
10
25
|
# Invalid cache, file not found or cache empty
|
@@ -14,12 +29,11 @@ class Money
|
|
14
29
|
class NoAccessKey < StandardError; end
|
15
30
|
|
16
31
|
# CurrencylayerBank base class
|
17
|
-
# rubocop:disable Metrics/ClassLength
|
18
32
|
class CurrencylayerBank < Money::Bank::VariableExchange
|
19
33
|
# CurrencylayerBank url
|
20
34
|
CL_URL = 'http://apilayer.net/api/live'.freeze
|
21
35
|
# CurrencylayerBank secure url
|
22
|
-
CL_SECURE_URL = CL_URL.
|
36
|
+
CL_SECURE_URL = CL_URL.sub('http:', 'https:')
|
23
37
|
# Default base currency
|
24
38
|
CL_SOURCE = 'USD'.freeze
|
25
39
|
|
@@ -28,27 +42,28 @@ class Money
|
|
28
42
|
# for the free plan users.
|
29
43
|
#
|
30
44
|
# @param value [Boolean] true for secure connection
|
31
|
-
#
|
32
45
|
# @return [Boolean] chosen secure connection
|
33
46
|
attr_accessor :secure_connection
|
34
47
|
|
35
48
|
# API must have a valid access_key
|
36
49
|
#
|
37
50
|
# @param value [String] API access key
|
38
|
-
#
|
39
51
|
# @return [String] chosen API access key
|
40
52
|
attr_accessor :access_key
|
41
53
|
|
42
54
|
# Cache accessor, can be a String or a Proc
|
43
55
|
#
|
44
56
|
# @param value [String,Pathname,Proc] cache system
|
45
|
-
#
|
46
57
|
# @return [String,Pathname,Proc] chosen cache system
|
47
58
|
attr_accessor :cache
|
48
59
|
|
49
60
|
# Parsed CurrencylayerBank result as Hash
|
50
61
|
attr_reader :rates
|
51
62
|
|
63
|
+
# Get the timestamp of rates in memory
|
64
|
+
# @return [Time] time object or nil
|
65
|
+
attr_reader :rates_mem_timestamp
|
66
|
+
|
52
67
|
# Set the seconds after than the current rates are automatically expired
|
53
68
|
# by default, they never expire.
|
54
69
|
#
|
@@ -56,7 +71,6 @@ class Money
|
|
56
71
|
# ttl_in_seconds = 86400 # will expire the rates in one day
|
57
72
|
#
|
58
73
|
# @param value [Integer] time to live in seconds
|
59
|
-
#
|
60
74
|
# @return [Integer] chosen time to live in seconds
|
61
75
|
attr_writer :ttl_in_seconds
|
62
76
|
|
@@ -68,7 +82,6 @@ class Money
|
|
68
82
|
# source = 'USD'
|
69
83
|
#
|
70
84
|
# @param value [String] Currency code, ISO 3166-1 alpha-3
|
71
|
-
#
|
72
85
|
# @return [String] chosen base currency
|
73
86
|
def source=(value)
|
74
87
|
@source = Money::Currency.find(value.to_s).try(:iso_code) || CL_SOURCE
|
@@ -90,57 +103,50 @@ class Money
|
|
90
103
|
# Update all rates from CurrencylayerBank JSON
|
91
104
|
# @return [Array] array of exchange rates
|
92
105
|
def update_rates(straight = false)
|
93
|
-
|
106
|
+
store.reset!
|
107
|
+
rates = exchange_rates(straight).each do |exchange_rate|
|
94
108
|
currency = exchange_rate.first[3..-1]
|
95
109
|
rate = exchange_rate.last
|
96
110
|
next unless Money::Currency.find(currency)
|
97
111
|
add_rate(source, currency, rate)
|
98
112
|
add_rate(currency, source, 1.0 / rate)
|
99
113
|
end
|
114
|
+
@rates_mem_timestamp = rates_timestamp
|
115
|
+
rates
|
100
116
|
end
|
101
117
|
|
118
|
+
# Override Money `add_rate` method for caching
|
119
|
+
# @param [String] from_currency Currency ISO code. ex. 'USD'
|
120
|
+
# @param [String] to_currency Currency ISO code. ex. 'CAD'
|
121
|
+
# @param [Numeric] rate Rate to use when exchanging currencies.
|
122
|
+
# @return [Numeric] rate.
|
123
|
+
def add_rate(from_currency, to_currency, rate)
|
124
|
+
super
|
125
|
+
end
|
126
|
+
|
127
|
+
# Alias super method
|
128
|
+
alias super_get_rate get_rate
|
129
|
+
|
102
130
|
# Override Money `get_rate` method for caching
|
103
131
|
# @param [String] from_currency Currency ISO code. ex. 'USD'
|
104
132
|
# @param [String] to_currency Currency ISO code. ex. 'CAD'
|
105
|
-
#
|
133
|
+
# @param [Hash] opts Options hash to set special parameters.
|
106
134
|
# @return [Numeric] rate.
|
107
|
-
def get_rate(from_currency, to_currency, opts = {})
|
135
|
+
def get_rate(from_currency, to_currency, opts = {})
|
108
136
|
expire_rates!
|
109
|
-
rate =
|
110
|
-
|
111
|
-
# Tries to calculate an inverse rate
|
112
|
-
inverse_rate = super(to_currency, from_currency, opts)
|
113
|
-
if inverse_rate
|
114
|
-
rate = 1.0 / inverse_rate
|
115
|
-
add_rate(from_currency, to_currency, rate)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
unless rate
|
119
|
-
# Tries to calculate a pair rate using base currency rate
|
120
|
-
from_base_rate = super(source, from_currency, opts)
|
121
|
-
unless from_base_rate
|
122
|
-
from_inverse_rate = super(from_currency, source, opts)
|
123
|
-
from_base_rate = 1.0 / from_inverse_rate if from_inverse_rate
|
124
|
-
end
|
125
|
-
to_base_rate = super(source, to_currency, opts)
|
126
|
-
unless to_base_rate
|
127
|
-
to_inverse_rate = super(to_currency, source, opts)
|
128
|
-
to_base_rate = 1.0 / to_inverse_rate if to_inverse_rate
|
129
|
-
end
|
130
|
-
if to_base_rate && from_base_rate
|
131
|
-
rate = to_base_rate / from_base_rate
|
132
|
-
add_rate(from_currency, to_currency, rate)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
rate
|
137
|
+
rate = get_rate_or_calc_inverse(from_currency, to_currency, opts)
|
138
|
+
rate || calc_pair_rate_using_base(from_currency, to_currency, opts)
|
136
139
|
end
|
137
140
|
|
138
|
-
# Fetch new rates if cached rates are expired
|
141
|
+
# Fetch new rates if cached rates are expired or stale
|
139
142
|
# @return [Boolean] true if rates are expired and updated from remote
|
140
143
|
def expire_rates!
|
141
144
|
if expired?
|
142
145
|
update_rates(true)
|
143
146
|
true
|
147
|
+
elsif stale?
|
148
|
+
update_rates
|
149
|
+
true
|
144
150
|
else
|
145
151
|
false
|
146
152
|
end
|
@@ -152,6 +158,14 @@ class Money
|
|
152
158
|
Time.now > rates_expiration
|
153
159
|
end
|
154
160
|
|
161
|
+
# Check if rates are stale
|
162
|
+
# Stale is true if rates are updated straight by another thread.
|
163
|
+
# The actual thread has always old rates in memory store.
|
164
|
+
# @return [Boolean] true if rates are stale
|
165
|
+
def stale?
|
166
|
+
rates_timestamp != rates_mem_timestamp
|
167
|
+
end
|
168
|
+
|
155
169
|
# Source url of CurrencylayerBank
|
156
170
|
# defined with access_key and secure_connection
|
157
171
|
# @return [String] the remote API url
|
@@ -226,7 +240,7 @@ class Money
|
|
226
240
|
# Opens an url and reads the content
|
227
241
|
# @return [String] unparsed JSON content
|
228
242
|
def open_url
|
229
|
-
|
243
|
+
::OpenURI.open_uri(source_url).read
|
230
244
|
rescue OpenURI::HTTPError
|
231
245
|
''
|
232
246
|
end
|
@@ -262,6 +276,7 @@ class Money
|
|
262
276
|
end
|
263
277
|
|
264
278
|
# Get raw exchange rates from cache and then from url
|
279
|
+
# @param rescue_straight [Boolean] true for rescue straight, default true
|
265
280
|
# @return [String] JSON content
|
266
281
|
def raw_rates_careful(rescue_straight = true)
|
267
282
|
JSON.parse(read_from_cache.to_s)
|
@@ -276,6 +291,38 @@ class Money
|
|
276
291
|
rescue JSON::ParserError
|
277
292
|
raw_rates_careful(false)
|
278
293
|
end
|
294
|
+
|
295
|
+
# Get rate or calculate it as inverse rate
|
296
|
+
# @param [String] from_currency Currency ISO code. ex. 'USD'
|
297
|
+
# @param [String] to_currency Currency ISO code. ex. 'CAD'
|
298
|
+
# @return [Numeric] rate or rate calculated as inverse rate.
|
299
|
+
def get_rate_or_calc_inverse(from_currency, to_currency, opts = {})
|
300
|
+
rate = super_get_rate(from_currency, to_currency, opts)
|
301
|
+
unless rate
|
302
|
+
# Tries to calculate an inverse rate
|
303
|
+
inverse_rate = super_get_rate(to_currency, from_currency, opts)
|
304
|
+
if inverse_rate
|
305
|
+
rate = 1.0 / inverse_rate
|
306
|
+
add_rate(from_currency, to_currency, rate)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
rate
|
310
|
+
end
|
311
|
+
|
312
|
+
# Tries to calculate a pair rate using base currency rate
|
313
|
+
# @param [String] from_currency Currency ISO code. ex. 'USD'
|
314
|
+
# @param [String] to_currency Currency ISO code. ex. 'CAD'
|
315
|
+
# @return [Numeric] rate or nil if cannot calculate rate.
|
316
|
+
def calc_pair_rate_using_base(from_currency, to_currency, opts = {})
|
317
|
+
from_base_rate = get_rate_or_calc_inverse(source, from_currency, opts)
|
318
|
+
to_base_rate = get_rate_or_calc_inverse(source, to_currency, opts)
|
319
|
+
if to_base_rate && from_base_rate
|
320
|
+
rate = to_base_rate / from_base_rate
|
321
|
+
add_rate(from_currency, to_currency, rate)
|
322
|
+
return rate
|
323
|
+
end
|
324
|
+
nil
|
325
|
+
end
|
279
326
|
end
|
280
327
|
end
|
281
328
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
3
5
|
|
4
6
|
describe Money::Bank::CurrencylayerBank do
|
@@ -208,9 +210,12 @@ describe Money::Bank::CurrencylayerBank do
|
|
208
210
|
delimiter: ','
|
209
211
|
}
|
210
212
|
Money::Currency.register(wtf)
|
211
|
-
subject.
|
212
|
-
|
213
|
-
|
213
|
+
Timecop.freeze(subject.rates_timestamp) do
|
214
|
+
subject.add_rate('USD', 'WTF', 2)
|
215
|
+
subject.add_rate('WTF', 'USD', 2)
|
216
|
+
subject.exchange_with(5000.to_money('WTF'), 'USD').cents
|
217
|
+
subject.exchange_with(5000.to_money('WTF'), 'USD').cents.wont_equal 0
|
218
|
+
end
|
214
219
|
end
|
215
220
|
end
|
216
221
|
|
@@ -232,9 +237,10 @@ describe Money::Bank::CurrencylayerBank do
|
|
232
237
|
@old_usd_eur_rate = 0.655
|
233
238
|
# see test/live.json +54
|
234
239
|
@new_usd_eur_rate = 0.886584
|
235
|
-
subject.add_rate('USD', 'EUR', @old_usd_eur_rate)
|
236
240
|
subject.cache = temp_cache_path
|
237
241
|
stub(subject).source_url { data_path }
|
242
|
+
subject.update_rates
|
243
|
+
subject.add_rate('USD', 'EUR', @old_usd_eur_rate)
|
238
244
|
end
|
239
245
|
|
240
246
|
after do
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: money-currencylayer-bank
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Egon Zemmer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-07-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: money
|
@@ -42,14 +42,14 @@ dependencies:
|
|
42
42
|
name: json
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '1.8'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.8'
|
55
55
|
- !ruby/object:Gem::Dependency
|
@@ -100,14 +100,14 @@ dependencies:
|
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
103
|
+
version: '12.0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
110
|
+
version: '12.0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: timecop
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -128,14 +128,14 @@ dependencies:
|
|
128
128
|
requirements:
|
129
129
|
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: 0.
|
131
|
+
version: 0.49.1
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: 0.
|
138
|
+
version: 0.49.1
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: inch
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -185,11 +185,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
185
185
|
- !ruby/object:Gem::Version
|
186
186
|
version: '0'
|
187
187
|
requirements: []
|
188
|
-
|
189
|
-
rubygems_version: 2.4.8
|
188
|
+
rubygems_version: 3.2.16
|
190
189
|
signing_key:
|
191
190
|
specification_version: 4
|
192
191
|
summary: A gem that calculates the exchange rate using published rates from currencylayer.com.
|
193
192
|
test_files:
|
194
193
|
- test/currencylayer_bank_test.rb
|
195
|
-
has_rdoc:
|