Skip to content

Commit

Permalink
New configs: Handle new and old endpoint. Rescue with cache on error.
Browse files Browse the repository at this point in the history
  • Loading branch information
phlegx committed Aug 25, 2022
1 parent d30ea77 commit a6ba272
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 35 deletions.
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ Metrics/ClassLength:
Metrics/MethodLength:
Max: 12

Metrics/LineLength:
Max: 100

Style/RedundantFreeze:
Exclude:
- 'lib/money/bank/currencylayer_bank.rb'
11 changes: 7 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ cache: bundler
before_install:
- gem update --remote bundler
rvm:
- 2.0.0
- 2.0
- 2.1
- 2.2
- 2.3.3
- 2.4.0
- jruby-9.1.7.0
- 2.3
- 2.4
- 2.5
- 2.6
- 2.7
- jruby-9.3.7.0
- ruby-head
- jruby-head
matrix:
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2017 Phlegx Systems OG
Copyright (c) 2022 Phlegx Systems OG

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,14 @@ Or install it yourself as:
# Minimal requirements.
require 'money/bank/currencylayer_bank'
mclb = Money::Bank::CurrencylayerBank.new
mclb.access_key = 'your access_key from https://currencylayer.com/product'

# New endpoint: https://apilayer.com/marketplace/currency_data-api or
# old endpoint: https://currencylayer.com/product
mclb.access_key = 'your access_key'

# (optional)
# Use the old endpoint api.currencylayer.com. By default, the new endpoint is used.
mclb.currencylayer = true

# (optional)
# Set the base currency for all rates. By default, USD is used.
Expand All @@ -80,6 +87,10 @@ mclb.ttl_in_seconds = 86400
# CurrencylayerBank only allows http as connection for the free plan users.
mclb.secure_connection = true

# (optional)
# Rescue with rates from the cache instead of reporting an error when the endpoint fails.
mclb.rescue_with_cache = true

# Define cache (string or pathname).
mclb.cache = 'path/to/file/cache'

Expand All @@ -103,9 +114,15 @@ Money.default_bank = mclb
~~~ ruby
mclb = Money::Bank::CurrencylayerBank.new

# Returns true if configured to use the old endpoint.
mclb.currencylayer

# Returns the base currency set for all rates.
mclb.source

# Returns true if configured to rescue rates from the cache.
mclb.rescue_with_cache

# Expires rates if the expiration time is reached.
mclb.expire_rates!

Expand Down
46 changes: 31 additions & 15 deletions lib/money/bank/currencylayer_bank.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,20 @@ class NoAccessKey < StandardError; end

# CurrencylayerBank base class
class CurrencylayerBank < Money::Bank::VariableExchange
# Apilayer url
URL_AL = 'http://api.apilayer.com/currency_data/live'.freeze
# CurrencylayerBank url
CL_URL = 'http://api.currencylayer.com/live'.freeze
# CurrencylayerBank secure url
CL_SECURE_URL = CL_URL.sub('http:', 'https:')
URL_CL = 'http://api.currencylayer.com/live'.freeze
# Default base currency
CL_SOURCE = 'USD'.freeze
SOURCE = 'USD'.freeze

# Use new or old endpoint.
# new: api.apilayer.com
# old: api.currencylayer.com
#
# @param value [Boolean] true for old endpoint
# @return [Boolean] chosen old endpoint if true
attr_accessor :currencylayer

# Use https to fetch rates from CurrencylayerBank
# CurrencylayerBank only allows http as connection
Expand All @@ -51,6 +59,13 @@ class CurrencylayerBank < Money::Bank::VariableExchange
# @return [String] chosen API access key
attr_accessor :access_key

# Rescue with rates from the cache instead of reporting an
# error when the endpoint fails.
#
# @param value [Boolean] true for rescue error with cache rates
# @return [Boolean] chosen rescue with cache rates
attr_accessor :rescue_with_cache

# Cache accessor, can be a String or a Proc
#
# @param value [String,Pathname,Proc] cache system
Expand Down Expand Up @@ -84,13 +99,13 @@ class CurrencylayerBank < Money::Bank::VariableExchange
# @param value [String] Currency code, ISO 3166-1 alpha-3
# @return [String] chosen base currency
def source=(value)
@source = Money::Currency.find(value.to_s).try(:iso_code) || CL_SOURCE
@source = Money::Currency.find(value.to_s).try(:iso_code) || SOURCE
end

# Get the base currency for all rates. By default, USD is used.
# @return [String] base currency
def source
@source ||= CL_SOURCE
@source ||= SOURCE
end

# Get the seconds after than the current rates are automatically expired
Expand Down Expand Up @@ -173,9 +188,10 @@ def stale?
# @return [String] the remote API url
def source_url
raise NoAccessKey if access_key.nil? || access_key.empty?
cl_url = CL_URL
cl_url = CL_SECURE_URL if secure_connection
"#{cl_url}?source=#{source}&access_key=#{access_key}"
url = "#{currencylayer ? URL_CL : URL_AL}?source=#{source}"
url = url.sub('http:', 'https:') if secure_connection
url = "#{url}&access_key=#{access_key}" if currencylayer
url
end

# Get rates expiration time based on ttl
Expand Down Expand Up @@ -243,6 +259,7 @@ def read_from_url
# @return [String] unparsed JSON content
def open_url
URI.open(source_url).read
currencylayer ? URI.open(source_url).read : URI.open(source_url, apikey: access_key).read
rescue OpenURI::HTTPError
''
end
Expand Down Expand Up @@ -270,15 +287,14 @@ def valid_rates?(text)
# @param straight [Boolean] true for straight, default is careful
# @return [Hash] key is country code (ISO 3166-1 alpha-3) value Float
def exchange_rates(straight = false)
rates = if straight
raw_rates_straight
else
raw_rates_careful
end
rates = straight ? raw_rates_straight : raw_rates_careful
if rates.key?('quotes')
@rates = rates['quotes']
elsif rates.key?('error')
raise Error, rates.dig('error', 'info')
raise Error, rates.dig('error', 'info') unless rescue_with_cache

