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
@@ -4,81 +4,38 @@ require "singleton"
4
4
  require "json"
5
5
  require "http"
6
6
 
7
- require_relative "configuration"
7
+ require_relative "exceptions"
8
8
  require_relative "adapter"
9
- require_relative "fetcher"
10
- require_relative "synchronizer"
9
+ require_relative "container"
10
+ require_relative "utils/string_extensions"
11
11
 
12
12
  Dir["#{File.expand_path __dir__}/adapters/**/*.rb"].each { |f| require f }
13
- Dir["#{File.expand_path __dir__}/storage/**/*.rb"].each { |f| require f }
13
+ Dir["#{File.expand_path __dir__}/storage/**/*.rb"].each { |f| require f }
14
14
 
15
15
  module CurrencyRate
16
+
17
+ @@default_config = {
18
+ connect_timeout: 10,
19
+ read_timeout: 10,
20
+ logger_callbacks: {},
21
+ logger_settings: { device: $stdout, level: :info, formatter: nil },
22
+ storage_settings: { path: __dir__, serializer: CurrencyRate::Storage::YAMLSerializer }
23
+ }
24
+
16
25
  class << self
17
- attr_writer :configuration
18
- end
19
26
 
20
- def self.method_missing(m, *args, &block)
21
- if m.to_s.end_with? "_adapters"
22
- self.send(:adapters, m[0..-10])
23
- else
24
- super
27
+ def default_config
28
+ @@default_config
25
29
  end
26
- end
27
30
 
28
- def self.adapters(type)
29
- Dir[File.join self.root, "lib/adapters/#{type}/*"].map do |file|
30
- File.basename(file, ".rb").split('_')[0...-1].map {|w| w.capitalize}.join
31
+ def update_default_config(config_hash)
32
+ @@default_config.merge!(config_hash)
31
33
  end
32
- end
33
-
34
- def self.configuration
35
- @configuration ||= Configuration.new
36
- end
37
-
38
- def self.configure
39
- yield(configuration)
40
- end
41
-
42
- def self.fetcher
43
- @fetcher ||= Fetcher.new(fiat_exchanges: configuration.fiat_adapters,
44
- crypto_exchanges: configuration.crypto_adapters,
45
- limit_sources_for_fiat_currencies: configuration.limit_sources_for_fiat_currencies)
46
- end
47
-
48
- def self.fetch_crypto(exchange, from, to)
49
- fetcher.fetch_crypto(exchange, from, to)
50
- end
51
-
52
- def self.fetch_fiat(from, to)
53
- fetcher.fetch_fiat(from, to)
54
- end
55
-
56
- def self.logger
57
- return @logger if @logger
58
- @logger = Logger.new(configuration.logger[:device])
59
- @logger.progname = "CurrencyRate"
60
- @logger.level = configuration.logger[:level]
61
- @logger.formatter = configuration.logger[:formatter] if configuration.logger[:formatter]
62
- @logger
63
- end
64
-
65
- def self.synchronizer
66
- @synchronizer ||= Synchronizer.new
67
- end
68
-
69
- def self.sync_crypto!
70
- synchronizer.sync_crypto!
71
- end
72
34
 
73
- def self.sync_fiat!
74
- synchronizer.sync_fiat!
75
- end
35
+ def root
36
+ File.expand_path("../", File.dirname(__FILE__))
37
+ end
76
38
 
77
- def self.sync!
78
- synchronizer.sync!
79
39
  end
80
40
 
81
- def self.root
82
- File.expand_path("../", File.dirname(__FILE__))
83
- end
84
41
  end
@@ -1,3 +1,3 @@
1
1
  module CurrencyRate
2
- VERSION = "1.5.3"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -0,0 +1,9 @@
1
+ module CurrencyRate
2
+ class CurrencyRateError < StandardError; end
3
+
4
+ class StorageNotDefinedError < CurrencyRateError
5
+ def initialize
6
+ super("Storage is not configured for currency-rate gem.")
7
+ end
8
+ end
9
+ end
@@ -3,27 +3,32 @@ module CurrencyRate
3
3
  attr_reader :path
4
4
  attr_accessor :serializer
5
5
 
6
- def initialize(path = nil, serializer: nil)
7
- @path = path || CurrencyRate.configuration.file_storage[:path]
6
+ def initialize(path:, container:, serializer: nil)
7
+ @path = path
8
+ @container = container
8
9
  @serializer = serializer || Storage::YAMLSerializer.new
9
10
  end
10
11
 
11
- def read(exchange_name)
12
- path = path_for exchange_name.downcase
12
+ def exists?(adapter_name)
13
+ File.exists? path_for(adapter_name)
14
+ end
15
+
16
+ def read(adapter_name)
17
+ path = path_for adapter_name
13
18
  @serializer.deserialize File.read(path)
