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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 2954dfb52185c910cfed6a3f3b1def920c80c847
4
- data.tar.gz: 408ba3963b1c258ce322e69237c951f34988ba1e
2
+ SHA256:
3
+ metadata.gz: b3c0fb9028cd1aeffa15e39f1ac11611a98ebc98c4388b3d7e15c3926f186ae2
4
+ data.tar.gz: 4cd0872bd7ddf5957a80b344a781656532b98c77b4eaf69f1d5b36eb6b4b4a48
5
5
  SHA512:
6
- metadata.gz: f1696528b4ac735817a8fc476b1b5a3e87512ce4e167baf36601a794ef6de6a521c0496a05f943e0ca9f643ab2f3a1361f4e5a663d32a4a684df27f1894a62fa
7
- data.tar.gz: a58368adeb7bbc501e5ef1e13f55e9deff90084b7b45f9021c1f49b9bb4241b4acfee44303e28f7002efdfc56c230547b98b149c59b3c4bcf34633a597c2e0cc
6
+ metadata.gz: cddb985e9d2f028674acff2e4f5605ff9d504396abbcec1e63cadadc3ea7a9620517e9af28d622fccf4729a70e0500918b6f9d218e38d01d42dd1fb91cc00581
7
+ data.tar.gz: b138d9a8d0ca0fa1ece3d4c576088ad7fe6e023b96fd3d21537e048764df887c871e9eebc2cfe8c425bca682aa20e63d2e1a38ebc0259611ee2a57f8bfb9e7a3
data/Gemfile CHANGED
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  gemspec
4
6
 
5
7
  group :test, :development do
6
8
  platforms :ruby_19, :jruby_19 do
7
- gem 'tins', '~> 1.6.0'
9
+ gem 'tins', '~> 1.6'
8
10
  end
9
11
  end
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2016 Phlegx Systems OG
3
+ Copyright (c) 2017 Phlegx Systems OG
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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 currencylayer bank
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
- # Return true if the expiration time is reached.
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) 2015 Phlegx Systems OG
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.gsub('http:', 'https:').freeze
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
- exchange_rates(straight).each do |exchange_rate|
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 = {}) # rubocop:disable all
135
+ def get_rate(from_currency, to_currency, opts = {})
108
136
  expire_rates!
109
- rate = super
110
- unless rate
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
- open(source_url).read
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.add_rate('USD', 'WTF', 2)
212
- subject.add_rate('WTF', 'USD', 2)
213
- subject.exchange_with(5000.to_money('WTF'), 'USD').cents.wont_equal 0
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
@@ -1,4 +1,6 @@
1
1
  # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
2
4
  require 'minitest/autorun'
3
5
  require 'rr'
4
6
  require 'money/bank/currencylayer_bank'
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.3
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: 2016-06-17 00:00:00.000000000 Z
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: '10.4'
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: '10.4'
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.37.2
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.37.2
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
- rubyforge_project:
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: