currency-rate 1.5.3 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -3
  3. data/Gemfile.lock +68 -0
  4. data/README.md +2 -2
  5. data/Rakefile +12 -3
  6. data/currency-rate.gemspec +1 -1
  7. data/lib/adapter.rb +56 -22
  8. data/lib/adapters/crypto/binance_adapter.rb +1 -1
  9. data/lib/adapters/crypto/bitfinex_adapter.rb +3 -2
  10. data/lib/adapters/crypto/bitpay_adapter.rb +1 -1
  11. data/lib/adapters/crypto/bitstamp_adapter.rb +1 -1
  12. data/lib/adapters/crypto/coin_market_cap_adapter.rb +2 -4
  13. data/lib/adapters/crypto/coinbase_adapter.rb +3 -3
  14. data/lib/adapters/crypto/exmo_adapter.rb +1 -1
  15. data/lib/adapters/crypto/hit_BTC_adapter.rb +64 -0
  16. data/lib/adapters/crypto/huobi_adapter.rb +17 -9
  17. data/lib/adapters/crypto/kraken_adapter.rb +5 -5
  18. data/lib/adapters/crypto/localbitcoins_adapter.rb +1 -1
  19. data/lib/adapters/crypto/okcoin_adapter.rb +4 -6
  20. data/lib/adapters/crypto/paxful_adapter.rb +18 -0
  21. data/lib/adapters/crypto/poloniex_adapter.rb +33 -0
  22. data/lib/adapters/crypto/yadio_adapter.rb +2 -1
  23. data/lib/adapters/fiat/bonbast_adapter.rb +3 -5
  24. data/lib/adapters/fiat/coinmonitor_adapter.rb +4 -5
  25. data/lib/adapters/fiat/currency_layer_adapter.rb +2 -3
  26. data/lib/adapters/fiat/fixer_adapter.rb +3 -4
  27. data/lib/adapters/fiat/forge_adapter.rb +15 -6
  28. data/lib/adapters/fiat/free_forex_adapter.rb +7 -3
  29. data/lib/container.rb +203 -0
  30. data/lib/currency_rate.rb +20 -63
  31. data/lib/currency_rate/version.rb +1 -1
  32. data/lib/exceptions.rb +9 -0
  33. data/lib/storage/file_storage.rb +15 -10
  34. data/lib/utils/string_extensions.rb +19 -0
  35. metadata +12 -14
  36. data/api_keys.yml.sample +0 -4
  37. data/bin/rake +0 -29
  38. data/bin/rspec +0 -29
  39. data/lib/adapters/crypto/btc_china_adapter.rb +0 -11
  40. data/lib/adapters/crypto/btc_e_adapter.rb +0 -18
  41. data/lib/adapters/fiat/yahoo_adapter.rb +0 -35
  42. data/lib/configuration.rb +0 -29
  43. data/lib/fetcher.rb +0 -78
  44. data/lib/synchronizer.rb +0 -51
@@ -1,8 +1,8 @@
1
1
  module CurrencyRate
2
2
  class KrakenAdapter < Adapter
3
3
  SUPPORTED_CURRENCIES = %w(
4
- ADA BCH BSV BTC DASH EOS ETC ETH GNO LTC MLN
5
- NMC QTUM REP XDG XLM XMR XRP XTZ ZEC
4
+ ADA BCH BSV BTC DAI DASH EOS ETC ETH GNO LTC MLN
5
+ NMC QTUM REP USDC XDG XLM XMR XRP XTZ ZEC
6
6
  )
7
7
 