14
19
  rescue StandardError => e
15
- CurrencyRate.logger.error(e)
20
+ @container.log(:error, e)
16
21
  nil
17
22
  end
18
23
 
19
- def write(exchange_name, data = "")
20
- File.write path_for(exchange_name.downcase), @serializer.serialize(data)
24
+ def write(adapter_name, data = "")
25
+ File.write path_for(adapter_name), @serializer.serialize(data)
21
26
  rescue StandardError => e
22
- CurrencyRate.logger.error(e)
27
+ @container.log(:error, e)
23
28
  end
24
29
 
25
- def path_for(exchange_name)
26
- File.join @path, "#{exchange_name}_rates.yml"
30
+ def path_for(adapter_name)
31
+ File.join @path, "#{adapter_name}.yml"
27
32
  end
28
33
  end
29
34
  end
@@ -0,0 +1,19 @@
1
+ module StringExtensions
2
+
3
+ def to_snake_case
4
+ self.to_s.gsub(/::/, '/').
5
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
6
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
7
+ tr("-", "_").
8
+ downcase
9
+ end
10
+
11
+ def to_camel_case
12
+ words = self.to_s.split('_')
13
+ words.map { |w| w[0].capitalize + w[1..-1] }.join
14
+ end
15
+
16
+ end
17
+
18
+ class String; include StringExtensions; end
19
+ class Symbol; include StringExtensions; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: currency-rate
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.3
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roman Snitko
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-27 00:00:00.000000000 Z
11
+ date: 2020-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '4.3'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '4.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: byebug
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -90,27 +90,26 @@ files:
90
90
  - ".gitignore"
91
91
  - ".rspec"
92
92
  - Gemfile
93
+ - Gemfile.lock
93
94
  - LICENSE.txt
94
95
  - README.md
95
96
  - Rakefile
96
- - api_keys.yml.sample
97
- - bin/rake
98
- - bin/rspec
99
97
  - currency-rate.gemspec
100
98
  - lib/adapter.rb
101
99
  - lib/adapters/crypto/binance_adapter.rb
102
100
  - lib/adapters/crypto/bitfinex_adapter.rb
103
101
  - lib/adapters/crypto/bitpay_adapter.rb
104
102
  - lib/adapters/crypto/bitstamp_adapter.rb
105
- - lib/adapters/crypto/btc_china_adapter.rb
106
- - lib/adapters/crypto/btc_e_adapter.rb
107
103
  - lib/adapters/crypto/coin_market_cap_adapter.rb
108
104
  - lib/adapters/crypto/coinbase_adapter.rb
109
105
  - lib/adapters/crypto/exmo_adapter.rb
106
+ - lib/adapters/crypto/hit_BTC_adapter.rb
110
107
  - lib/adapters/crypto/huobi_adapter.rb
111
108
  - lib/adapters/crypto/kraken_adapter.rb
112
109
  - lib/adapters/crypto/localbitcoins_adapter.rb
113
110
  - lib/adapters/crypto/okcoin_adapter.rb
111
+ - lib/adapters/crypto/paxful_adapter.rb
112
+ - lib/adapters/crypto/poloniex_adapter.rb
114
113
  - lib/adapters/crypto/yadio_adapter.rb
115
114
  - lib/adapters/fiat/bonbast_adapter.rb
116
115
  - lib/adapters/fiat/coinmonitor_adapter.rb
@@ -118,14 +117,13 @@ files:
118
117
  - lib/adapters/fiat/fixer_adapter.rb
119
118
  - lib/adapters/fiat/forge_adapter.rb
120
119
  - lib/adapters/fiat/free_forex_adapter.rb
121
- - lib/adapters/fiat/yahoo_adapter.rb
122
- - lib/configuration.rb
120
+ - lib/container.rb
123
121
  - lib/currency_rate.rb
124
122
  - lib/currency_rate/version.rb
125
- - lib/fetcher.rb
123
+ - lib/exceptions.rb
126
124
  - lib/storage/file_storage.rb
127
125
  - lib/storage/serializers/yaml_serializer.rb
128
- - lib/synchronizer.rb
126
+ - lib/utils/string_extensions.rb
129
127
  homepage: https://gitlab.com/hodlhodl-public/currency-rate
130
128
  licenses: []
131
129
  metadata:
@@ -147,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
145
  - !ruby/object:Gem::Version
148
146
  version: '0'
149
147
  requirements: []
150
- rubygems_version: 3.0.3
148
+ rubygems_version: 3.1.2
151
149
  signing_key:
152
150
  specification_version: 4
153
151
  summary: Converter for fiat and crypto currencies