rates = raw_rates_careful(false)
@rates = rates.key?('quotes') ? rates['quotes'] : { 'quotes' => {} }
else
raise Error, 'Unknown rates situation!'
end
Expand Down
6 changes: 3 additions & 3 deletions money-currencylayer-bank.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

Gem::Specification.new do |s|
s.name = 'money-currencylayer-bank'
s.version = '0.6.0'
s.version = '0.7.0'
s.date = Time.now.utc.strftime('%Y-%m-%d')
s.homepage = "http://github.com/phlegx/#{s.name}"
s.authors = ['Egon Zemmer']
s.email = 'office@phlegx.com'
s.description = 'A gem that calculates the exchange rate using published ' \
'rates from currencylayer.com. Compatible with the money gem.'
'rates from currencylayer.com and apilayer.com. Compatible with the money gem.'
s.summary = 'A gem that calculates the exchange rate using published rates ' \
'from currencylayer.com.'
'from currencylayer.com and apilayer.com.'
s.extra_rdoc_files = %w[README.md]
s.files = Dir['LICENSE', 'README.md', 'Gemfile', 'lib/**/*.rb',
'test/**/*']
Expand Down
2 changes: 1 addition & 1 deletion test/TEST_ACCESS_KEY
Original file line number Diff line number Diff line change
@@ -1 +1 @@
your access_key from https://currencylayer.com/product
your access_key from https://currencylayer.com/product or https://apilayer.com/marketplace/currency_data-api
44 changes: 34 additions & 10 deletions test/currencylayer_bank_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
describe Money::Bank::CurrencylayerBank do
Money.rounding_mode = BigDecimal::ROUND_HALF_EVEN
subject { Money::Bank::CurrencylayerBank.new }
let(:url) { Money::Bank::CurrencylayerBank::CL_URL }
let(:secure_url) { Money::Bank::CurrencylayerBank::CL_SECURE_URL }
let(:source) { Money::Bank::CurrencylayerBank::CL_SOURCE }
let(:url_al) { Money::Bank::CurrencylayerBank::URL_AL }
let(:url_cl) { Money::Bank::CurrencylayerBank::URL_CL }
let(:secure_url_al) { Money::Bank::CurrencylayerBank::URL_AL.sub('http:', 'https:') }
let(:secure_url_cl) { Money::Bank::CurrencylayerBank::URL_CL.sub('http:', 'https:') }
let(:source) { Money::Bank::CurrencylayerBank::SOURCE }
let(:temp_cache_path) do
File.expand_path(File.join(File.dirname(__FILE__), 'temp.json'))
end
Expand Down Expand Up @@ -161,24 +163,46 @@
end

describe '#secure_connection' do
it "should use the non-secure http url if secure_connection isn't set" do
it "should use the non-secure http apilayer.com url if secure_connection isn't set" do
subject.secure_connection = nil
subject.access_key = TEST_ACCESS_KEY
_(subject.source_url).must_equal "#{url}?source=#{source}&"\
_(subject.source_url).must_equal "#{url_al}?source=#{source}"
end

it 'should use the non-secure http apilayer.com url if secure_connection is false' do
subject.secure_connection = false
subject.access_key = TEST_ACCESS_KEY
_(subject.source_url).must_equal "#{url_al}?source=#{source}"
end

it 'should use the secure https apilayer.com url if secure_connection is set to true' do
subject.secure_connection = true
subject.access_key = TEST_ACCESS_KEY
_(subject.source_url).must_equal "#{secure_url_al}?source=#{source}"
_(subject.source_url).must_include 'https://'
end

it "should use the non-secure http currencylayer.com url if secure_connection isn't set" do
subject.secure_connection = nil
subject.currencylayer = true
subject.access_key = TEST_ACCESS_KEY
_(subject.source_url).must_equal "#{url_cl}?source=#{source}&"\
"access_key=#{TEST_ACCESS_KEY}"
end

it 'should use the non-secure http url if secure_connection is false' do
it 'should use the non-secure http currencylayer.com url if secure_connection is false' do
subject.secure_connection = false
subject.currencylayer = true
subject.access_key = TEST_ACCESS_KEY
_(subject.source_url).must_equal "#{url}?source=#{source}&"\
_(subject.source_url).must_equal "#{url_cl}?source=#{source}&"\
"access_key=#{TEST_ACCESS_KEY}"
end

it 'should use the secure https url if secure_connection is set to true' do
it 'should use the secure https currencylayer.com url if secure_connection is set to true' do
subject.secure_connection = true
subject.currencylayer = true
subject.access_key = TEST_ACCESS_KEY
_(subject.source_url).must_equal "#{secure_url}?source=#{source}&"\
_(subject.source_url).must_equal "#{secure_url_cl}?source=#{source}&"\
"access_key=#{TEST_ACCESS_KEY}"
_(subject.source_url).must_include 'https://'
end
Expand Down Expand Up @@ -223,7 +247,7 @@
describe '#access_key' do
before do
subject.cache = temp_cache_path
stub(OpenURI::OpenRead).open(url) { File.read data_path }
stub(OpenURI::OpenRead).open(url_al) { File.read data_path }
end

it 'should raise an error if no access key is set' do
Expand Down

0 comments on commit a6ba272

Please sign in to comment.