currency-rate 2.0.0 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- metadata +8 -119
- data/.document +0 -5
- data/.gitignore +0 -20
- data/.rspec +0 -1
- data/Gemfile +0 -6
- data/Gemfile.lock +0 -68
- data/LICENSE.txt +0 -20
- data/README.md +0 -73
- data/Rakefile +0 -43
- data/currency-rate.gemspec +0 -41
- data/lib/adapter.rb +0 -100
- data/lib/adapters/crypto/binance_adapter.rb +0 -42
- data/lib/adapters/crypto/bitfinex_adapter.rb +0 -41
- data/lib/adapters/crypto/bitpay_adapter.rb +0 -29
- data/lib/adapters/crypto/bitstamp_adapter.rb +0 -26
- data/lib/adapters/crypto/coin_market_cap_adapter.rb +0 -29
- data/lib/adapters/crypto/coinbase_adapter.rb +0 -30
- data/lib/adapters/crypto/exmo_adapter.rb +0 -27
- data/lib/adapters/crypto/hit_BTC_adapter.rb +0 -64
- data/lib/adapters/crypto/huobi_adapter.rb +0 -25
- data/lib/adapters/crypto/kraken_adapter.rb +0 -46
- data/lib/adapters/crypto/localbitcoins_adapter.rb +0 -25
- data/lib/adapters/crypto/okcoin_adapter.rb +0 -17
- data/lib/adapters/crypto/paxful_adapter.rb +0 -18
- data/lib/adapters/crypto/poloniex_adapter.rb +0 -33
- data/lib/adapters/crypto/yadio_adapter.rb +0 -19
- data/lib/adapters/fiat/bonbast_adapter.rb +0 -23
- data/lib/adapters/fiat/coinmonitor_adapter.rb +0 -13
- data/lib/adapters/fiat/currency_layer_adapter.rb +0 -31
- data/lib/adapters/fiat/fixer_adapter.rb +0 -17
- data/lib/adapters/fiat/forge_adapter.rb +0 -34
- data/lib/adapters/fiat/free_forex_adapter.rb +0 -36
- data/lib/container.rb +0 -203
- data/lib/currency_rate.rb +0 -41
- data/lib/currency_rate/version.rb +0 -3
- data/lib/exceptions.rb +0 -9
- data/lib/storage/file_storage.rb +0 -34
- data/lib/storage/serializers/yaml_serializer.rb +0 -15
- data/lib/utils/string_extensions.rb +0 -19
@@ -1,25 +0,0 @@
|
|
1
|
-
module CurrencyRate
|
2
|
-
class LocalbitcoinsAdapter < Adapter
|
3
|
-
# No need to use it for fetching, just additional information about supported currencies
|
4
|
-
SUPPORTED_CURRENCIES = %w(
|
5
|
-
AED AOA ARS AUD BDT BRL BYN CAD CHF CLP CNY COP CRC CZK DKK DOP EGP ETH
|
6
|
-
EUR GBP GEL GHS HKD HUF IDR ILS INR IRR JOD JPY KES KRW KWD KZT LKR LTC
|
7
|
-
MAD MWK MXN MYR NGN NOK NZD OMR PAB PEN PHP PKR PLN QAR RON RSD RUB RWF
|
8
|
-
SAR SEK SGD SZL THB TRY TWD TZS UAH UGX USD UYU VES VND XAF XMR XOF XRP
|
9
|
-
ZAR ZMW
|
10
|
-
)
|
11
|
-
|
12
|
-
ANCHOR_CURRENCY = "BTC"
|
13
|
-
|
14
|
-
FETCH_URL = 'https://localbitcoins.com/bitcoinaverage/ticker-all-currencies/'
|
15
|
-
|
16
|
-
def normalize(data)
|
17
|
-
return nil unless data = super
|
18
|
-
data.reduce({ "anchor" => ANCHOR_CURRENCY }) do |result, (fiat, value)|
|
19
|
-
result["#{fiat.upcase}"] = BigDecimal(value["rates"]["last"].to_s)
|
20
|
-
result
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module CurrencyRate
|
2
|
-
class OkcoinAdapter < Adapter
|
3
|
-
FETCH_URL = {
|
4
|
-
'LTC_USD' => 'https://www.okcoin.com/api/spot/v3/instruments/LTC-USD/ticker',
|
5
|
-
'BTC_USD' => 'https://www.okcoin.com/api/spot/v3/instruments/BTC-USD/ticker',
|
6
|
-
}
|
7
|
-
|
8
|
-
def normalize(data)
|
9
|
-
return nil unless data = super
|
10
|
-
data.reduce({}) do |result, (pair, value)|
|
11
|
-
result[pair] = BigDecimal(value["last"].to_s)
|
12
|
-
result
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module CurrencyRate
|
2
|
-
class PaxfulAdapter < Adapter
|
3
|
-
SUPPORTED_CURRENCIES = %w(USD)
|
4
|
-
|
5
|
-
ANCHOR_CURRENCY = "BTC"
|
6
|
-
|
7
|
-
FETCH_URL = "https://paxful.com/api/currency/btc"
|
8
|
-
|
9
|
-
def normalize(data)
|
10
|
-
return nil unless data = super
|
11
|
-
|
12
|
-
{
|
13
|
-
"anchor" => ANCHOR_CURRENCY,
|
14
|
-
"USD" => BigDecimal(data["price"].to_s)
|
15
|
-
}
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
module CurrencyRate
|
2
|
-
class PoloniexAdapter < Adapter
|
3
|
-
SUPPORTED_CURRENCIES = %w(BTS DASH DOGE LTC NXT STR XEM XMR XRP USDT
|
4
|
-
ETH SC DCR LSK STEEM ETC REP ARDR ZEC STRAT
|
5
|
-
GNT ZRX CVC OMG GAS STORJ EOS SNT KNC BAT
|
6
|
-
LOOM QTUM USDC MANA BNT BCHABC BCHSV FOAM
|
7
|
-
NMR POLY LPT ATOM TRX ETHBNT LINK XTZ PAX
|
8
|
-
USDJ SNX MATIC MKR DAI NEO SWFTC FXC AVA
|
9
|
-
CHR BNB BUSD MDT XFIL LEND REN LRC WRX SXP
|
10
|
-
STPT SWAP EXE).freeze
|
11
|
-
|
12
|
-
ANCHOR_CURRENCY = "BTC".freeze
|
13
|
-
|
14
|
-
FETCH_URL = "https://poloniex.com/public?command=returnTicker".freeze
|
15
|
-
|
16
|
-
def normalize(data)
|
17
|
-
return nil unless data = super
|
18
|
-
|
19
|
-
data.each_with_object({ "anchor" => ANCHOR_CURRENCY }) do |(pair_name, pair_info), result|
|
20
|
-
next unless pair_name.include?(ANCHOR_CURRENCY)
|
21
|
-
|
22
|
-
key = pair_name.sub(ANCHOR_CURRENCY, "").sub("_", "")
|
23
|
-
|
24
|
-
result[key] =
|
25
|
-
if pair_name.index(ANCHOR_CURRENCY) == 0
|
26
|
-
1 / BigDecimal(pair_info["last"])
|
27
|
-
else
|
28
|
-
BigDecimal(pair_info["last"])
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module CurrencyRate
|
2
|
-
class YadioAdapter < Adapter
|
3
|
-
SUPPORTED_CURRENCIES = %w(VES)
|
4
|
-
|
5
|
-
ANCHOR_CURRENCY = "BTC"
|
6
|
-
|
7
|
-
FETCH_URL = "https://api.yadio.io/rate/BTC"
|
8
|
-
|
9
|
-
def normalize(data)
|
10
|
-
return nil unless data = super
|
11
|
-
|
12
|
-
{
|
13
|
-
"anchor" => ANCHOR_CURRENCY,
|
14
|
-
"USD" => BigDecimal(data["usd"].to_s),
|
15
|
-
"VES" => BigDecimal(data["rate"].to_s),
|
16
|
-
}
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module CurrencyRate
|
2
|
-
class BonbastAdapter < Adapter
|
3
|
-
# No need to use it for fetching, just additional information about supported currencies
|
4
|
-
SUPPORTED_CURRENCIES = %w(USD IRR)
|
5
|
-
ANCHOR_CURRENCY = "USD"
|
6
|
-
FETCH_URL = "https://www.bonbast.com/"
|
7
|
-
|
8
|
-
def normalize(data)
|
9
|
-
sell = data.match(/<td id="usd1"[^>]*>(\d+)<\/td>/)[1].to_f
|
10
|
-
buy = data.match(/<td id="usd2"[^>]*>(\d+)<\/td>/)[1].to_f
|
11
|
-
{ "anchor" => self.class::ANCHOR_CURRENCY, "IRR" => BigDecimal(([buy, sell].reduce(:+).fdiv(2)*10).to_s) }
|
12
|
-
end
|
13
|
-
|
14
|
-
def request(url)
|
15
|
-
http_client = HTTP.timeout(
|
16
|
-
connect: @container.connect_timeout,
|
17
|
-
read: @container.read_timeout
|
18
|
-
)
|
19
|
-
http_client.get(url).to_s
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
module CurrencyRate
|
2
|
-
class CoinmonitorAdapter < Adapter
|
3
|
-
# No need to use it for fetching, just additional information about supported currencies
|
4
|
-
SUPPORTED_CURRENCIES = %w(ARS)
|
5
|
-
ANCHOR_CURRENCY = "USD"
|
6
|
-
FETCH_URL = "https://ar.coinmonitor.info/data_ar.json"
|
7
|
-
|
8
|
-
def normalize(data)
|
9
|
-
return nil unless data = super
|
10
|
-
{ "anchor" => ANCHOR_CURRENCY }.merge({ SUPPORTED_CURRENCIES.first => BigDecimal(data["BTC_avr_ars"].to_s) })
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
module CurrencyRate
|
2
|
-
class CurrencyLayerAdapter < Adapter
|
3
|
-
# No need to use it for fetching, just additional information about supported currencies
|
4
|
-
SUPPORTED_CURRENCIES = %w(
|
5
|
-
AED AFN ALL AMD ANG AOA ARS AUD AWG AZN BAM BBD BDT BGN
|
6
|
-
BHD BIF BMD BND BOB BRL BSD BTC BTN BWP BYN BYR BZD CAD
|
7
|
-
CDF CHF CLF CLP CNY COP CRC CUC CUP CVE CZK DJF DKK DOP
|
8
|
-
DZD EGP ERN ETB EUR FJD FKP GBP GEL GGP GHS GIP GMD GNF
|
9
|
-
GTQ GYD HKD HNL HRK HTG HUF IDR ILS IMP INR IQD IRR ISK
|
10
|
-
JEP JMD JOD JPY KES KGS KHR KMF KPW KRW KWD KYD KZT LAK
|
11
|
-
LBP LKR LRD LSL LTL LVL LYD MAD MDL MGA MKD MMK MNT MOP
|
12
|
-
MRO MUR MVR MWK MXN MYR MZN NAD NGN NIO NOK NPR NZD OMR
|
13
|
-
PAB PEN PGK PHP PKR PLN PYG QAR RON RSD RUB RWF SAR SBD
|
14
|
-
SCR SDG SEK SGD SHP SLL SOS SRD STD SVC SYP SZL THB TJS
|
15
|
-
TMT TND TOP TRY TTD TWD TZS UAH UGX USD UYU UZS VEF VND
|
16
|
-
VUV WST XAF XAG XAU XCD XDR XOF XPF YER ZAR ZMK ZMW ZWL
|
17
|
-
)
|
18
|
-
|
19
|
-
ANCHOR_CURRENCY = "USD"
|
20
|
-
FETCH_URL = "http://apilayer.net/api/live?access_key=__API_KEY__"
|
21
|
-
|
22
|
-
def normalize(data)
|
23
|
-
return nil unless data = super
|
24
|
-
rates = { "anchor" => self.class::ANCHOR_CURRENCY }
|
25
|
-
data["quotes"].each do |key, value|
|
26
|
-
rates[key.sub(self.class::ANCHOR_CURRENCY, "")] = BigDecimal(value.to_s)
|
27
|
-
end
|
28
|
-
rates
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module CurrencyRate
|
2
|
-
class FixerAdapter < Adapter
|
3
|
-
# EUR is the only currency available as a base on free plan
|
4
|
-
ANCHOR_CURRENCY = "EUR"
|
5
|
-
FETCH_URL = "http://data.fixer.io/api/latest?access_key=__API_KEY__&base=#{ANCHOR_CURRENCY}"
|
6
|
-
|
7
|
-
def normalize(data)
|
8
|
-
return nil unless data = super
|
9
|
-
rates = { "anchor" => ANCHOR_CURRENCY }
|
10
|
-
data["rates"].each do |k,v|
|
11
|
-
rates[k] = BigDecimal(v.to_s)
|
12
|
-
end
|
13
|
-
rates
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module CurrencyRate
|
2
|
-
class ForgeAdapter < Adapter
|
3
|
-
SUPPORTED_CURRENCIES = %w(
|
4
|
-
AED AFN ALL AMD ANG AOA ARE ARS AUD AUN AWG BAM BBD BDT BGN BHD BIF BMD
|
5
|
-
BND BOB BRI BRL BSD BTN BWP BYN BZD CAD CDF CHF CLF CLP CLY CNH CNY COP
|
6
|
-
CRC CUP CVE CYP CZK DJF DKK DOE DOP DZD EGP ETB EUR FJD FRN GBP GEL GHS
|
7
|
-
GMD GNF GTQ GYD HKD HNL HRK HTG HUF IDR ILS INR IQD IRR ISK JMD JOD JPY
|
8
|
-
KES KHR KMF KRU KRW KWD KYD KZT LAK LBP LFX LKR LRD LSL LTL LYD M5P MAD
|
9
|
-
MAL MDL MGA MKD MMK MOP MRU MTL MUR MVR MWK MXN MYR MZN NAD NBL NGN NIO
|
10
|
-
NOK NPR NSO NZD OMR OSO PAB PEN PGK PHP PKR PLN PYG QAR RON RSD RUB RWF
|
11
|
-
SAR SBD SCR SDG SEK SGD SHP SLL SOS SRD STN SVC SZL THB TJS TMT TND TOP
|
12
|
-
TRY TTD TWD TZS UAH UGX USD UYU UZS VES VND VRL VRN XAG XAGK XAU XAUK XCD
|
13
|
-
XDR XOF XPD XPDK XPF XPT XPTK YER ZAR ZMW ZWD
|
14
|
-
)
|
15
|
-
|
16
|
-
ANCHOR_CURRENCY = "USD"
|
17
|
-
currency_pairs_request = SUPPORTED_CURRENCIES.map { |c| "#{ANCHOR_CURRENCY}/#{c}" }.join(",")
|
18
|
-
FETCH_URL = "https://api.1forge.com/quotes?pairs=#{currency_pairs_request}&api_key=__API_KEY__"
|
19
|
-
|
20
|
-
def normalize(data)
|
21
|
-
return nil unless data = super
|
22
|
-
rates = { "anchor" => self.class::ANCHOR_CURRENCY }
|
23
|
-
data.each do |rate|
|
24
|
-
if rate["error"]
|
25
|
-
@container.logger.error("Forge exchange returned error")
|
26
|
-
return nil
|
27
|
-
end
|
28
|
-
rates[rate["s"].sub("#{self.class::ANCHOR_CURRENCY}/", "")] = BigDecimal(rate["p"].to_s) if rate["p"]
|
29
|
-
end
|
30
|
-
rates
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
34
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
module CurrencyRate
|
2
|
-
|
3
|
-
# Beware, freeforexapi.com doesn't normally work and you need to place
|
4
|
-
# a small banner on your website so they'd list your websites IP address.
|
5
|
-
# More info here: http://freeforexapi.com/Home/Api
|
6
|
-
class FreeForexAdapter < Adapter
|
7
|
-
SUPPORTED_CURRENCIES = %w(
|
8
|
-
AED AFN ALL AMD ANG AOA ARS ATS AUD AWG AZM AZN BAM BBD BDT
|
9
|
-
BEF BGN BHD BIF BMD BND BOB BRL BSD BTN BWP BYN BYR BZD CAD
|
10
|
-
CDF CLP CNH CNY COP CRC CUC CUP CVE CYP CZK DEM DJF DKK DOP
|
11
|
-
DZD EEK EGP ERN ESP ETB EUR FIM FJD FKP FRF GBP GEL GGP GHC
|
12
|
-
GHS GIP GMD GNF GRD GTQ GYD HKD HNL HRK HTG HUF IDR IEP ILS
|
13
|
-
IMP INR IQD IRR ISK ITL JEP JMD JOD KES KGS KHR KMF KPW KRW
|
14
|
-
KWD KYD KZT LAK LBP LKR LRD LSL LTL LUF LVL LYD MAD MDL MGA
|
15
|
-
MGF MKD MMK MNT MOP MRO MRU MTL MUR MVR MWK MXN MYR MZM MZN
|
16
|
-
NAD NGN NIO NLG NOK NPR NZD OMR PAB PEN PGK PHP PKR PLN PTE
|
17
|
-
PYG QAR ROL RON RSD RUB RWF SAR SBD SCR SDD SDG SEK SGD SHP
|
18
|
-
SIT SKK SLL SOS SPL SRD SRG STD STN SVC SYP SZL THB TJS TMM
|
19
|
-
TMT TND TOP TRL TRY TTD TVD TWD TZS UAH UGX UYU UZS VAL VEB
|
20
|
-
VEF VES VND VUV WST XAF XAG XAU XBT XCD XDR XOF XPD XPF XPT
|
21
|
-
YER ZAR ZMK ZMW ZWD
|
22
|
-
)
|
23
|
-
|
24
|
-
ANCHOR_CURRENCY = "USD"
|
25
|
-
FETCH_URL = "https://www.freeforexapi.com/api/live?pairs=" + SUPPORTED_CURRENCIES.map { |c| "USD#{c}"}.join(",")
|
26
|
-
|
27
|
-
def normalize(data)
|
28
|
-
return nil unless data = super
|
29
|
-
rates = { "anchor" => self.class::ANCHOR_CURRENCY }
|
30
|
-
data["rates"].each do |pair, payload|
|
31
|
-
rates[pair.sub(self.class::ANCHOR_CURRENCY, "")] = BigDecimal(payload["rate"].to_s)
|
32
|
-
end
|
33
|
-
rates
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
data/lib/container.rb
DELETED
@@ -1,203 +0,0 @@
|
|
1
|
-
class CurrencyRate::Container
|
2
|
-
|
3
|
-
attr_accessor :api_keys
|
4
|
-
attr_accessor :logger
|
5
|
-
attr_accessor :adapters
|
6
|
-
attr_accessor :connect_timeout
|
7
|
-
attr_accessor :read_timeout
|
8
|
-
attr_accessor :storage
|
9
|
-
attr_accessor :logger_callbacks
|
10
|
-
attr_accessor :limit_adapters_for_pairs
|
11
|
-
|
12
|
-
def initialize(
|
13
|
-
api_keys: {},
|
14
|
-
adapter_type: nil,
|
15
|
-
adapter_names: nil,
|
16
|
-
limit_adapters_for_pairs: {},
|
17
|
-
storage_settings: CurrencyRate.default_config[:storage_settings],
|
18
|
-
connect_timeout: CurrencyRate.default_config[:connect_timeout],
|
19
|
-
read_timeout: CurrencyRate.default_config[:read_timeout],
|
20
|
-
logger_settings: CurrencyRate.default_config[:logger_settings],
|
21
|
-
logger_callbacks: CurrencyRate.default_config[:logger_callbacks]
|
22
|
-
)
|
23
|
-
|
24
|
-
logger_settigns = CurrencyRate.default_config[:logger_settings].merge(logger_settings)
|
25
|
-
storage_settigns = CurrencyRate.default_config[:storage_settings].merge(storage_settings)
|
26
|
-
|
27
|
-
method(__method__).parameters.map do |_, name|
|
28
|
-
value = binding.local_variable_get(name)
|
29
|
-
instance_variable_set("@#{name}", value)
|
30
|
-
end
|
31
|
-
|
32
|
-
_logger_callbacks = {}
|
33
|
-
@logger_callbacks.each do |k,v|
|
34
|
-
_logger_callbacks[Logger::Severity.const_get(k.to_s.upcase)] = v
|
35
|
-
end
|
36
|
-
@logger_callbacks = _logger_callbacks
|
37
|
-
|
38
|
-
_limit_adapters_for_pairs = {}
|
39
|
-
@limit_adapters_for_pairs.each do |k,v|
|
40
|
-
_limit_adapters_for_pairs[k] = CurrencyRate.const_get(v.to_s.to_camel_case + "Adapter").instance
|
41
|
-
end
|
42
|
-
@limit_adapters_for_pairs = _limit_adapters_for_pairs
|
43
|
-
|
44
|
-
@storage = CurrencyRate.const_get(storage_settigns[:type].to_s.to_camel_case + "Storage").new(
|
45
|
-
path: storage_settings[:path],
|
46
|
-
container: self,
|
47
|
-
serializer: storage_settings[:serializer]
|
48
|
-
)
|
49
|
-
|
50
|
-
load_adapters!(names: adapter_names, type: adapter_type)
|
51
|
-
end
|
52
|
-
|
53
|
-
def method_missing(m, *args, &block)
|
54
|
-
if m.to_s.end_with? "_adapters"
|
55
|
-
self.send(:adapters, m[0..-10])
|
56
|
-
else
|
57
|
-
super
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def logger
|
62
|
-
return @logger if @logger
|
63
|
-
@logger = Logger.new(@logger_settings[:device])
|
64
|
-
@logger.progname = "CurrencyRate"
|
65
|
-
@logger.level = @logger_settings[:level]
|
66
|
-
@logger.formatter = @logger_settings[:formatter] if @logger_settings[:formatter]
|
67
|
-
@logger
|
68
|
-
end
|
69
|
-
|
70
|
-
# This method doesn't make any requests. It merely reads normalized data
|
71
|
-
# from the selected storage. Contrary to what one might assume,
|
72
|
-
# it doesn't call CurrencyRate::Container.sync! if storage for a particular
|
73
|
-
# adapter doesn't exist - that's because those are two separate tasks
|
74
|
-
# and you might not want to make that external http(s) request even if
|
75
|
-
# the rates in storage are not found.
|
76
|
-
#
|
77
|
-
# It also uses a 3 different strategies to calculate rates before they are returned:
|
78
|
-
#
|
79
|
-
# 1. If only one adapter is specified and/or available, it will return the rate for the
|
80
|
-
# pair using the data from the storage for this particular adapter.
|
81
|
-
#
|
82
|
-
# 2. If two or more adapters are specified and available, it will return the average rate
|
83
|
-
# based on the the rates from all of those adapters.
|
84
|
-
#
|
85
|
-
# 3. If `strategy: :majority` flag is set and an odd number of adapters (say, 3)
|
86
|
-
# is passed and/or available it will look at the rates from all, separate them
|
87
|
-
# into two groups based on how close their rates are and return the average of the rates
|
88
|
-
# that are closest in the group that is the largest.
|
89
|
-
#
|
90
|
-
# It will also check @limit_adapters_for_pairs hash for the request pair
|
91
|
-
# and if the key exists it will exclude adapters that are not listed in the array
|
92
|
-
# under that key.
|
93
|
-
def get_rate(from, to, use_adapters=@adapter_names, strategy: :average)
|
94
|
-
adapters_for_pair = @limit_adapters_for_pairs["#{from}/#{to}"]
|
95
|
-
_adapters = adapters.select { |a| use_adapters.include?(a.name(:camel_case)) }
|
96
|
-
_adapters = _adapters.select { |a| adapters_for_pair.include?(a.name(:camel_case)) } if adapters_for_pair
|
97
|
-
|
98
|
-
warning_loggers = []
|
99
|
-
rates = _adapters.map do |a|
|
100
|
-
rate = [a.name(:camel_case), a.get_rate(from, to)]
|
101
|
-
warning_loggers.push -> { self.log(:warn, "No rate for pair #{from}/#{to} found in #{a.name(:camel_case)} adapter") if rate[1].nil? }
|
102
|
-
rate
|
103
|
-
end.select { |r| !r[1].nil? }
|
104
|
-
|
105
|
-
if rates.empty?
|
106
|
-
self.log(:error,
|
107
|
-
"No rate for pair #{from}/#{to} is found in any of the available adapters " +
|
108
|
-
"(#{_adapters.map { |a| a.name(:camel_case) }.join(", ")})"
|
109
|
-
)
|
110
|
-
else
|
111
|
-
warning_loggers.each { |wl| wl.call }
|
112
|
-
end
|
113
|
-
|
114
|
-
if rates.size == 1
|
115
|
-
rates[0][1]
|
116
|
-
else
|
117
|
-
if strategy == :majority && rates.size.odd?
|
118
|
-
rates.sort! { |r1, r2| r1[1] <=> r2[1] }
|
119
|
-
|
120
|
-
largest_discrepancy_rate_index = 0
|
121
|
-
last_discrepancy = 0
|
122
|
-
|
123
|
-
rates.each_with_index do |r,i|
|
124
|
-
if i > 0
|
125
|
-
discrepancy = r[1] - rates[i-1][1]
|
126
|
-
if discrepancy > last_discrepancy
|
127
|
-
last_discrepancy = discrepancy
|
128
|
-
largest_discrepancy_rate_index = i
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
rates_group_1 = rates[0...largest_discrepancy_rate_index]
|
134
|
-
rates_group_2 = rates[largest_discrepancy_rate_index..rates.size-1]
|
135
|
-
rates = [rates_group_1, rates_group_2].max { |g1,g2| g1.size <=> g2.size }
|
136
|
-
end
|
137
|
-
rates.inject(BigDecimal(0)) { |sum, i| sum + i[1] } / BigDecimal(rates.size)
|
138
|
-
end
|
139
|
-
|
140
|
-
end
|
141
|
-
|
142
|
-
def sync!
|
143
|
-
successfull = []
|
144
|
-
failed = []
|
145
|
-
@adapters.each do |adapter|
|
146
|
-
adapter_name = adapter.class.to_s.sub("CurrencyRate::", "")
|
147
|
-
begin
|
148
|
-
rates = adapter.fetch_rates
|
149
|
-
unless rates
|
150
|
-
self.log(:warn, "Trying to sync rates for #{adapter_name}, rates not found, but http(s) request did not return any error.")
|
151
|
-
failed << adapter_name
|
152
|
-
next
|
153
|
-
end
|
154
|
-
@storage.write(adapter_name.to_snake_case.sub("_adapter", ""), rates)
|
155
|
-
successfull << adapter_name
|
156
|
-
rescue StandardError => e
|
157
|
-
failed << adapter_name
|
158
|
-
self.log(:error, e)
|
159
|
-
next
|
160
|
-
end
|
161
|
-
end
|
162
|
-
[successfull, failed]
|
163
|
-
end
|
164
|
-
|
165
|
-
def log(level, message)
|
166
|
-
severity = Logger::Severity.const_get(level.to_s.upcase)
|
167
|
-
logger.send(level, message)
|
168
|
-
if @logger_callbacks[severity]
|
169
|
-
@logger_callbacks[severity].call(level, message)
|
170
|
-
else
|
171
|
-
@logger_callbacks.keys.sort.each do |k|
|
172
|
-
@logger_callbacks[k].call(level, message) && break if k < severity
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
private
|
178
|
-
|
179
|
-
def load_adapters!(names: nil, type: :all)
|
180
|
-
if names
|
181
|
-
names = [names] if names.kind_of?(String)
|
182
|
-
@adapters = names.map do |name|
|
183
|
-
CurrencyRate.const_get(name + "Adapter").instance
|
184
|
-
end
|
185
|
-
else
|
186
|
-
crypto_adapter_files = Dir[File.join CurrencyRate.root, "lib/adapters/crypto/*"]
|
187
|
-
fiat_adapter_files = Dir[File.join CurrencyRate.root, "lib/adapters/fiat/*"]
|
188
|
-
adapter_files = case type
|
189
|
-
when :crypto then crypto_adapter_files
|
190
|
-
when :fiat then fiat_adapter_files
|
191
|
-
else crypto_adapter_files + fiat_adapter_files
|
192
|
-
end
|
193
|
-
@adapters = adapter_files.map do |file|
|
194
|
-
CurrencyRate.const_get(File.basename(file, ".rb").to_camel_case).instance
|
195
|
-
end
|
196
|
-
end
|
197
|
-
@adapters.each { |a| a.container = self; a.api_key = @api_keys[a.name] }
|
198
|
-
@adapter_names = @adapters.map { |a| a.name(:camel_case) }
|
199
|
-
@adapters
|
200
|
-
end
|
201
|
-
|
202
|
-
|
203
|
-
end
|