currency-rate 1.5.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
-