omni_exchange 0.2.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.rubocop_todo.yml +2 -2
- data/Gemfile.lock +7 -1
- data/README.md +21 -11
- data/bin/console +2 -0
- data/lib/omni_exchange/error.rb +15 -0
- data/lib/omni_exchange/provider.rb +3 -9
- data/lib/omni_exchange/providers/open_exchange_rates.rb +2 -2
- data/lib/omni_exchange/providers/xe.rb +7 -1
- data/lib/omni_exchange/version.rb +1 -1
- data/lib/omni_exchange.rb +26 -38
- data/omni_exchange.gemspec +1 -0
- metadata +17 -3
- data/lib/omni_exchange/currency_data.json +0 -2719
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79069dcd87cf3788896dbc2093d114028b94592743a5fea03559cfce974b4d5c
|
4
|
+
data.tar.gz: 03a24de2dcfe6dca9ee1c0532e40b8bab6addbc14c9e4863ef644789a0a24adb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb380807a18d884f6ebef61b911e9a5301f6704577a15b8b4fc75cc11a4e0337ee1c911ea01c9a8269f919f6df62f86225cc9270bf24c8cad753eed0cefa8e2d
|
7
|
+
data.tar.gz: f0909dabbacc96857512b405cc590431ca9df724f622d0e59d589c62e2856e2d6193e992370f4a06d52df2eff7c3f6d1acca1741f5d6c12c9be20c9736b6eef0
|
data/.gitignore
CHANGED
data/.rubocop_todo.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2022-06-
|
3
|
+
# on 2022-06-30 01:49:03 UTC using RuboCop version 0.93.1.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
@@ -15,7 +15,7 @@ Metrics/AbcSize:
|
|
15
15
|
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods.
|
16
16
|
# ExcludedMethods: refine
|
17
17
|
Metrics/BlockLength:
|
18
|
-
Max:
|
18
|
+
Max: 27
|
19
19
|
|
20
20
|
# Offense count: 1
|
21
21
|
Naming/AccessorMethodName:
|
data/Gemfile.lock
CHANGED
@@ -1,19 +1,25 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
omni_exchange (
|
4
|
+
omni_exchange (1.2.0)
|
5
5
|
faraday (= 0.17.4)
|
6
|
+
money (~> 6.13.1)
|
6
7
|
|
7
8
|
GEM
|
8
9
|
remote: https://rubygems.org/
|
9
10
|
specs:
|
10
11
|
ast (2.4.2)
|
11
12
|
coderay (1.1.3)
|
13
|
+
concurrent-ruby (1.1.10)
|
12
14
|
diff-lcs (1.5.0)
|
13
15
|
dotenv (2.7.6)
|
14
16
|
faraday (0.17.4)
|
15
17
|
multipart-post (>= 1.2, < 3)
|
18
|
+
i18n (1.10.0)
|
19
|
+
concurrent-ruby (~> 1.0)
|
16
20
|
method_source (1.0.0)
|
21
|
+
money (6.13.8)
|
22
|
+
i18n (>= 0.6.4, <= 2)
|
17
23
|
multipart-post (2.2.3)
|
18
24
|
parallel (1.22.1)
|
19
25
|
parser (3.1.2.0)
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
OmniExchange converts currencies using up-to-the-minute foreign exchange rates.
|
4
4
|
|
5
|
-
OmniExchange also supports fail-over logic and handles timeouts. In other words, if currency conversion isn't possible because an API data source cannot provide an exchange rate, OR if that data source times out, OmniExchange will retrieve exchange rate data from another API data source.
|
5
|
+
OmniExchange also supports fail-over logic and handles timeouts. In other words, if currency conversion isn't possible because an API data source cannot provide an exchange rate, OR if that data source times out, OmniExchange will retrieve exchange rate data seamlessly from another API data source.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
@@ -46,24 +46,34 @@ OmniExchange.configure do |config|
|
|
46
46
|
end
|
47
47
|
```
|
48
48
|
|
49
|
-
#### **Step 3) Convert Currency**
|
49
|
+
#### **Step 3) Convert Currency and/or Get An Exchange Rate**
|
50
50
|
|
51
|
-
To convert currency, all you have to do is call `OmniExchange.
|
52
|
-
1. amount: (Integer)the amount of the currency you want to convert. NOTE: OmniExchange will read this amount as being the smallest unit of a currency. In other words, if you pass `10` as the amount for USD, OmniExchange will read this as 10 cents, not 10 dollars.
|
51
|
+
To convert currency and/or get an exchange rate, all you have to do is call `OmniExchange.get_fx_data()`. This method requires you to pass the following four named parameters:
|
52
|
+
1. amount: (Integer) the amount of the currency you want to convert. NOTE: OmniExchange will read this amount as being the smallest unit of a currency. In other words, if you pass `10` as the amount for USD, OmniExchange will read this as 10 cents, not 10 dollars.
|
53
53
|
2. base_currency: (String) the ISO Currency Code of the currency that you're exchanging from. ie. 'USD', 'JPY'
|
54
54
|
3. target_currency: (String) the ISO Currency Code of the currency that you're exchanging to. ie. 'EUR', 'KRW'
|
55
|
-
4. providers: (Array) the keys of the API providers that you want data from in order of preference. ie. [:xe, :open_exchange_rates]
|
55
|
+
4. providers: (Array of Symbols) the keys of the API providers that you want data from in order of preference. ie. [:xe, :open_exchange_rates]
|
56
56
|
|
57
|
-
|
57
|
+
What you get back is a hash containing:
|
58
|
+
1. converted_amount: (BigDecimal) the amount of money exchanged from the base currency to the target currency
|
59
|
+
2. exchange_rate: (BigDecimal) the rate used to calculate the converted_amount
|
60
|
+
3. provider_class: (Class) the name of the provider class that supplied the exchange_rate (ie. OmniExchange::OpenExhangeRates)
|
58
61
|
|
62
|
+
[For the sake of precise calculation](https://www.bigbinary.com/blog/handling-money-in-ruby), converted_amount and exchange_rate are BigDecimal. Simply call `.to_f` to the results if you'd like to see a number that is easier to read.
|
59
63
|
|
60
|
-
|
64
|
+
|
65
|
+
Here is an example. Lets say I want to convert $1.00 US Dollar to Japanese Yen, and I want it converted using exchange rate data from Open Exchange Rates. If Open Exchange Rates fails, I'd like OmniExchange to try to use exchange rate data from Xe as a fallback.
|
61
66
|
|
62
67
|
```ruby
|
63
|
-
USD_to_JPY = OmniExchange.
|
68
|
+
USD_to_JPY = OmniExchange.get_fx_data(amount: 100, base_currency: 'USD', target_currency: 'JPY', providers: [:open_exchange_rates, :xe])
|
69
|
+
|
70
|
+
puts USD_to_JPY # => { :converted_amount=>0.13566633333e3, :exchange_rate=>0.13566633333e1, :provider_class=>OmniExchange::OpenExchangeRates }
|
71
|
+
|
72
|
+
puts USD_to_JPY[:converted_amount] # => 0.13566633333e3
|
73
|
+
puts USD_to_JPY[:converted_amount].to_f # => 135.66633333
|
64
74
|
|
65
|
-
puts USD_to_JPY # => 0.
|
66
|
-
puts USD_to_JPY.to_f # =>
|
75
|
+
puts USD_to_JPY[:exchange_rate] # => 0.13566633333e1
|
76
|
+
puts USD_to_JPY[:exchange_rate].to_f # => 1.3566633333
|
67
77
|
|
68
78
|
```
|
69
79
|
|
@@ -75,7 +85,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
75
85
|
|
76
86
|
## Contributing
|
77
87
|
|
78
|
-
Bug reports and
|
88
|
+
Bug reports and issues are welcome on GitHub at https://github.com/degica/omni_exchange. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/degica/omni_exchange/blob/master/CODE_OF_CONDUCT.md).
|
79
89
|
|
80
90
|
## License
|
81
91
|
|
data/bin/console
CHANGED
@@ -5,8 +5,10 @@ require 'bundler/setup'
|
|
5
5
|
require 'omni_exchange'
|
6
6
|
require 'dotenv/load'
|
7
7
|
require 'faraday'
|
8
|
+
require 'money'
|
8
9
|
require_relative '../lib/omni_exchange/provider'
|
9
10
|
require_relative '../lib/omni_exchange/configuration'
|
11
|
+
require_relative '../lib/omni_exchange/error'
|
10
12
|
require_relative '../lib/omni_exchange/providers/xe'
|
11
13
|
require_relative '../lib/omni_exchange/providers/open_exchange_rates'
|
12
14
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniExchange
|
4
|
+
# A custom error for an unknown or invalid currency
|
5
|
+
class UnknownCurrency < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
# A custom error for failure to get data from a provider
|
9
|
+
class HttpError < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
# A custom error for exceeding the monthly request limit for xe.com
|
13
|
+
class XeMonthlyLimit < StandardError
|
14
|
+
end
|
15
|
+
end
|
@@ -4,7 +4,6 @@ module OmniExchange
|
|
4
4
|
class Provider
|
5
5
|
# @providers is a hash of registered providers that OmniExchange can request exchange rates data from
|
6
6
|
@providers = {}
|
7
|
-
@currency_data = nil
|
8
7
|
|
9
8
|
# This method registers providers by adding a provider's name as a key and a provider class as a value to
|
10
9
|
# @providers. This happens automatically on load at the top of the lib/omni_exchange.rb file when each
|
@@ -43,18 +42,13 @@ module OmniExchange
|
|
43
42
|
|
44
43
|
# Some currencies, such as the US dollar, have subunits (ie. cents). Therefore, to make sure that currencies are
|
45
44
|
# exchanged accurately, a currency's subunit needs to be taken into account, and that's what this method does.
|
46
|
-
# Subunit data
|
45
|
+
# Subunit data is easily found through use of the RubyMoney gem.
|
47
46
|
#
|
48
47
|
# @param base_currency [String] the ISO Currency Code of the currency that you're exchanging from. A check is done
|
49
48
|
# on this currency to see if it has subunits (such as the US dollar having cents). ie. "USD", "JPY"
|
50
|
-
# @return [Float] the amount an exchange rate should be multiplied by to account for subunits
|
49
|
+
# @return [Float] the amount an exchange rate should be multiplied by to account for a currency's potential subunits
|
51
50
|
def self.get_currency_unit(base_currency)
|
52
|
-
|
53
|
-
|
54
|
-
file = File.read(File.join(File.dirname(__FILE__), './currency_data.json'))
|
55
|
-
@currency_data = JSON.parse(file)
|
56
|
-
|
57
|
-
1.0 / @currency_data[base_currency.downcase]['subunit_to_unit']
|
51
|
+
1.0 / Money::Currency.wrap(base_currency).subunit_to_unit
|
58
52
|
end
|
59
53
|
end
|
60
54
|
end
|
@@ -33,9 +33,9 @@ module OmniExchange
|
|
33
33
|
|
34
34
|
exchange_rate = JSON.parse(response.body, symbolize_names: true)[:rates][target_currency.to_sym].to_d
|
35
35
|
|
36
|
-
currency_unit = get_currency_unit(base_currency)
|
36
|
+
currency_unit = get_currency_unit(base_currency).to_d
|
37
37
|
|
38
|
-
exchange_rate * currency_unit
|
38
|
+
(exchange_rate * currency_unit).to_d
|
39
39
|
end
|
40
40
|
|
41
41
|
# when this file is required at the top of lib/omni_exchange.rb, this method call is run and allows
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# rubocop:disable Metrics/AbcSize
|
3
4
|
require 'omni_exchange'
|
4
5
|
|
5
6
|
module OmniExchange
|
@@ -36,7 +37,11 @@ module OmniExchange
|
|
36
37
|
raise e.class, 'xe.com has timed out.'
|
37
38
|
end
|
38
39
|
|
39
|
-
JSON.parse(response.body, symbolize_names: true)
|
40
|
+
body = JSON.parse(response.body, symbolize_names: true)
|
41
|
+
|
42
|
+
raise OmniExchange::XeMonthlyLimit, 'Xe.com monthly limit has been exceeded' if body[:code] == 3
|
43
|
+
|
44
|
+
body[:to][0][:mid].to_d
|
40
45
|
end
|
41
46
|
|
42
47
|
# when this file is required at the top of lib/omni_exchange.rb, this method call is run and allows
|
@@ -44,3 +49,4 @@ module OmniExchange
|
|
44
49
|
OmniExchange::Provider.register_provider(:xe, self)
|
45
50
|
end
|
46
51
|
end
|
52
|
+
# rubocop:enable Metrics/AbcSize
|
data/lib/omni_exchange.rb
CHANGED
@@ -7,7 +7,9 @@ require 'omni_exchange/providers/open_exchange_rates'
|
|
7
7
|
require 'omni_exchange/providers/xe'
|
8
8
|
require 'omni_exchange/version'
|
9
9
|
require 'omni_exchange/configuration'
|
10
|
+
require 'omni_exchange/error'
|
10
11
|
require 'faraday'
|
12
|
+
require 'money'
|
11
13
|
require 'json'
|
12
14
|
require 'bigdecimal/util'
|
13
15
|
|
@@ -32,8 +34,9 @@ module OmniExchange
|
|
32
34
|
|
33
35
|
module_function
|
34
36
|
|
35
|
-
# returns the amount of money in one country's currency when exchanged from an
|
36
|
-
# currency using exchange rates data from API providers
|
37
|
+
# returns foreign exchange data including the amount of money in one country's currency when exchanged from an
|
38
|
+
# amount of money of another country's currency using exchange rates data from API providers, the exchange
|
39
|
+
# rate used to calculate that amount, and the API provider that supplied that rate.
|
37
40
|
#
|
38
41
|
# @param amount: [Integer, #to_d] the amount to exchange (in cents, if applicable to the currency). ie. 1, 10, 100
|
39
42
|
# @param base_currency: [String] the ISO Currency Code of the currency that you're exchanging from. ie. "USD", "JPY"
|
@@ -41,41 +44,24 @@ module OmniExchange
|
|
41
44
|
# @param providers: [Array] an array of symbols of the providers that will be used to get exchange rates API
|
42
45
|
# data. The symbols must be found in the @providers hash in the Provider class (lib/omni_exchange/provider.rb).
|
43
46
|
# ie. xe:, :open_exchange_rates
|
44
|
-
# @return [
|
47
|
+
# @return [Hash] If all of the providers in the providers hash fail to retrieve data, or if one of the currencies
|
48
|
+
# is not valid, an exception is raised.
|
49
|
+
# * :converted_amount [BigDecimal] the amount of money exchanged from the base currency to the target
|
50
|
+
# currency as a BigDecimal for precice calculation. ie. 1, 10, 100
|
51
|
+
# * :exchange_rate [BigDecimal] the rate used to calculate the converted_amount as a BigDecimal. ie. 0.95211e1
|
52
|
+
# * :provider_class [Class] the provider class that supplied the exchange_rate data. ie. OmniExchange::Xe
|
53
|
+
#the amount of the base currency exchanged to the target currency using an exchange rate
|
45
54
|
# provided by one of the data providers in the providers hash. The final amount is returned as a BigDecimal
|
46
|
-
# for precise calculation. If all of the providers in the providers hash fail to retrieve data,
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
# Gracefully hit each provider and fail-over to the next one
|
55
|
-
provider_classes.each do |klass|
|
56
|
-
rate = klass.get_exchange_rate(base_currency: base_currency, target_currency: target_currency)
|
57
|
-
|
58
|
-
return rate * amount.to_d
|
59
|
-
rescue Faraday::Error, Faraday::ConnectionFailed => e
|
60
|
-
error_messages << e.inspect
|
55
|
+
# for precise calculation. If all of the providers in the providers hash fail to retrieve data, an exception is raised.
|
56
|
+
def get_fx_data(amount:, base_currency:, target_currency:, providers:)
|
57
|
+
# if one of the currencies is not valid (ie. 'fake_crypto'), an exception is raised.
|
58
|
+
begin
|
59
|
+
Money::Currency.wrap(base_currency)
|
60
|
+
Money::Currency.wrap(target_currency)
|
61
|
+
rescue Money::Currency::UnknownCurrency => exception
|
62
|
+
raise OmniExchange::UnknownCurrency, "#{exception}"
|
61
63
|
end
|
62
|
-
|
63
|
-
raise "Failed to load #{base_currency}->#{target_currency}:\n#{exception_messages.join("\n")}"
|
64
|
-
end
|
65
64
|
|
66
|
-
# This method returns the exchange rate, the rate at which the smallest unit of one currency (the base currency)
|
67
|
-
# will be exchanged for another currency (the target currency), from API providers
|
68
|
-
#
|
69
|
-
# @param base_currency: [String] the ISO Currency Code of the currency that you're exchanging from. ie. "USD", "JPY"
|
70
|
-
# @param target_currency: [String] the ISO Currency Code of the currency that you're exchanging to. ie. "EUR", "KRW"
|
71
|
-
# @param providers: [Array] an array of symbols of the providers that will be used to get exchange rates API
|
72
|
-
# data. The symbols must be found in the @providers hash in the Provider class (lib/omni_exchange/provider.rb).
|
73
|
-
# ie. xe:, :open_exchange_rates
|
74
|
-
# @return [BigDecimal] the amount of the base currency exchanged to the target currency using an exchange rate
|
75
|
-
# provided by one of the data providers in the providers hash. The final amount is returned as a BigDecimal
|
76
|
-
# for precise calculation. If all of the providers in the providers hash fail to retrieve data,
|
77
|
-
# an exception is raised.
|
78
|
-
def get_exchange_rate(base_currency:, target_currency:, providers:)
|
79
65
|
error_messages = []
|
80
66
|
|
81
67
|
# Make sure all providers passed exist. If not, a LoadError is raise and not rescued
|
@@ -84,13 +70,15 @@ module OmniExchange
|
|
84
70
|
# Gracefully hit each provider and fail-over to the next one
|
85
71
|
provider_classes.each do |klass|
|
86
72
|
rate = klass.get_exchange_rate(base_currency: base_currency, target_currency: target_currency)
|
87
|
-
|
88
|
-
|
89
|
-
|
73
|
+
|
74
|
+
exchanged_amount = rate.to_d * amount.to_d
|
75
|
+
|
76
|
+
return { converted_amount: exchanged_amount, exchange_rate: rate, provider_class: klass }
|
77
|
+
rescue Faraday::Error, Faraday::ConnectionFailed, OmniExchange::XeMonthlyLimit => e
|
90
78
|
error_messages << e.inspect
|
91
79
|
end
|
92
80
|
|
93
|
-
raise "Failed to load #{base_currency}->#{target_currency}:\n#{
|
81
|
+
raise OmniExchange::HttpError, "Failed to load #{base_currency}->#{target_currency}:\n#{error_messages.join("\n")}"
|
94
82
|
end
|
95
83
|
end
|
96
84
|
# rubocop:enable Lint/Syntax
|
data/omni_exchange.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omni_exchange
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yun Chung
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-07-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.17.4
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: money
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 6.13.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 6.13.1
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: dotenv
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -132,7 +146,7 @@ files:
|
|
132
146
|
- bin/setup
|
133
147
|
- lib/omni_exchange.rb
|
134
148
|
- lib/omni_exchange/configuration.rb
|
135
|
-
- lib/omni_exchange/
|
149
|
+
- lib/omni_exchange/error.rb
|
136
150
|
- lib/omni_exchange/provider.rb
|
137
151
|
- lib/omni_exchange/providers/open_exchange_rates.rb
|
138
152
|
- lib/omni_exchange/providers/xe.rb
|