8
8
  ASSET_MAP = {
@@ -21,14 +21,14 @@ module CurrencyRate
21
21
 
22
22
  ANCHOR_CURRENCY = "BTC"
23
23
 
24
- FETCH_URL = "https://api.kraken.com/0/public/Ticker?pair=#{ %w(ADAXBT BCHXBT BSVXBT DASHXBT EOSXBT GNOXBT QTUMXBT XTZXBT XETCXXBT XETHXXBT XLTCXXBT XREPXXBT XXLMXXBT XXMRXXBT XXRPXXBT XZECXXBT XXBTZUSD).join(",") }"
24
+ FETCH_URL = "https://api.kraken.com/0/public/Ticker?pair=#{ %w(ADAXBT BCHXBT BSVXBT DASHXBT EOSXBT GNOXBT QTUMXBT XTZXBT XETCXXBT XETHXXBT XLTCXXBT XREPXXBT XXLMXXBT XXMRXXBT XXRPXXBT XZECXXBT XXBTZUSD XBTDAI XBTUSDC).join(",") }"
25
25
 
26
26
  def normalize(data)
27
- return nil unless super
27
+ return nil unless data = super
28
28
  data["result"].reduce({ "anchor" => ANCHOR_CURRENCY }) do |result, (pair, value)|
29
29
  key = ta(pair.sub(ta(ANCHOR_CURRENCY), ""))
30
30
 
31
- if key == "USD"
31
+ if %w(USD DAI USDC).include?(key)
32
32
  result[key] = BigDecimal(value["c"].first.to_s)
33
33
  else
34
34
  result[key] = 1 / BigDecimal(value["c"].first.to_s)
@@ -14,7 +14,7 @@ module CurrencyRate
14
14
  FETCH_URL = 'https://localbitcoins.com/bitcoinaverage/ticker-all-currencies/'
15
15
 
16
16
  def normalize(data)
17
- return nil unless super
17
+ return nil unless data = super
18
18
  data.reduce({ "anchor" => ANCHOR_CURRENCY }) do |result, (fiat, value)|
19
19
  result["#{fiat.upcase}"] = BigDecimal(value["rates"]["last"].to_s)
20
20
  result
@@ -1,16 +1,14 @@
1
1
  module CurrencyRate
2
2
  class OkcoinAdapter < Adapter
3
3
  FETCH_URL = {
4
- 'LTC_USD' => 'https://www.okcoin.com/api/v1/ticker.do?symbol=ltc_usd',
5
- 'BTC_USD' => 'https://www.okcoin.com/api/v1/ticker.do?symbol=btc_usd',
6
- 'LTC_CNY' => 'https://www.okcoin.cn/api/ticker.do?symbol=ltc_cny',
7
- 'BTC_CNY' => 'https://www.okcoin.cn/api/ticker.do?symbol=btc_cny'
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',
8
6
  }
9
7
 
10
8
  def normalize(data)
11
- return nil unless super
9
+ return nil unless data = super
12
10
  data.reduce({}) do |result, (pair, value)|
13
- result[pair] = BigDecimal(value["ticker"]["last"].to_s)
11
+ result[pair] = BigDecimal(value["last"].to_s)
14
12
  result
15
13
  end
16
14
  end
@@ -0,0 +1,18 @@
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
@@ -0,0 +1,33 @@
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
@@ -7,10 +7,11 @@ module CurrencyRate
7
7
  FETCH_URL = "https://api.yadio.io/rate/BTC"
8
8
 
9
9
  def normalize(data)
10
- return nil unless super
10
+ return nil unless data = super
11
11
 
12
12
  {
13
13
  "anchor" => ANCHOR_CURRENCY,
14
+ "USD" => BigDecimal(data["usd"].to_s),
14
15
  "VES" => BigDecimal(data["rate"].to_s),
15
16
  }
16
17
  end
@@ -12,13 +12,11 @@ module CurrencyRate
12
12
  end
13
13
 
14
14
  def request(url)
15
-
16
- http_client = HTTP.timeout(connect:
17
- CurrencyRate.configuration.connect_timeout, read:
18
- CurrencyRate.configuration.read_timeout
15
+ http_client = HTTP.timeout(
16
+ connect: @container.connect_timeout,
17
+ read: @container.read_timeout
19
18
  )
20
19
  http_client.get(url).to_s
21
-
22
20
  end
23
21
 
24
22
  end
@@ -3,12 +3,11 @@ module CurrencyRate
3
3
  # No need to use it for fetching, just additional information about supported currencies
4
4
  SUPPORTED_CURRENCIES = %w(ARS)
5
5
  ANCHOR_CURRENCY = "USD"
6
- FETCH_URL = "https://ar.coinmonitor.info/api/dolar_ar/"
6
+ FETCH_URL = "https://ar.coinmonitor.info/data_ar.json"
7
7
 
8
8
  def normalize(data)
9
- return nil unless super
10
-
11
- { "anchor" => ANCHOR_CURRENCY }.merge({ SUPPORTED_CURRENCIES.first => BigDecimal(data["DOL_blue"].to_s) })
9
+ return nil unless data = super
10
+ { "anchor" => ANCHOR_CURRENCY }.merge({ SUPPORTED_CURRENCIES.first => BigDecimal(data["BTC_avr_ars"].to_s) })
12
11
  end
13
12
  end
14
- end
13
+ end
@@ -17,11 +17,10 @@ module CurrencyRate
17
17
  )
18
18
 
19
19
  ANCHOR_CURRENCY = "USD"
20
- FETCH_URL = "http://www.apilayer.net/api/live"
21
- API_KEY_PARAM = "access_key"
20
+ FETCH_URL = "http://apilayer.net/api/live?access_key=__API_KEY__"
22
21
 
23
22
  def normalize(data)
24
- return nil unless super
23
+ return nil unless data = super
25
24
  rates = { "anchor" => self.class::ANCHOR_CURRENCY }
26
25
  data["quotes"].each do |key, value|
27
26
  rates[key.sub(self.class::ANCHOR_CURRENCY, "")] = BigDecimal(value.to_s)
@@ -2,13 +2,12 @@ module CurrencyRate
2
2
  class FixerAdapter < Adapter
3
3
  # EUR is the only currency available as a base on free plan
4
4
  ANCHOR_CURRENCY = "EUR"
5
- FETCH_URL = "http://data.fixer.io/latest"
6
- API_KEY_PARAM = "access_key"
5
+ FETCH_URL = "http://data.fixer.io/api/latest?access_key=__API_KEY__&base=#{ANCHOR_CURRENCY}"
7
6
 
8
7
  def normalize(data)
9
- return nil unless super
8
+ return nil unless data = super
10
9
  rates = { "anchor" => ANCHOR_CURRENCY }
11
- data["rates"].each do |k, v|
10
+ data["rates"].each do |k,v|
12
11
  rates[k] = BigDecimal(v.to_s)
13
12
  end
14
13
  rates
@@ -1,22 +1,31 @@
1
1
  module CurrencyRate
2
2
  class ForgeAdapter < Adapter
3
3
  SUPPORTED_CURRENCIES = %w(
4
- JPY CHF CAD SEK NOK MXN ZAR TRY CNH EUR GBP AUD NZD XAU XAG
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
5
14
  )
6
15
 
7
16
  ANCHOR_CURRENCY = "USD"
8
- FETCH_URL = "https://forex.1forge.com/1.0.2/quotes?pairs=" + SUPPORTED_CURRENCIES.map { |c| "#{ANCHOR_CURRENCY}#{c}" }.join(",")
9
- API_KEY_PARAM = "api_key"
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__"
10
19
 
11
20
  def normalize(data)
12
- return nil unless super
21
+ return nil unless data = super
13
22
  rates = { "anchor" => self.class::ANCHOR_CURRENCY }
14
23
  data.each do |rate|
15
24
  if rate["error"]
16
- CurrencyRate.logger.error("Forge exchange returned error")
25
+ @container.logger.error("Forge exchange returned error")
17
26
  return nil
18
27
  end
19
- rates[rate["symbol"].sub(self.class::ANCHOR_CURRENCY, "")] = BigDecimal(rate["price"].to_s)
28
+ rates[rate["s"].sub("#{self.class::ANCHOR_CURRENCY}/", "")] = BigDecimal(rate["p"].to_s) if rate["p"]
20
29
  end
21
30
  rates
22
31
  end
@@ -1,4 +1,8 @@
1
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
2
6
  class FreeForexAdapter < Adapter
3
7
  SUPPORTED_CURRENCIES = %w(
4
8
  AED AFN ALL AMD ANG AOA ARS ATS AUD AWG AZM AZN BAM BBD BDT
@@ -18,10 +22,10 @@ module CurrencyRate
18
22
  )
19
23
 
20
24
  ANCHOR_CURRENCY = "USD"
21
- FETCH_URL = "https://www.freeforexapi.com/api/live?pairs=" + SUPPORTED_CURRENCIES.map{|cur| "USD#{cur}"}.join(",")
25
+ FETCH_URL = "https://www.freeforexapi.com/api/live?pairs=" + SUPPORTED_CURRENCIES.map { |c| "USD#{c}"}.join(",")
22
26
 
23
27
  def normalize(data)
24
- return nil unless super
28
+ return nil unless data = super
25
29
  rates = { "anchor" => self.class::ANCHOR_CURRENCY }
26
30
  data["rates"].each do |pair, payload|
27
31
  rates[pair.sub(self.class::ANCHOR_CURRENCY, "")] = BigDecimal(payload["rate"].to_s)
@@ -29,4 +33,4 @@ module CurrencyRate
29
33
  rates
30
34
  end
31
35
  end
32
- end
36
+ end
@@ -0,0 +1,203 @@
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