currencylayer 0.0.1
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 +7 -0
- data/lib/money/bank/currencylayer.rb +356 -0
- data/spec/currencylayer_spec.rb +115 -0
- data/spec/spec_helper.rb +4 -0
- metadata +133 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c8609a897bb711b7bfb0771651f86ca1cf76b64f
|
4
|
+
data.tar.gz: 90e220b8604e77ee6b0fb8783eb09be7c61f9261
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ff7762a4543cdcbb5cfa02ad8baa0683061d07eaaaad1c6d29c5db914f155738b39f21f92ad4d3d58084b6a2ae4e9afb52ee00358716581d75f1bfa95f0a8ea0
|
7
|
+
data.tar.gz: 0dc6dbc5b707448222178ee70287989ace921b88360f05d54c3e20fd0039ac772e6cb993aed04093cebb363530e8b36e9c9b97b2587ecd4c90d18c7599de17af
|
@@ -0,0 +1,356 @@
|
|
1
|
+
require 'money'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
# Money class, see http://github.com/RubyMoney/money
|
5
|
+
class Money
|
6
|
+
|
7
|
+
# Provides classes that aid in the ability of exchange one currency with
|
8
|
+
# another.
|
9
|
+
module Bank
|
10
|
+
|
11
|
+
# Exception that will be thrown if jsonrates.com api returns error on api request.
|
12
|
+
class RequestError < StandardError ; end
|
13
|
+
|
14
|
+
# Exception that will be thrown if access_key was not specified.
|
15
|
+
class AccessError < StandardError
|
16
|
+
# Default message.
|
17
|
+
def message
|
18
|
+
"Blank access_key! You should get your api access_key on currencylayer.com and specify it"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Money::Bank implementation that gives access to the current exchange rates using jsonrates.com api.
|
23
|
+
class Currencylayer < Money::Bank::VariableExchange
|
24
|
+
|
25
|
+
# Host of service jsonrates
|
26
|
+
SERVICE_HOST = "apilayer.net"
|
27
|
+
|
28
|
+
# Relative path of jsonrates api
|
29
|
+
SERVICE_PATH = "/api/live"
|
30
|
+
|
31
|
+
# @return [Hash] Stores the currently known rates.
|
32
|
+
attr_reader :rates
|
33
|
+
|
34
|
+
# accessor of access_key of jsonrates.com service
|
35
|
+
attr_accessor :access_key
|
36
|
+
|
37
|
+
class << self
|
38
|
+
# @return [Integer] Returns the Time To Live (TTL) in seconds.
|
39
|
+
attr_reader :ttl_in_seconds
|
40
|
+
|
41
|
+
# @return [Time] Returns the time when the rates expire.
|
42
|
+
attr_reader :rates_expiration
|
43
|
+
|
44
|
+
# @return [Boolean] Returns is Rates Careful mode set.
|
45
|
+
attr_reader :rates_careful
|
46
|
+
|
47
|
+
##
|
48
|
+
# Set Rates Careful mode
|
49
|
+
#
|
50
|
+
# @param [Boolean] value - mode Careful, if set - don't reload cache if get some exception
|
51
|
+
def rates_careful= value
|
52
|
+
@rates_careful = !!value
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Set the Time To Live (TTL) in seconds.
|
57
|
+
#
|
58
|
+
# @param [Integer] value - the seconds between an expiration and another.
|
59
|
+
def ttl_in_seconds= value
|
60
|
+
@ttl_in_seconds = value
|
61
|
+
refresh_rates_expiration! if ttl_in_seconds
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Set the rates expiration TTL seconds from the current time.
|
66
|
+
#
|
67
|
+
# @return [Time] The next expiration.
|
68
|
+
def refresh_rates_expiration!
|
69
|
+
@rates_expiration = Time.now + ttl_in_seconds
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Clears all rates stored in @rates
|
75
|
+
#
|
76
|
+
# @return [Hash] The empty @rates Hash.
|
77
|
+
#
|
78
|
+
# @example
|
79
|
+
# bank = Money::Bank::Currencylayer.new #=> <Money::Bank::Currencylayer...>
|
80
|
+
# bank.get_rate(:USD, :EUR) #=> 0.776337241
|
81
|
+
# bank.flush_rates #=> {}
|
82
|
+
def flush_rates
|
83
|
+
@mutex.synchronize{
|
84
|
+
@rates = {}
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Clears the specified rate stored in @rates.
|
90
|
+
#
|
91
|
+
# @param [String, Symbol, Currency] from Currency to convert from (used
|
92
|
+
# for key into @rates).
|
93
|
+
# @param [String, Symbol, Currency] to Currency to convert to (used for
|
94
|
+
# key into @rates).
|
95
|
+
#
|
96
|
+
# @return [Float] The flushed rate.
|
97
|
+
#
|
98
|
+
# @example
|
99
|
+
# bank = Money::Bank::Currencylayer.new #=> <Money::Bank::Currencylayer...>
|
100
|
+
# bank.get_rate(:USD, :EUR) #=> 0.776337241
|
101
|
+
# bank.flush_rate(:USD, :EUR) #=> 0.776337241
|
102
|
+
def flush_rate(from, to)
|
103
|
+
key = rate_key_for(from, to)
|
104
|
+
@mutex.synchronize{
|
105
|
+
@rates.delete(key)
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# Returns the requested rate.
|
111
|
+
#
|
112
|
+
# It uses +#get_rate_careful+ or +#get_rate_straight+ respect of @rates_careful value
|
113
|
+
#
|
114
|
+
# @param [String, Symbol, Currency] from Currency to convert from
|
115
|
+
# @param [String, Symbol, Currency] to Currency to convert to
|
116
|
+
#
|
117
|
+
# @return [Float] The requested rate.
|
118
|
+
#
|
119
|
+
# @example
|
120
|
+
# bank = Money::Bank::Currencylayer.new #=> <Money::Bank::Currencylayer...>
|
121
|
+
# bank.get_rate(:USD, :EUR) #=> 0.776337241
|
122
|
+
def get_rate(from, to)
|
123
|
+
if self.class.rates_careful
|
124
|
+
get_rate_careful(from, to)
|
125
|
+
else
|
126
|
+
get_rate_straight(from, to)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Registers a conversion rate and returns it (uses +#set_rate+).
|
131
|
+
#
|
132
|
+
# @param [Currency, String, Symbol] from Currency to exchange from.
|
133
|
+
# @param [Currency, String, Symbol] to Currency to exchange to.
|
134
|
+
# @param [Numeric] rate Rate to use when exchanging currencies.
|
135
|
+
#
|
136
|
+
# @return [Numeric]
|
137
|
+
#
|
138
|
+
# @example
|
139
|
+
# bank = Money::Bank::Currencylayer.new #=> <Money::Bank::Currencylayer...>
|
140
|
+
# bank.add_rate("USD", "CAD", 1.24515) #=> 1.24515
|
141
|
+
# bank.add_rate("CAD", "USD", 0.803115) #=> 0.803115
|
142
|
+
def add_rate from, to, rate
|
143
|
+
set_rate from, to, rate
|
144
|
+
end
|
145
|
+
|
146
|
+
# Set the rate for the given currencies. Uses +Mutex+ to synchronize data
|
147
|
+
# access.
|
148
|
+
#
|
149
|
+
# @param [Currency, String, Symbol] from Currency to exchange from.
|
150
|
+
# @param [Currency, String, Symbol] to Currency to exchange to.
|
151
|
+
# @param [Numeric] rate Rate to use when exchanging currencies.
|
152
|
+
# @param [Hash] opts Options hash to set special parameters
|
153
|
+
# @option opts [Boolean] :without_mutex disables the usage of a mutex
|
154
|
+
#
|
155
|
+
# @return [Numeric]
|
156
|
+
#
|
157
|
+
# @example
|
158
|
+
# @bank = Money::Bank::Currencylayer.new #=> <Money::Bank::Currencylayer...>
|
159
|
+
# bank.set_rate("USD", "CAD", 1.24515) #=> 1.24515
|
160
|
+
# bank.set_rate("CAD", "USD", 0.803115) #=> 0.803115
|
161
|
+
def set_rate from, to, rate
|
162
|
+
if self.class.rates_careful
|
163
|
+
set_rate_with_time(from, to, rate)
|
164
|
+
else
|
165
|
+
super
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
# Return the rate hashkey for the given currencies.
|
171
|
+
#
|
172
|
+
# @param [Currency, String, Symbol] from The currency to exchange from.
|
173
|
+
# @param [Currency, String, Symbol] to The currency to exchange to.
|
174
|
+
#
|
175
|
+
# @return [String]
|
176
|
+
#
|
177
|
+
# @example
|
178
|
+
# rate_key_for("USD", "CAD") #=> "USD_TO_CAD"
|
179
|
+
# Money::Bank::Currencylayer.rates_careful = true
|
180
|
+
# rate_key_for("USD", "CAD") #=> "USD_TO_CAD_C"
|
181
|
+
def rate_key_for(from, to)
|
182
|
+
if self.class.rates_careful
|
183
|
+
"#{Currency.wrap(from).iso_code}_TO_#{Currency.wrap(to).iso_code}_C".upcase
|
184
|
+
else
|
185
|
+
super
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
##
|
190
|
+
# Flushes all the rates if they are expired.
|
191
|
+
#
|
192
|
+
# @return [Boolean]
|
193
|
+
def expire_rates
|
194
|
+
if expired?
|
195
|
+
flush_rates
|
196
|
+
self.class.refresh_rates_expiration!
|
197
|
+
true
|
198
|
+
else
|
199
|
+
false
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
##
|
206
|
+
# Returns whether the time expired.
|
207
|
+
#
|
208
|
+
# @return [Boolean]
|
209
|
+
def expired?
|
210
|
+
self.class.ttl_in_seconds && self.class.rates_expiration <= Time.now
|
211
|
+
end
|
212
|
+
|
213
|
+
##
|
214
|
+
# Returns the requested rate.
|
215
|
+
#
|
216
|
+
# It not flushes all the rates and create rates with created_at time.
|
217
|
+
# Check expired for each rate respectively.
|
218
|
+
# If it can't get new rate by some reason it returns cached value.
|
219
|
+
#
|
220
|
+
# @param [String, Symbol, Currency] from Currency to convert from
|
221
|
+
# @param [String, Symbol, Currency] to Currency to convert to
|
222
|
+
#
|
223
|
+
# @return [Float] The requested rate.
|
224
|
+
def get_rate_careful(from, to)
|
225
|
+
|
226
|
+
rate_key = rate_key_for(from, to)
|
227
|
+
rate_cached = @rates[rate_key]
|
228
|
+
|
229
|
+
if rate_cached.nil? || expired_time?(rate_cached[:created_at])
|
230
|
+
set_rate_with_time(from, to, fetch_rate(from, to))
|
231
|
+
@rates[rate_key][:rate]
|
232
|
+
else
|
233
|
+
rate_cached[:rate]
|
234
|
+
end
|
235
|
+
rescue RequestError => e
|
236
|
+
if rate_cached.nil?
|
237
|
+
raise e
|
238
|
+
else
|
239
|
+
rate_cached[:rate]
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
##
|
244
|
+
# Returns the requested rate.
|
245
|
+
#
|
246
|
+
# It also flushes all the rates when and if they are expired.
|
247
|
+
#
|
248
|
+
# @param [String, Symbol, Currency] from Currency to convert from
|
249
|
+
# @param [String, Symbol, Currency] to Currency to convert to
|
250
|
+
#
|
251
|
+
# @return [Float] The requested rate.
|
252
|
+
def get_rate_straight(from, to)
|
253
|
+
expire_rates
|
254
|
+
|
255
|
+
@mutex.synchronize{
|
256
|
+
@rates[rate_key_for(from, to)] ||= fetch_rate(from, to)
|
257
|
+
}
|
258
|
+
end
|
259
|
+
|
260
|
+
# Registers a conversion rate with created_at
|
261
|
+
# and returns it (uses +#set_rate_with_time+).
|
262
|
+
#
|
263
|
+
# @param [Currency, String, Symbol] from Currency to exchange from.
|
264
|
+
# @param [Currency, String, Symbol] to Currency to exchange to.
|
265
|
+
# @param [Numeric] rate Rate to use when exchanging currencies.
|
266
|
+
#
|
267
|
+
# @return [Numeric]
|
268
|
+
def add_rate_with_time(from, to, rate)
|
269
|
+
set_rate_with_time(from, to, rate)
|
270
|
+
end
|
271
|
+
|
272
|
+
# Set the rate and created_at time for the given currencies.
|
273
|
+
# Uses +Mutex+ to synchronize data access.
|
274
|
+
#
|
275
|
+
# @param [Currency, String, Symbol] from Currency to exchange from.
|
276
|
+
# @param [Currency, String, Symbol] to Currency to exchange to.
|
277
|
+
# @param [Numeric] rate Rate to use when exchanging currencies.
|
278
|
+
# @param [Hash] opts Options hash to set special parameters
|
279
|
+
# @option opts [Boolean] :without_mutex disables the usage of a mutex
|
280
|
+
#
|
281
|
+
# @return [Numeric]
|
282
|
+
def set_rate_with_time(from, to, rate)
|
283
|
+
rate_d = BigDecimal.new(rate.to_s)
|
284
|
+
@mutex.synchronize {
|
285
|
+
@rates[rate_key_for(from, to)] = {rate: rate_d, created_at: Time.now}
|
286
|
+
}
|
287
|
+
rate_d
|
288
|
+
end
|
289
|
+
|
290
|
+
##
|
291
|
+
# Check if time is expired
|
292
|
+
#
|
293
|
+
# @param [Time] time Time to check
|
294
|
+
#
|
295
|
+
# @return [Boolean] Is the time expired.
|
296
|
+
def expired_time? time
|
297
|
+
time + self.class.ttl_in_seconds.to_i < Time.now
|
298
|
+
end
|
299
|
+
|
300
|
+
##
|
301
|
+
# Queries for the requested rate and returns it.
|
302
|
+
#
|
303
|
+
# @param [String, Symbol, Currency] from Currency to convert from
|
304
|
+
# @param [String, Symbol, Currency] to Currency to convert to
|
305
|
+
#
|
306
|
+
# @return [BigDecimal] The requested rate.
|
307
|
+
def fetch_rate(from, to)
|
308
|
+
from, to = Currency.wrap(from), Currency.wrap(to)
|
309
|
+
uri = build_uri(from, to)
|
310
|
+
data = perform_request(uri)
|
311
|
+
extract_rate(data, from, to)
|
312
|
+
end
|
313
|
+
|
314
|
+
##
|
315
|
+
# Performs request on uri or raise exception message with RequestError
|
316
|
+
#
|
317
|
+
# @param [String] uri Requested uri
|
318
|
+
#
|
319
|
+
# @return [String]
|
320
|
+
def perform_request(uri)
|
321
|
+
uri.read
|
322
|
+
rescue Exception => e
|
323
|
+
raise RequestError, e.message
|
324
|
+
end
|
325
|
+
|
326
|
+
##
|
327
|
+
# Build a URI for the given arguments.
|
328
|
+
#
|
329
|
+
# @param [Currency] from The currency to convert from.
|
330
|
+
# @param [Currency] to The currency to convert to.
|
331
|
+
#
|
332
|
+
# @return [URI::HTTP]
|
333
|
+
def build_uri(from, to)
|
334
|
+
raise AccessError if access_key.nil? || access_key.empty?
|
335
|
+
uri = URI::HTTP.build(
|
336
|
+
:host => SERVICE_HOST,
|
337
|
+
:path => SERVICE_PATH,
|
338
|
+
:query => "access_key=#{access_key}&source=#{from}¤cies=#{to}"
|
339
|
+
)
|
340
|
+
end
|
341
|
+
|
342
|
+
##
|
343
|
+
# Takes the response from jsonrates.com and extract the rate.
|
344
|
+
#
|
345
|
+
# @param [String] data HTTP-Response of api.
|
346
|
+
#
|
347
|
+
# @return [BigDecimal]
|
348
|
+
def extract_rate(data, from, to)
|
349
|
+
request_hash = JSON.parse(data)
|
350
|
+
success = request_hash['success']
|
351
|
+
raise RequestError, request_hash['error']['info'] unless success
|
352
|
+
BigDecimal.new(request_hash['quotes']["#{from}#{to}"].to_s)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Currencylayer" do
|
4
|
+
before :each do
|
5
|
+
@bank = Money::Bank::Currencylayer.new
|
6
|
+
@bank.access_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should accept a ttl_in_seconds option" do
|
10
|
+
Money::Bank::Currencylayer.ttl_in_seconds = 86400
|
11
|
+
expect(Money::Bank::Currencylayer.ttl_in_seconds).to eq(86400)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '.get_rate' do
|
15
|
+
it 'returns rate' do
|
16
|
+
uri = @bank.send(:build_uri, 'USD', 'EUR').to_s
|
17
|
+
stub_request(:get, uri).to_return( :status => 200,
|
18
|
+
:body => '{"success":true,"terms":"https:\/\/currencylayer.com\/terms","privacy":"https:\/\/currencylayer.com\/privacy","timestamp":1434443053,"source":"USD","quotes":{"USDEUR":0.887701}}')
|
19
|
+
|
20
|
+
@bank.flush_rates
|
21
|
+
rate = @bank.get_rate('USD', 'EUR')
|
22
|
+
expect(rate).to eq(BigDecimal.new("0.887701"))
|
23
|
+
end
|
24
|
+
context "in careful mode" do
|
25
|
+
|
26
|
+
it "don't flush rate if get some exception on request" do
|
27
|
+
|
28
|
+
Money::Bank::Currencylayer.rates_careful = true
|
29
|
+
Money::Bank::Currencylayer.ttl_in_seconds = 0
|
30
|
+
|
31
|
+
@bank.flush_rates
|
32
|
+
@bank.add_rate('USD', 'EUR', 1.011)
|
33
|
+
|
34
|
+
|
35
|
+
uri = @bank.send(:build_uri, 'USD', 'EUR').to_s
|
36
|
+
|
37
|
+
stub_request(:get, uri).to_return(:status => 200, :body => '{"success":false,"error":{"code":202,"info":"You have provided one or more invalid Currency Codes. [Required format: currencies=EUR,USD,GBP,...]"}}')
|
38
|
+
rate = @bank.get_rate('USD', 'EUR')
|
39
|
+
expect(rate).to eq(BigDecimal.new("1.011"))
|
40
|
+
|
41
|
+
Money::Bank::Currencylayer.rates_careful = false
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe ".refresh_rates_expiration!" do
|
48
|
+
it "set the .rates_expiration using the TTL and the current time" do
|
49
|
+
Money::Bank::Currencylayer.ttl_in_seconds = 86400
|
50
|
+
new_time = Time.now
|
51
|
+
Timecop.freeze(new_time)
|
52
|
+
Money::Bank::Currencylayer.refresh_rates_expiration!
|
53
|
+
expect(Money::Bank::Currencylayer.rates_expiration).to eq(new_time + 86400)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe ".flush_rates" do
|
58
|
+
it "should empty @rates" do
|
59
|
+
@bank.add_rate("USD", "CAD", 1.24515)
|
60
|
+
@bank.flush_rates
|
61
|
+
expect(@bank.rates).to eq({})
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe 'careful mode' do
|
66
|
+
it 'returns cached value if exception raised' do
|
67
|
+
@bank.flush_rates
|
68
|
+
@bank.add_rate("USD", "CAD", 32.231)
|
69
|
+
expect(@bank.get_rate("USD", "CAD")).to eq (BigDecimal.new('32.231'))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe ".flush_rate" do
|
74
|
+
it "should remove a specific rate from @rates" do
|
75
|
+
@bank.flush_rates
|
76
|
+
@bank.add_rate('USD', 'EUR', 1.4)
|
77
|
+
@bank.add_rate('USD', 'JPY', 0.3)
|
78
|
+
@bank.flush_rate('USD', 'EUR')
|
79
|
+
expect(@bank.rates).to include('USD_TO_JPY')
|
80
|
+
expect(@bank.rates).to_not include('USD_TO_EUR')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe ".expire_rates" do
|
85
|
+
before do
|
86
|
+
Money::Bank::Currencylayer.ttl_in_seconds = 1000
|
87
|
+
end
|
88
|
+
|
89
|
+
context "when the ttl has expired" do
|
90
|
+
before do
|
91
|
+
new_time = Time.now + 1001
|
92
|
+
Timecop.freeze(new_time)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should flush all rates" do
|
96
|
+
expect(@bank).to receive(:flush_rates)
|
97
|
+
@bank.expire_rates
|
98
|
+
end
|
99
|
+
|
100
|
+
it "updates the next expiration time" do
|
101
|
+
exp_time = Time.now + 1000
|
102
|
+
|
103
|
+
@bank.expire_rates
|
104
|
+
expect(Money::Bank::Currencylayer.rates_expiration).to eq(exp_time)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context "when the ttl has not expired" do
|
109
|
+
it "not should flush all rates" do
|
110
|
+
expect(@bank).to_not receive(:flush_rates)
|
111
|
+
@bank.expire_rates
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: currencylayer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrey Skuratovsky
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: money
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 6.5.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 6.5.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.0.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: timecop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: webmock
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.7'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.7'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '10.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '10.0'
|
97
|
+
description: This gem extends Money::Bank::VariableExchange with Money::Bank::Currencylayer
|
98
|
+
and gives you access to the current exchange rates on currencylayer.com.
|
99
|
+
email:
|
100
|
+
- skuratowsky@gmail.com
|
101
|
+
executables: []
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- lib/money/bank/currencylayer.rb
|
106
|
+
- spec/currencylayer_spec.rb
|
107
|
+
- spec/spec_helper.rb
|
108
|
+
homepage: http://github.com/askuratovsky/currencylayer
|
109
|
+
licenses:
|
110
|
+
- MIT
|
111
|
+
metadata: {}
|
112
|
+
post_install_message:
|
113
|
+
rdoc_options: []
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
requirements: []
|
127
|
+
rubyforge_project:
|
128
|
+
rubygems_version: 2.4.3
|
129
|
+
signing_key:
|
130
|
+
specification_version: 4
|
131
|
+
summary: Access to the currencylayer.com online exchange rates
|
132
|
+
test_files: []
|
133
|
+
has_rdoc:
|