@@ -1,4 +0,0 @@
1
- ForgeAdapter: "<your_key>"
2
- CurrencyLayerAdapter: "<your_key>"
3
- FixerAdapter: "<your_key>"
4
- CoinMarketCapAdapter: "<your_key>"
data/bin/rake DELETED
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- #
5
- # This file was generated by Bundler.
6
- #
7
- # The application 'rake' is installed as part of a gem, and
8
- # this file is here to facilitate running it.
9
- #
10
-
11
- require "pathname"
12
- ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
- Pathname.new(__FILE__).realpath)
14
-
15
- bundle_binstub = File.expand_path("../bundle", __FILE__)
16
-
17
- if File.file?(bundle_binstub)
18
- if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
- load(bundle_binstub)
20
- else
21
- abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
- Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
- end
24
- end
25
-
26
- require "rubygems"
27
- require "bundler/setup"
28
-
29
- load Gem.bin_path("rake", "rake")
data/bin/rspec DELETED
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- #
5
- # This file was generated by Bundler.
6
- #
7
- # The application 'rspec' is installed as part of a gem, and
8
- # this file is here to facilitate running it.
9
- #
10
-
11
- require "pathname"
12
- ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
- Pathname.new(__FILE__).realpath)
14
-
15
- bundle_binstub = File.expand_path("../bundle", __FILE__)
16
-
17
- if File.file?(bundle_binstub)
18
- if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
- load(bundle_binstub)
20
- else
21
- abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
- Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
- end
24
- end
25
-
26
- require "rubygems"
27
- require "bundler/setup"
28
-
29
- load Gem.bin_path("rspec-core", "rspec")
@@ -1,11 +0,0 @@
1
- module CurrencyRate
2
- class BtcChinaAdapter < Adapter
3
- FETCH_URL = 'https://data.btcchina.com/data/ticker'
4
-
5
- def normalize(data)
6
- return nil unless super
7
- { "BTC_CNY" => BigDecimal(data["ticker"]["last"].to_s) }
8
- end
9
-
10
- end
11
- end
@@ -1,18 +0,0 @@
1
- module CurrencyRate
2
- class BtcEAdapter < Adapter
3
- FETCH_URL = {
4
- 'BTC_USD' => 'https://wex.nz/api/2/btc_usd/ticker',
5
- 'BTC_EUR' => 'https://wex.nz/api/2/btc_eur/ticker',
6
- 'BTC_RUB' => 'https://wex.nz/api/2/btc_rur/ticker',
7
- }
8
-
9
- def normalize(data)
10
- return nil unless super
11
- data.reduce({}) do |result, (pair, value)|
12
- result[pair] = BigDecimal(value["ticker"]["last"].to_s)
13
- result
14
- end
15
- end
16
-
17
- end
18
- end
@@ -1,35 +0,0 @@
1
- module CurrencyRate
2
- class YahooAdapter < Adapter
3
- SUPPORTED_CURRENCIES = %w(
4
- AED AFN ALL AMD ANG AOA ARS AUD AWG AZN BAM BBD BDT BGN BHD BIF BMD BND
5
- BRL BSD BTN BWP BYR BZD CAD CHF CLF CLP CNY COP CRC CUC CUP CVE CZK DEM
6
- DJF DKK DOP DZD ECS EGP ERN ETB EUR FJD FKP FRF GBP GEL GHS GIP GMD GNF
7
- GTQ GYD HKD HNL HRK HTG HUF IDR IEP ILS INR IQD ISK ITL JMD JOD JPY KES
8
- KGS KHR KMF KWD KYD KZT LAK LBP LKR LRD LSL LTL LVL LYD MAD MGA MMK MNT
9
- MOP MRO MUR MVR MWK MXN MXV MYR MZN NAD NGN NIO NOK NPR NZD OMR PAB PEN
10
- PGK PHP PKR PLN PYG QAR RON RSD RUB RWF SAR SBD SCR SDG SEK SGD SLL SOS
11
- SRD STD SVC SYP SZL THB TJS TMT TND TOP TRY TTD UAH UGX USD UYU UZS VND
12
- VUV WST XAF XAG XAU XCD XDR XOF XPD XPF XPT YER ZAR ZMW ZWL
13
- )
14
-
15
- ANCHOR_CURRENCY = "USD"
16
-
17
- FETCH_URL = "http://query.yahooapis.com/v1/public/yql?" + URI.encode_www_form(
18
- format: 'json',
19
- env: "store://datatables.org/alltableswithkeys",
20
- q: "SELECT * FROM yahoo.finance.xchange WHERE pair IN" +
21
- # The following line is building array string in SQL: '("USDJPY", "USDRUB", ...)'
22
- "(#{SUPPORTED_CURRENCIES.map{|x| '"' + ANCHOR_CURRENCY + x.upcase + '"'}.join(',')})"
23
- )
24
-
25
- def normalize(data)
26
- return nil unless super
27
- rates = { "anchor" => self.class::ANCHOR_CURRENCY }
28
- data["query"]["results"]["rate"].each do |rate|
29
- rates[rate["Name"].split("/")[1]] = BigDecimal(rate["Rate"].to_s)
30
- end
31
- rates
32
- end
33
-
34
- end
35
- end
@@ -1,29 +0,0 @@
1
- module CurrencyRate
2
- class Configuration
3
- attr_accessor :api_keys
4
- attr_accessor :file_storage
5
- attr_accessor :logger
6
- attr_accessor :crypto_adapters
7
- attr_accessor :fiat_adapters
8
- attr_accessor :connect_timeout
9
- attr_accessor :read_timeout
10
- attr_accessor :limit_sources_for_fiat_currencies
11
- attr_accessor :crypto_currencies
12
-
13
- def initialize
14
- @api_keys = { }
15
- @file_storage = { path: "" }
16
- @logger = {
17
- device: $stdout,
18
- level: :info,
19
- formatter: nil,
20
- }
21
- @crypto_adapters = CurrencyRate.adapters :crypto
22
- @fiat_adapters = CurrencyRate.adapters :fiat
23
- @connect_timeout = 4
24
- @read_timeout = 4
25
- @limit_sources_for_fiat_currencies = {}
26
- @crypto_currencies = []
27
- end
28
- end
29
- end
@@ -1,78 +0,0 @@
1
- module CurrencyRate
2
- class Fetcher
3
- attr_accessor :storage
4
- attr_accessor :fiat_exchanges
5
- attr_accessor :crypto_exchanges
6
- attr_accessor :limit_sources_for_fiat_currencies
7
-
8
- def initialize(fiat_exchanges: nil, crypto_exchanges: nil, storage: nil, limit_sources_for_fiat_currencies: {})
9
- @storage = storage || FileStorage.new
10
- @fiat_exchanges = fiat_exchanges || ["Yahoo", "Fixer", "Forge"]
11
- @crypto_exchanges = crypto_exchanges || ["Bitstamp", "Binance"]
12
- @limit_sources_for_fiat_currencies = limit_sources_for_fiat_currencies
13
- end
14
-
15
- def fetch_crypto(exchange, from, to)
16
- from = from.strip.upcase
17
- to = to.strip.upcase
18
- rates = @storage.read(exchange)
19
-
20
- if rates.nil?
21
- CurrencyRate.logger.warn("Fetcher#fetch_crypto: rates for #{exchange} not found in storage <#{@storage.class.name}>")
22
- return nil
23
- end
24
-
25
- rate = calculate_rate(rates, from, to)
26
- return rate unless rate.nil?
27
-
28
- if to != "USD"
29
- usd_fiat = fetch_fiat("USD", to)
30
- return BigDecimal(rates["USD"] * usd_fiat) if usd_fiat && rates["USD"]
31
- end
32
- nil
33
- end
34
-
35
- def fetch_fiat(from, to)
36
- from = from.strip.upcase
37
- to = to.strip.upcase
38
-
39
- exchanges = @fiat_exchanges.dup
40
- exchanges += @crypto_exchanges if is_crypto_currency?(from) || is_crypto_currency?(to)
41
-
42
- if(@limit_sources_for_fiat_currencies[from])
43
- exchanges.select! { |ex| @limit_sources_for_fiat_currencies[from].include?(ex) }
44
- end
45
- if(@limit_sources_for_fiat_currencies[to])
46
- exchanges.select! { |ex| @limit_sources_for_fiat_currencies[to].include?(ex) }
47
- end
48
-
49
- exchanges.each do |exchange|
50
- rates = @storage.read(exchange)
51
- next if rates.nil?
52
-
53
- rate = calculate_rate(rates, from, to)
54
- return rate unless rate.nil?
55
- end
56
- nil
57
- end
58
-
59
- private
60
-
61
- def calculate_rate(rates, from, to)
62
- anchor = rates.delete("anchor")
63
-
64
- return BigDecimal(rates[to]) if anchor == from && rates[to]
65
- return BigDecimal(1 / rates[from]) if anchor == to && rates[from]
66
- return BigDecimal(rates[to] / rates[from]) if rates[from] && rates[to]
67
-
68
- CurrencyRate.logger.warn("Fetcher: rate for #{from}_#{to} not found.")
69
- nil
70
- end
71
-
72
- def is_crypto_currency?(currency)
73
- CurrencyRate.configuration.crypto_currencies.include?(currency)
74
- end
75
-
76
- end
77
- end
78
-