├── .ruby-version ├── .rspec ├── .hound.yml ├── spec ├── cassettes │ ├── CAE.gz │ ├── CBE.gz │ ├── CIB.gz │ ├── EGB.gz │ ├── NBE.gz │ ├── NBG.gz │ ├── UBE.gz │ ├── AAIB.gz │ ├── ADIB.gz │ ├── Blom.gz │ ├── EDBE.gz │ ├── MIDB.gz │ ├── SAIB.gz │ ├── AlexBank.gz │ ├── BanqueMisr.gz │ ├── AlBarakaBank.gz │ ├── BanqueDuCaire.gz │ ├── SuezCanalBank.gz │ └── AlAhliBankOfKuwait.gz ├── egp_rates │ ├── bank_spec.rb │ ├── edbe_spec.rb │ ├── ube_spec.rb │ ├── midb_spec.rb │ ├── nbg_spec.rb │ ├── adib_spec.rb │ ├── al_ahli_bank_of_kuwait_spec.rb │ ├── blom_spec.rb │ ├── nbe_spec.rb │ ├── cae_spec.rb │ ├── egb_spec.rb │ ├── al_baraka_bank_spec.rb │ ├── cbe_spec.rb │ ├── cib_spec.rb │ ├── suez_canal_bank_spec.rb │ ├── aaib_spec.rb │ ├── alex_bank_spec.rb │ ├── banque_du_caire_spec.rb │ └── banque_misr_spec.rb ├── spec_helper.rb └── egp_rates_spec.rb ├── pre-commit ├── .gitignore ├── Gemfile ├── bin ├── setup └── console ├── .codeclimate.yml ├── .editorconfig ├── Rakefile ├── .rubocop.yml ├── LICENSE.md ├── wercker.yml ├── EGP_Rates.gemspec ├── lib ├── egp_rates │ ├── cbe.rb │ ├── banque_du_caire.rb │ ├── nbg.rb │ ├── alex_bank.rb │ ├── egb.rb │ ├── ube.rb │ ├── cae.rb │ ├── al_ahli_bank_of_kuwait.rb │ ├── midb.rb │ ├── edbe.rb │ ├── blom.rb │ ├── aaib.rb │ ├── suez_canal_bank.rb │ ├── al_baraka_bank.rb │ ├── banque_misr.rb │ ├── adib.rb │ ├── cib.rb │ ├── nbe.rb │ └── bank.rb └── egp_rates.rb ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md └── README.md /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.4.0 2 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | --format documentation 3 | --color 4 | -------------------------------------------------------------------------------- /.hound.yml: -------------------------------------------------------------------------------- 1 | ruby: 2 | config_file: .rubocop.yml 3 | 4 | fail_on_violations: true 5 | -------------------------------------------------------------------------------- /spec/cassettes/CAE.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/CAE.gz -------------------------------------------------------------------------------- /spec/cassettes/CBE.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/CBE.gz -------------------------------------------------------------------------------- /spec/cassettes/CIB.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/CIB.gz -------------------------------------------------------------------------------- /spec/cassettes/EGB.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/EGB.gz -------------------------------------------------------------------------------- /spec/cassettes/NBE.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/NBE.gz -------------------------------------------------------------------------------- /spec/cassettes/NBG.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/NBG.gz -------------------------------------------------------------------------------- /spec/cassettes/UBE.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/UBE.gz -------------------------------------------------------------------------------- /spec/cassettes/AAIB.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/AAIB.gz -------------------------------------------------------------------------------- /spec/cassettes/ADIB.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/ADIB.gz -------------------------------------------------------------------------------- /spec/cassettes/Blom.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/Blom.gz -------------------------------------------------------------------------------- /spec/cassettes/EDBE.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/EDBE.gz -------------------------------------------------------------------------------- /spec/cassettes/MIDB.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/MIDB.gz -------------------------------------------------------------------------------- /spec/cassettes/SAIB.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/SAIB.gz -------------------------------------------------------------------------------- /spec/cassettes/AlexBank.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/AlexBank.gz -------------------------------------------------------------------------------- /pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | system('bundle exec rake rubocop') 4 | -------------------------------------------------------------------------------- /spec/cassettes/BanqueMisr.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/BanqueMisr.gz -------------------------------------------------------------------------------- /spec/cassettes/AlBarakaBank.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/AlBarakaBank.gz -------------------------------------------------------------------------------- /spec/cassettes/BanqueDuCaire.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/BanqueDuCaire.gz -------------------------------------------------------------------------------- /spec/cassettes/SuezCanalBank.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/SuezCanalBank.gz -------------------------------------------------------------------------------- /spec/cassettes/AlAhliBankOfKuwait.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a14m/EGP-Rates/HEAD/spec/cassettes/AlAhliBankOfKuwait.gz -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | tags 11 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | source 'https://rubygems.org' 3 | 4 | # Specify your gem's dependencies in EGP_Rates.gemspec 5 | gemspec 6 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | duplication: 4 | enabled: false 5 | config: 6 | languages: 7 | - ruby 8 | fixme: 9 | enabled: true 10 | rubocop: 11 | enabled: true 12 | ratings: 13 | paths: 14 | - "**.rb" 15 | exclude_paths: 16 | - spec/ 17 | -------------------------------------------------------------------------------- /spec/egp_rates/bank_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::Bank do 3 | subject(:bank) { described_class.new } 4 | 5 | describe '#exchange_rates' do 6 | it 'Raises NotImplementedError' do 7 | expect { bank.exchange_rates }.to raise_error NotImplementedError 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'bundler/setup' 5 | require 'egp_rates' 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. 9 | 10 | require 'pry' 11 | Pry.start 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.{yml,yaml,haml}] 13 | indent_size = 2 14 | 15 | [*.rb] 16 | indent_size = 2 17 | 18 | [*.{html,erb}] 19 | indent_size = 4 20 | 21 | [*.js] 22 | indent_size = 4 23 | 24 | [*.coffee] 25 | indent_size = 2 26 | 27 | [*.{css,scss,sass}] 28 | indent_size = 2 29 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'bundler/gem_tasks' 3 | require 'rspec/core/rake_task' 4 | require 'rubocop/rake_task' 5 | 6 | RSpec::Core::RakeTask.new(:spec) 7 | RuboCop::RakeTask.new do |task| 8 | task.requires << 'rubocop-rspec' 9 | end 10 | 11 | RSpec::Core::RakeTask.new(:spec_local) do |t| 12 | t.rspec_opts = '--tag ~live' 13 | end 14 | 15 | RSpec::Core::RakeTask.new(:spec_live) do |t| 16 | t.rspec_opts = '--tag live' 17 | end 18 | 19 | task default: :spec 20 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: rubocop-rspec 2 | 3 | AllCops: 4 | TargetRubyVersion: 2.3 5 | 6 | Metrics/AbcSize: 7 | Max: 20 8 | 9 | Metrics/MethodLength: 10 | Max: 20 11 | 12 | LiteralInInterpolation: 13 | Exclude: 14 | - 'spec/**/*' 15 | 16 | Style/SignalException: 17 | Enabled: false 18 | 19 | Style/AsciiComments: 20 | Enabled: false 21 | 22 | RSpec/MultipleExpectations: 23 | Max: 5 24 | 25 | RSpec/SubjectStub: 26 | Enabled: false 27 | 28 | RSpec/ExampleLength: 29 | Max: 20 30 | 31 | RSpec/NestedGroups: 32 | MaxNesting: 3 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 [Mad-Raz](https://github.com/mad-raz) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 3 | require 'pry' 4 | require 'webmock/rspec' 5 | require 'vcr' 6 | require 'simplecov' 7 | SimpleCov.start do 8 | add_filter '/spec/' 9 | add_filter '/.bundle/' 10 | end 11 | 12 | require 'egp_rates' 13 | 14 | WebMock.disable_net_connect! 15 | 16 | VCR.configure do |config| 17 | config.cassette_library_dir = 'spec/cassettes' 18 | config.hook_into :webmock 19 | config.allow_http_connections_when_no_cassette = false 20 | config.configure_rspec_metadata! 21 | config.default_cassette_options[:serialize_with] = :compressed 22 | end 23 | 24 | RSpec.configure do |config| 25 | config.order = 'random' 26 | 27 | config.around(:each) do |ex| 28 | if ex.metadata.key?(:live) 29 | WebMock.allow_net_connect! 30 | VCR.turned_off { ex.run } 31 | WebMock.disable_net_connect! 32 | elsif ex.metadata.key?(:no_vcr) 33 | VCR.turned_off { ex.run } 34 | elsif ex.metadata.key?(:vcr) 35 | VCR.configuration.default_cassette_options[:record] = :once 36 | ex.run 37 | else 38 | ex.run 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /wercker.yml: -------------------------------------------------------------------------------- 1 | # This references the default Ruby container from 2 | # the Docker Hub. 3 | # https://registry.hub.docker.com/_/ruby/ 4 | # If you want to use a specific version you would use a tag: 5 | # ruby:2.2.2 6 | box: ruby:2.3 7 | # You can also use services such as databases. Read more on our dev center: 8 | # http://devcenter.wercker.com/docs/services/index.html 9 | # services: 10 | # - postgres 11 | # http://devcenter.wercker.com/docs/services/postgresql.html 12 | 13 | # - mongo 14 | # http://devcenter.wercker.com/docs/services/mongodb.html 15 | 16 | # This is the build pipeline. Pipelines are the core of wercker 17 | # Read more about pipelines on our dev center 18 | # http://devcenter.wercker.com/docs/pipelines/index.html 19 | build: 20 | # Steps make up the actions in your pipeline 21 | # Read more about steps on our dev center: 22 | # http://devcenter.wercker.com/docs/steps/index.html 23 | steps: 24 | - bundle-install 25 | - script: 26 | name: rspec local 27 | code: bundle exec rspec -t ~live 28 | - script: 29 | name: CodeClimate 30 | code: bundle exec codeclimate-test-reporter 31 | - script: 32 | name: rspec live testing 33 | code: bundle exec rspec -t live -f progress 34 | -------------------------------------------------------------------------------- /EGP_Rates.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # frozen_string_literal: true 3 | lib = File.expand_path('../lib', __FILE__) 4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'EGP_Rates' 8 | spec.version = '1.1.2' 9 | spec.authors = ['Ahmed Abdel-Razzak'] 10 | spec.email = ['abdelrazzak.ahmed@gmail.com'] 11 | 12 | spec.summary = 'Scrape EGP exchange rate from different EG Banks' 13 | spec.homepage = 'https://github.com/mad-raz/EGP-Rates' 14 | spec.license = 'MIT' 15 | spec.required_ruby_version = '~>2.3' 16 | 17 | spec.files = Dir['lib/**/*.rb'] + Dir['spec/**/*'] 18 | spec.require_paths = ['lib'] 19 | 20 | spec.add_development_dependency 'bundler' 21 | spec.add_development_dependency 'rake' 22 | spec.add_development_dependency 'rspec' 23 | spec.add_development_dependency 'pry-byebug' 24 | spec.add_development_dependency 'rubocop' 25 | spec.add_development_dependency 'rubocop-rspec' 26 | spec.add_development_dependency 'vcr' 27 | spec.add_development_dependency 'webmock' 28 | spec.add_development_dependency 'simplecov' 29 | spec.add_development_dependency 'codeclimate-test-reporter' 30 | 31 | spec.add_dependency 'oga', '~>2.7' 32 | end 33 | -------------------------------------------------------------------------------- /lib/egp_rates/cbe.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # Central Bank of Egypt 4 | class CBE < EGPRates::Bank 5 | def initialize 6 | @sym = :CBE 7 | @uri = URI.parse('http://www.cbe.org.eg/en/EconomicResearch/Statistics/Pages/ExchangeRatesListing.aspx') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | private 20 | 21 | # Send the request to the URL and retrun raw data of the response 22 | # @return [Enumerator::Lazy] with the table row in HTML that evaluates to 23 | # [ 24 | # "US Dollar \r\n ", "15.7297", "16.3929", 25 | # "Euro \r\n, ", "17.5308", "18.1938", 26 | # ... 27 | # ] 28 | # 29 | def raw_exchange_rates 30 | table_rows = Oga.parse_html(response.body).css('tbody').last&.children 31 | # CBE porvide 9 currencies on the home page 32 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 9 33 | table_rows.lazy.map(&:children).map { |cell| cell.map(&:text) } 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/egp_rates/banque_du_caire.rb: -------------------------------------------------------------------------------- 1 | 2 | # frozen_string_literal: true 3 | module EGPRates 4 | # Banque Du Caire 5 | class BanqueDuCaire < EGPRates::Bank 6 | def initialize 7 | @sym = :BanqueDuCaire 8 | @uri = URI.parse('http://www.banqueducaire.com/English/MarketUpdates/Pages/CurrencyExchange.aspx') 9 | end 10 | 11 | # @return [Hash] of exchange rates for selling and buying 12 | # { 13 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 14 | # { buy: { SYM: rate }, { SYM: rate }, ... } 15 | # } 16 | def exchange_rates 17 | @exchange_rates ||= parse(raw_exchange_rates) 18 | end 19 | 20 | private 21 | 22 | # Send the request to the URL and retrun raw data of the response 23 | # @return [Enumerator::Lazy] with the table row in HTML that evaluates to 24 | # [ 25 | # ["US DOLLAR", "15.35", "16.25"], 26 | # ["EURO", "16.6194", "17.7499"], 27 | # ... 28 | # ] 29 | # 30 | def raw_exchange_rates 31 | # Banque Du Caire provide 17 currencies only 32 | table_rows = Oga.parse_html(response.body).css('table.curTbl tr') 33 | # But they have 1 empty and 1 header elements 34 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 19 35 | table_rows.lazy.drop(2).map(&:children).map { |cell| cell.map(&:text) } 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/egp_rates/nbg.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # National Bank of Greece (NBG) 4 | class NBG < EGPRates::Bank 5 | def initialize 6 | @sym = :NBG 7 | @uri = URI.parse('http://www.nbg.com.eg/en/exchange-rates') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | private 20 | 21 | # Send the request to the URL and retrun raw data of the response 22 | # @return [Enumerator::Lazy] with the table row in HTML that evaluates to 23 | # [ 24 | # ["", "US Dollar", "", "USD", "", "17.2000", "", "17.8000", ...] 25 | # ["", "Euro", "", "EUR", "", "18.1030", "", "18.3039", ... ] 26 | # ... 27 | # ] 28 | def raw_exchange_rates 29 | table_rows = Oga.parse_html(response.body).css('.row_exchange') 30 | # NBG porvide 9 currencies (and 2 header rows) 31 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 11 32 | table_rows.lazy.drop(2).map(&:children).map do |cell| 33 | cell.map(&:text).map(&:strip) 34 | end 35 | end 36 | 37 | # Parse the #raw_exchange_rates returned in response 38 | # @param [Array] of the raw_data scraped 39 | # @return [Hash] of exchange rates for selling and buying 40 | # { 41 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 42 | # { buy: { SYM: rate }, { SYM: rate }, ... } 43 | # } 44 | def parse(raw_data) 45 | raw_data.each_with_object(sell: {}, buy: {}) do |row, result| 46 | sell_rate = row[7].to_f 47 | buy_rate = row[5].to_f 48 | currency = row[3].to_sym 49 | 50 | result[:sell][currency] = sell_rate 51 | result[:buy][currency] = buy_rate 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Not released yet 2 | # v1.1.2 3 | - Fix NBE to use ISO symbols for `BHD`, `QAR` [PR #27](https://github.com/mad-raz/EGP-Rates/pull/27) 4 | 5 | # v1.1.1 6 | - Follow 301 redirect pages on banks 7 | 8 | # v1.1.0 9 | - Remove SAIB support (they provide the rates as image) 10 | 11 | - Ignore reporting bundled directory from coverage (locally) 12 | - Add documentation on how to run different tags in test suite 13 | - Add rake tasks to run different tags on test suite (live/local) 14 | - Fix LICENSE file to be picked up by github and shown in repo status 15 | - Fix wrong link in contribution guide 16 | - Update LICENCE year 17 | 18 | # v1.0.7 19 | - Allow development till ruby v.3 20 | - Fix documentation on require gem and run the test suite 21 | 22 | # v1.0.6 23 | - Fix ADIB page changes 24 | 25 | # v1.0.5 26 | - Add missing require to avoid class resolving Net::HTTP name issue 27 | - Add missing require to avoid class resolving URI name issue 28 | - Replace yml cassettes with gz to make gem size smaller 29 | - Fix AlAhliBankofKuwait to scrape their new website 30 | - Change the text when Currency "Not Available" to "N/A" 31 | - Update README adding a link to the CLI 32 | 33 | # v1.0.0 34 | - Support Export Development Bank of Egypt (EDBE) 35 | - Support Bank of Alexandria (AlexBank) 36 | - Support Blom Bank Egypt (Blom) 37 | - Support Abu Dhabi Islamic Bank (ADIB) 38 | - Support Egyptian Gulf Bank (EGB) 39 | - Support National Bank of Greece (NBG) 40 | - Support Faisal Islamic Bank 41 | - Add API methods to easily interact with the gem. 42 | - Fix BanqueMisr swapping CHF and SEK exchange rates 43 | 44 | # v0.3.0 45 | - Support Banque Du Caire 46 | - Support Banque Misr 47 | - Support Suez Canal Bank 48 | - Support Al Baraka Bank 49 | - Support Al Ahli Bank of Kuwait 50 | - Support SAIB 51 | - Support MIDB 52 | - Support The United Bank of Egypt 53 | - Support Crédit Agricole Egypt (CAE) 54 | 55 | # v0.2.0 56 | - Support CIB 57 | - Support AAIB 58 | 59 | # v0.1.0 60 | - Support CBE 61 | - Support NBE 62 | -------------------------------------------------------------------------------- /lib/egp_rates/alex_bank.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # Bank of Alexandria 4 | class AlexBank < EGPRates::Bank 5 | def initialize 6 | @sym = :AlexBank 7 | @uri = URI.parse('https://www.alexbank.com/En/Home/ExchangeRates') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | private 20 | 21 | # Send the request to the URL and retrun raw data of the response 22 | # @return [Enumerator::Lazy] with the table row in HTML that evaluates to 23 | # ["", "US Dollar", "", ..., "17.10000", "", "17.50000", ""] 24 | # ["", "British Pound", "", ..., "21.12363", "", "21.60025", ""] 25 | # ... 26 | # 27 | def raw_exchange_rates 28 | # AlexBank provide 17 currencies (and 1 ) 29 | table_rows = Oga.parse_html(response.body).css('.exchangerate-table tr') 30 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 18 31 | table_rows.lazy.drop(1).map(&:children).map do |cell| 32 | cell.map(&:text).map(&:strip) 33 | end 34 | end 35 | 36 | # Parse the #raw_exchange_rates returned in response 37 | # @param [Array] of the raw_data scraped 38 | # @return [Hash] of exchange rates for selling and buying 39 | # { 40 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 41 | # { buy: { SYM: rate }, { SYM: rate }, ... } 42 | # } 43 | def parse(raw_data) 44 | raw_data.each_with_object(sell: {}, buy: {}) do |row, result| 45 | sell_rate = row[9].to_f 46 | buy_rate = row[7].to_f 47 | currency = currency_symbol(row[1]) 48 | 49 | result[:sell][currency] = sell_rate 50 | result[:buy][currency] = buy_rate 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/egp_rates/egb.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # Egyptian Gulf Bank (EGB) 4 | class EGB < EGPRates::Bank 5 | def initialize 6 | @sym = :EGB 7 | @uri = URI.parse('https://eg-bank.com/ExchangeRates/ExchangeRates/') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | private 20 | 21 | # Send the request to the URL and retrun raw data of the response 22 | # @return [Enumerator::Lazy] with the table row in HTML that evaluates to 23 | # [ 24 | # ["", "USD", "", "17.00", "", "17.75", ""], 25 | # ["", "EUR", "", "17.9639", "", "18.8896", ""], 26 | # ["", "GBP", "", "20.9049", "", "22.0739", ""], 27 | # ... 28 | # ] 29 | def raw_exchange_rates 30 | table_rows = Oga.parse_html(response.body).css('.row.row-wrapper') 31 | # EGB porvide 17 currencies (and a header row) 32 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 18 33 | table_rows.lazy.drop(1).map(&:children).map do |cell| 34 | cell.map(&:text).map(&:strip) 35 | end 36 | end 37 | 38 | # Parse the #raw_exchange_rates returned in response 39 | # @param [Array] of the raw_data scraped 40 | # @return [Hash] of exchange rates for selling and buying 41 | # { 42 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 43 | # { buy: { SYM: rate }, { SYM: rate }, ... } 44 | # } 45 | def parse(raw_data) 46 | raw_data.each_with_object(sell: {}, buy: {}) do |row, result| 47 | sell_rate = row[5].to_f 48 | buy_rate = row[3].to_f 49 | currency = row[1].to_sym 50 | 51 | result[:sell][currency] = sell_rate 52 | result[:buy][currency] = buy_rate 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/egp_rates/ube.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # The United Bank of Egypt (UBE) 4 | class UBE < EGPRates::Bank 5 | def initialize 6 | @sym = :UBE 7 | @uri = URI.parse('http://www.theubeg.com/fxRate') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | private 20 | 21 | # Send the request to the URL and retrun raw data of the response 22 | # @return [Enumerator::Lazy] with the table row in HTML that evaluates to 23 | # [ 24 | # [ 25 | # "\r", "U.S. Dollar", "\r", "USD", "\r", "17.28", "\r", "17.55", "\r" 26 | # ], [ 27 | # "\r", "Euro", "\r", "EUR", "\r", "18.265", "\r", "18.6644", "\r" 28 | # ] 29 | # ... 30 | # ] 31 | def raw_exchange_rates 32 | table_rows = Oga.parse_html(response.body).css('table tr') 33 | # UBE porvide 6 currencies on the home page but with header 34 | # and an empty row in the end 35 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 7 36 | table_rows.lazy.drop(1).map(&:children).map { |cell| cell.map(&:text) } 37 | end 38 | 39 | # Parse the #raw_exchange_rates returned in response 40 | # @param [Array] of the raw_data scraped 41 | # @return [Hash] of exchange rates for selling and buying 42 | # { 43 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 44 | # { buy: { SYM: rate }, { SYM: rate }, ... } 45 | # } 46 | def parse(raw_data) 47 | raw_data.each_with_object(sell: {}, buy: {}) do |row, result| 48 | sell_rate = row[7].to_f 49 | buy_rate = row[5].to_f 50 | currency = row[3].to_sym 51 | 52 | result[:sell][currency] = sell_rate 53 | result[:buy][currency] = buy_rate 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/egp_rates/cae.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # Crédit Agricole Egypt (CAE) 4 | class CAE < EGPRates::Bank 5 | def initialize 6 | @sym = :CAE 7 | @uri = URI.parse('https://www.ca-egypt.com/en/personal-banking/') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | private 20 | 21 | # Send the request to the URL and retrun raw data of the response 22 | # @return [Enumerator::Lazy] with the table row in HTML that evaluates to 23 | # [ 24 | # ["QAR", "4.7367", "4.9294"], 25 | # ["USD", "17.25", "17.95"] 26 | # ... 27 | # ] 28 | def raw_exchange_rates 29 | table_rows = Oga.parse_html(response.body).css('#f_box option') 30 | # CAE porvide 17 currencies on the home page but with header 31 | # and an empty row in the end 32 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 17 33 | table_rows.lazy.map do |row| 34 | [ 35 | row.text, 36 | row.attribute('data-buy').value, 37 | row.attribute('data-sell').value 38 | ] 39 | end 40 | end 41 | 42 | # Parse the #raw_exchange_rates returned in response 43 | # @param [Array] of the raw_data scraped 44 | # @return [Hash] of exchange rates for selling and buying 45 | # { 46 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 47 | # { buy: { SYM: rate }, { SYM: rate }, ... } 48 | # } 49 | def parse(raw_data) 50 | raw_data.each_with_object(sell: {}, buy: {}) do |row, result| 51 | sell_rate = row[2].to_f 52 | buy_rate = row[1].to_f 53 | currency = row[0].to_sym 54 | 55 | result[:sell][currency] = sell_rate 56 | result[:buy][currency] = buy_rate 57 | end 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /lib/egp_rates/al_ahli_bank_of_kuwait.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # Al Ahli Bank Of Kuwait 4 | class AlAhliBankOfKuwait < EGPRates::Bank 5 | def initialize 6 | @sym = :AlAhliBankOfKuwait 7 | @uri = URI.parse('http://www.abkegypt.com/rates_abk.aspx') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | private 20 | 21 | # Send the request to the URL and retrun raw data of the response 22 | # @return [Enumerator::Lazy] with the table row in HTML that evaluates to 23 | # [ 24 | # [ 25 | # "", "EUR", "", "17.094800", "", "17.528100", "", ... 26 | # ], [ 27 | # "", "CHF", "", "15.983800", "", "16.395900", "", ... 28 | # ] 29 | # ... 30 | # ] 31 | # 32 | def raw_exchange_rates 33 | table_rows = Oga.parse_html(response.body).css('.ORANGE_TEXT, .BLUE_TEXT') 34 | # AlAhliBankOfKuwait porvide 8 currencies on the home page 35 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 8 36 | table_rows.lazy.map(&:children).map do |cell| 37 | cell.map(&:text).map(&:strip) 38 | end 39 | end 40 | 41 | # Parse the #raw_exchange_rates returned in response 42 | # @param [Array] of the raw_data scraped 43 | # @return [Hash] of exchange rates for selling and buying 44 | # { 45 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 46 | # { buy: { SYM: rate }, { SYM: rate }, ... } 47 | # } 48 | def parse(raw_data) 49 | raw_data.each_with_object(sell: {}, buy: {}) do |row, result| 50 | sell_rate = row[9].to_f 51 | buy_rate = row[7].to_f 52 | currency = row[1].to_sym 53 | 54 | result[:sell][currency] = sell_rate 55 | result[:buy][currency] = buy_rate 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/egp_rates/midb.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # Misr Iran Development Bank (MIDB) 4 | class MIDB < EGPRates::Bank 5 | def initialize 6 | @sym = :MIDB 7 | @uri = URI.parse('http://www.midb.com.eg/currency.aspx') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | private 20 | 21 | # Send the request to the URL and retrun raw data of the response 22 | # @return [Enumerator::Lazy] with the table row in HTML that evaluates to 23 | # [ 24 | # [ 25 | # "\r\n\t", "USD", "17.5", "17.05", "17.5", "17.05", "\r\n\t\t" 26 | # ], [ 27 | # "\r\n\t", "GBP", "21.6866", "21.0027", "21.6866", "21.0027", ... 28 | # ] 29 | # ... 30 | # ] 31 | def raw_exchange_rates 32 | table_rows = Oga.parse_html(response.body) 33 | .css('#MainContent_grdcurrency tr') 34 | # MIDB porvide 7 currencies on the home page but with header 35 | # and an empty row in the end 36 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 8 37 | table_rows.lazy.drop(1).map(&:children).map { |cell| cell.map(&:text) } 38 | end 39 | 40 | # Parse the #raw_exchange_rates returned in response 41 | # @param [Array] of the raw_data scraped 42 | # @return [Hash] of exchange rates for selling and buying 43 | # { 44 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 45 | # { buy: { SYM: rate }, { SYM: rate }, ... } 46 | # } 47 | def parse(raw_data) 48 | raw_data.each_with_object(sell: {}, buy: {}) do |row, result| 49 | sell_rate = row[2].to_f 50 | buy_rate = row[3].to_f 51 | currency = row[1].to_sym 52 | 53 | result[:sell][currency] = sell_rate 54 | result[:buy][currency] = buy_rate 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/egp_rates/edbe.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # Export Development Bank of Egypt (EDBE) 4 | class EDBE < EGPRates::Bank 5 | def initialize 6 | @sym = :EDBE 7 | @uri = URI.parse('http://www.edbebank.com/EN/BankingServices/TreasuryFiles/exchangerates.xml') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | private 20 | 21 | # Send the request to the URL and retrun raw data of the response 22 | # @return [Array] 23 | # [ 24 | # Attribute(name: "USDBbuy" value: "17.2500"), 25 | # Attribute(name: "USDBsell" value: "17.7000"), 26 | # Attribute(name: "USDTbuy" value: "17.2500"), 27 | # Attribute(name: "USDTsell" value: "17.6000"), 28 | # ... 29 | # ] 30 | def raw_exchange_rates 31 | table_rows = Oga.parse_xml(response.body).xpath('Details/rates') 32 | # EDBE porvide 5 as 20 XML attributes 33 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 1 34 | table_rows.flat_map(&:attributes).take(20) 35 | end 36 | 37 | # Parse the #raw_exchange_rates returned in response 38 | # @param [Array] of the raw_data scraped 39 | # @return [Hash] of exchange rates for selling and buying 40 | # { 41 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 42 | # { buy: { SYM: rate }, { SYM: rate }, ... } 43 | # } 44 | def parse(raw_data) 45 | raw_data.each_with_object(sell: {}, buy: {}) do |row, result| 46 | next unless row.name[3] == 'B' 47 | currency = row.name[0..2].to_sym 48 | action = row.name[4..-1].to_sym 49 | action_rate = row.value.to_f 50 | action_rate = (action_rate * 100).round(4) if currency == :JPY 51 | 52 | result[action][currency] = action_rate 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/egp_rates/blom.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # Blom Bank Egypt 4 | class Blom < EGPRates::Bank 5 | def initialize 6 | @sym = :Blom 7 | @uri = URI.parse('http://www.blombankegypt.com/BlomEgypt/Exchange-rates') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | private 20 | 21 | # Send the request to the URL and retrun raw data of the response 22 | # @return [Enumerator::Lazy] with the table row in HTML that evaluates to 23 | # [ 24 | # ["", "USD   02", "", "", "", ..., "17.0000", "", "17.7500", ""] 25 | # ["", "EURO  30", "", "", "", ..., "17.9690", "", "18.8896", ""] 26 | # ... 27 | # ] 28 | def raw_exchange_rates 29 | table_rows = Oga.parse_html(response.body).css('.tableHolder').first 30 | table_rows = table_rows&.css('tr') 31 | # Blom porvide 14 currencies on the home page (and 2 ) 32 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 16 33 | table_rows.lazy.drop(2).map(&:children).map do |row| 34 | row.map(&:text).map(&:strip) 35 | end 36 | end 37 | 38 | # Parse the #raw_exchange_rates returned in response 39 | # @param [Array] of the raw_data scraped 40 | # @return [Hash] of exchange rates for selling and buying 41 | # { 42 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 43 | # { buy: { SYM: rate }, { SYM: rate }, ... } 44 | # } 45 | def parse(raw_data) 46 | raw_data.each_with_object(sell: {}, buy: {}) do |row, result| 47 | sell_rate = row[11].to_f 48 | buy_rate = row[9].to_f 49 | currency = row[1][0..2].to_sym 50 | currency = :JPY if currency == '100'.to_sym 51 | 52 | result[:sell][currency] = sell_rate 53 | result[:buy][currency] = buy_rate 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/egp_rates/aaib.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # Arab African International Bank 4 | class AAIB < EGPRates::Bank 5 | def initialize 6 | @sym = :AAIB 7 | @uri = URI.parse('http://aaib.com/services/rates') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | private 20 | 21 | # Send the request to the URL and retrun raw data of the response 22 | # @return [Enumerator::Lazy] with the table row in HTML that evaluates to 23 | # [ 24 | # [ 25 | # "\r\n ", 26 | # "US DOLLAR" 27 | # "\r\n " 28 | # "15.9" 29 | # "\r\n " 30 | # "16.05" 31 | # "\r\n" 32 | # ], [ 33 | # "\r\n " 34 | # "EURO CURRENCY" 35 | # "\r\n " 36 | # "17.2213" 37 | # "\r\n " 38 | # "17.5314" 39 | # "\r\n " 40 | # ], [ 41 | # ... 42 | # ] 43 | def raw_exchange_rates 44 | raw = response.body&.gsub("\u0000", '') 45 | # AAIB provide 7 currencies only 46 | table_rows = Oga.parse_html(raw).css('#rates-table tr') 47 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 7 48 | table_rows.lazy.map(&:children).map { |cell| cell.map(&:text) } 49 | end 50 | 51 | # Parse the #raw_exchange_rates returned in response 52 | # @param [Array] of the raw_data scraped 53 | # @return [Hash] of exchange rates for selling and buying 54 | # { 55 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 56 | # { buy: { SYM: rate }, { SYM: rate }, ... } 57 | # } 58 | def parse(raw_data) 59 | raw_data.each_with_object(sell: {}, buy: {}) do |row, result| 60 | sell_rate = row[5].to_f 61 | buy_rate = row[3].to_f 62 | currency = currency_symbol(row[1]) 63 | 64 | result[:sell][currency] = sell_rate 65 | result[:buy][currency] = buy_rate 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/egp_rates/suez_canal_bank.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # Suez Canal Bank 4 | class SuezCanalBank < EGPRates::Bank 5 | def initialize 6 | @sym = :SuezCanalBank 7 | @uri = URI.parse('http://scbank.com.eg/CurrencyAll.aspx') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | # Send the request to the URL and retrun raw data of the response 20 | # @return [Enumerator::Lazy] with the table row in HTML that evaluates to 21 | # [ 22 | # "\r\n ", "\r\n ", "\r\n ", "\r\n US Dollar ", "\r\n ", 23 | # "\r\n 15.1500", "\r\n ", "\r\n 15.8000", "\r\n ", ... 24 | # ], [ 25 | # "\r\n ", "\r\n ", "\r\n ", "\r\n Sterling Pound", "\r\n ", 26 | # "\r\n 18.5933", "\r\n ", "\r\n 20.0448", "\r\n ", ... 27 | # ], 28 | # rubocop:disable Style/MultilineMethodCallIndentation 29 | def raw_exchange_rates 30 | # Suez Canal Bank provides 13 currencies only 31 | table_rows = Oga.parse_html(response.body)\ 32 | .css('#Table_01 tr:nth-child(4) > td:nth-child(2) > table tr') 33 | # But they have 2 used for the table headers 34 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 15 35 | table_rows.lazy.drop(2).map(&:children).map { |cell| cell.map(&:text) } 36 | end 37 | # rubocop:enable Style/MultilineMethodCallIndentation 38 | 39 | # Parse the #raw_exchange_rates returned in response 40 | # @param [Array] of the raw_data scraped 41 | # @return [Hash] of exchange rates for selling and buying 42 | # { 43 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 44 | # { buy: { SYM: rate }, { SYM: rate }, ... } 45 | # } 46 | def parse(raw_data) 47 | raw_data.each_with_object(sell: {}, buy: {}) do |row, result| 48 | sell_rate = row[7].to_f 49 | buy_rate = row[5].to_f 50 | currency = currency_symbol(row[3].strip) 51 | 52 | result[:sell][currency] = sell_rate 53 | result[:buy][currency] = buy_rate 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /spec/egp_rates/edbe_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::EDBE do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 5 8 | expect(bank.exchange_rates[:sell].size).to eq 5 9 | end 10 | 11 | describe '.new' do 12 | it 'initialize instance variables' do 13 | expect(bank.sym).to eq :EDBE 14 | expect(bank.instance_variable_get(:@uri)).to be_a URI 15 | end 16 | end 17 | 18 | describe '#exchange_rates' do 19 | it 'calls #parse with #raw_exchange_rates' do 20 | expect(bank).to receive(:raw_exchange_rates) 21 | expect(bank).to receive(:parse) 22 | bank.exchange_rates 23 | end 24 | end 25 | 26 | describe '#raw_exchange_rates' do 27 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 28 | stub_request(:get, /.*edbebank.*/).to_return(body: '', status: 500) 29 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 30 | EGPRates::ResponseError, '500' 31 | end 32 | 33 | it 'raises ResponseError if HTML structure changed', :no_vcr do 34 | stub_request(:get, /.*edbebank.*/).to_return(body: '', status: 200) 35 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 36 | EGPRates::ResponseError, 'Unknown HTML' 37 | end 38 | 39 | it 'returns <#Array> of 20 values', vcr: { cassette_name: :EDBE } do 40 | lazy_enumerator = bank.send(:raw_exchange_rates) 41 | expect(lazy_enumerator).to be_a Array 42 | expect(lazy_enumerator.size).to eq 20 43 | end 44 | end 45 | 46 | describe '#parse', vcr: { cassette_name: :EDBE } do 47 | let(:raw_data) { bank.send(:raw_exchange_rates) } 48 | 49 | it 'returns sell: hash of selling prices' do 50 | expect(bank.send(:parse, raw_data)[:sell]).to match( 51 | CHF: 17.8749, 52 | EUR: 19.2402, 53 | GBP: 22.9266, 54 | JPY: 15.8884, 55 | USD: 18.0 56 | ) 57 | end 58 | 59 | it 'returns buy: hash of buying prices' do 60 | expect(bank.send(:parse, raw_data)[:buy]).to match( 61 | CHF: 17.5064, 62 | EUR: 18.824, 63 | GBP: 22.2652, 64 | JPY: 15.5153, 65 | USD: 17.72 66 | ) 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/egp_rates/al_baraka_bank.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # Central Bank of Egypt 4 | class AlBarakaBank < EGPRates::Bank 5 | def initialize 6 | @sym = :AlBarakaBank 7 | @uri = URI.parse('http://www.albaraka-bank.com.eg/banking-services/exchange-rates.aspx') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | private 20 | 21 | # Send the request to the URL and retrun raw data of the response 22 | # @return [Enumerator::Lazy] with the table row in HTML that evaluates to 23 | # [ 24 | # [ 25 | # "USD", "1596.00", "1650.00", ... 26 | # ], [ 27 | # "GBP", "1953.06", "2079.83", ... 28 | # ], [ 29 | # "EURO", "1672.32", "1787.11", ... 30 | # ] 31 | # ... 32 | # ] 33 | # 34 | def raw_exchange_rates 35 | table_rows = Oga.parse_html(response.body).css('table').first&.children 36 | # AlBarakaBank porvide 7 currencies on the home page 37 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 8 38 | table_rows.lazy.drop(1).map(&:children).map { |cell| cell.map(&:text) } 39 | end 40 | 41 | # Parse the #raw_exchange_rates returned in response 42 | # @param [Array] of the raw_data scraped 43 | # [ 44 | # [ 'Currency_1', 'BuyRate', 'SellRate', ... ], 45 | # [ 'Currency_2', 'BuyRate', 'SellRate', ... ], 46 | # [ 'Currency_3', 'BuyRate', 'SellRate', ... ], 47 | # ... 48 | # ] 49 | # 50 | # @return [Hash] of exchange rates for selling and buying 51 | # { 52 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 53 | # { buy: { SYM: rate }, { SYM: rate }, ... } 54 | # } 55 | def parse(raw_data) 56 | raw_data.each_with_object(sell: {}, buy: {}) do |row, result| 57 | sell_rate = (row[2].to_f / 100).round(4) 58 | buy_rate = (row[1].to_f / 100).round(4) 59 | currency = currency_symbol(row[0]) 60 | 61 | result[:sell][currency] = sell_rate 62 | result[:buy][currency] = buy_rate 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /spec/egp_rates/ube_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::UBE do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 6 8 | expect(bank.exchange_rates[:sell].size).to eq 6 9 | end 10 | 11 | describe '.new' do 12 | it 'initialize instance variables' do 13 | expect(bank.sym).to eq :UBE 14 | expect(bank.instance_variable_get(:@uri)).to be_a URI 15 | end 16 | end 17 | 18 | describe '#exchange_rates' do 19 | it 'calls #parse with #raw_exchange_rates' do 20 | expect(bank).to receive(:raw_exchange_rates) 21 | expect(bank).to receive(:parse) 22 | bank.exchange_rates 23 | end 24 | end 25 | 26 | describe '#raw_exchange_rates' do 27 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 28 | stub_request(:get, /.*theubeg.*/).to_return(body: '', status: 500) 29 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 30 | EGPRates::ResponseError, '500' 31 | end 32 | 33 | it 'raises ResponseError if HTML structure changed', :no_vcr do 34 | stub_request(:get, /.*theubeg.*/).to_return(body: '', status: 200) 35 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 36 | EGPRates::ResponseError, 'Unknown HTML' 37 | end 38 | 39 | it 'returns <#Enumerator::Lazy> of 6 rows', vcr: { cassette_name: :UBE } do 40 | lazy_enumerator = bank.send(:raw_exchange_rates) 41 | expect(lazy_enumerator).to be_a Enumerator::Lazy 42 | expect(lazy_enumerator.size).to eq 6 43 | end 44 | end 45 | 46 | describe '#parse', vcr: { cassette_name: :UBE } do 47 | let(:raw_data) { bank.send(:raw_exchange_rates) } 48 | 49 | it 'returns sell: hash of selling prices' do 50 | expect(bank.send(:parse, raw_data)[:sell]).to match( 51 | AED: 4.8734, 52 | EUR: 19.1333, 53 | GBP: 22.7992, 54 | KWD: 58.7174, 55 | SAR: 4.7731, 56 | USD: 17.9 57 | ) 58 | end 59 | 60 | it 'returns buy: hash of buying prices' do 61 | expect(bank.send(:parse, raw_data)[:buy]).to match( 62 | AED: 4.8191, 63 | EUR: 18.808, 64 | GBP: 22.2507, 65 | KWD: 58.0804, 66 | SAR: 4.695, 67 | USD: 17.7 68 | ) 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /spec/egp_rates/midb_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::MIDB do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 7 8 | expect(bank.exchange_rates[:sell].size).to eq 7 9 | end 10 | 11 | describe '.new' do 12 | it 'initialize instance variables' do 13 | expect(bank.sym).to eq :MIDB 14 | expect(bank.instance_variable_get(:@uri)).to be_a URI 15 | end 16 | end 17 | 18 | describe '#exchange_rates' do 19 | it 'calls #parse with #raw_exchange_rates' do 20 | expect(bank).to receive(:raw_exchange_rates) 21 | expect(bank).to receive(:parse) 22 | bank.exchange_rates 23 | end 24 | end 25 | 26 | describe '#raw_exchange_rates' do 27 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 28 | stub_request(:get, /.*midb.*/).to_return(body: '', status: 500) 29 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 30 | EGPRates::ResponseError, '500' 31 | end 32 | 33 | it 'raises ResponseError if HTML structure changed', :no_vcr do 34 | stub_request(:get, /.*midb.*/).to_return(body: '', status: 200) 35 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 36 | EGPRates::ResponseError, 'Unknown HTML' 37 | end 38 | 39 | it 'returns <#Enumerator::Lazy> of 7 rows', vcr: { cassette_name: :MIDB } do 40 | lazy_enumerator = bank.send(:raw_exchange_rates) 41 | expect(lazy_enumerator).to be_a Enumerator::Lazy 42 | expect(lazy_enumerator.size).to eq 7 43 | end 44 | end 45 | 46 | describe '#parse', vcr: { cassette_name: :MIDB } do 47 | let(:raw_data) { bank.send(:raw_exchange_rates) } 48 | 49 | it 'returns sell: hash of selling prices' do 50 | expect(bank.send(:parse, raw_data)[:sell]).to match( 51 | AED: 4.934, 52 | CHF: 17.925, 53 | EUR: 19.3346, 54 | GBP: 23.0696, 55 | JPY: 15.9611, 56 | SAR: 4.8322, 57 | USD: 18.05 58 | ) 59 | end 60 | 61 | it 'returns buy: hash of buying prices' do 62 | expect(bank.send(:parse, raw_data)[:buy]).to match( 63 | AED: 4.7094, 64 | CHF: 17.423, 65 | EUR: 18.7931, 66 | GBP: 22.4235, 67 | JPY: 15.5141, 68 | SAR: 4.6122, 69 | USD: 17.65 70 | ) 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /spec/egp_rates/nbg_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::NBG do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 9 8 | expect(bank.exchange_rates[:sell].size).to eq 9 9 | end 10 | 11 | describe '.new' do 12 | it 'initialize instance variables' do 13 | expect(bank.sym).to eq :NBG 14 | expect(bank.instance_variable_get(:@uri)).to be_a URI 15 | end 16 | end 17 | 18 | describe '#exchange_rates' do 19 | it 'calls #parse with #raw_exchange_rates' do 20 | expect(bank).to receive(:raw_exchange_rates) 21 | expect(bank).to receive(:parse) 22 | bank.exchange_rates 23 | end 24 | end 25 | 26 | describe '#raw_exchange_rates' do 27 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 28 | stub_request(:get, /.*nbg.*/).to_return(body: '', status: 500) 29 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 30 | EGPRates::ResponseError, '500' 31 | end 32 | 33 | it 'raises ResponseError if HTML structure changed', :no_vcr do 34 | stub_request(:get, /.*nbg.*/).to_return(body: '', status: 200) 35 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 36 | EGPRates::ResponseError, 'Unknown HTML' 37 | end 38 | 39 | it 'returns <#Enumerator::Lazy> of 9 rows', vcr: { cassette_name: :NBG } do 40 | lazy_enumerator = bank.send(:raw_exchange_rates) 41 | expect(lazy_enumerator).to be_a Enumerator::Lazy 42 | expect(lazy_enumerator.size).to eq 9 43 | end 44 | end 45 | 46 | describe '#parse', vcr: { cassette_name: :NBG } do 47 | let(:raw_data) { bank.send(:raw_exchange_rates) } 48 | 49 | it 'returns sell: hash of selling prices' do 50 | expect(bank.send(:parse, raw_data)[:sell]).to match( 51 | CAD: 13.7348, 52 | CHF: 18.0735, 53 | DKK: 2.6155, 54 | EUR: 19.454, 55 | GBP: 23.1813, 56 | JPY: 16.065, 57 | SAR: 4.8536, 58 | SEK: 1.983, 59 | USD: 18.2 60 | ) 61 | end 62 | 63 | it 'returns buy: hash of buying prices' do 64 | expect(bank.send(:parse, raw_data)[:buy]).to match( 65 | CAD: 13.1381, 66 | CHF: 17.2891, 67 | DKK: 2.4995, 68 | EUR: 18.5903, 69 | GBP: 21.9888, 70 | JPY: 15.3227, 71 | SAR: 4.666, 72 | SEK: 1.8968, 73 | USD: 17.5 74 | ) 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /spec/egp_rates/adib_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::ADIB do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 5 8 | expect(bank.exchange_rates[:sell].size).to eq 5 9 | expect(bank.exchange_rates[:buy].keys).to include( 10 | :AED, :EUR, :GBP, :SAR, :USD 11 | ) 12 | expect(bank.exchange_rates[:sell].keys).to include( 13 | :AED, :EUR, :GBP, :SAR, :USD 14 | ) 15 | end 16 | 17 | describe '.new' do 18 | it 'initialize instance variables' do 19 | expect(bank.sym).to eq :ADIB 20 | expect(bank.instance_variable_get(:@uri)).to be_a URI 21 | end 22 | end 23 | 24 | describe '#exchange_rates' do 25 | it 'calls #parse with #raw_exchange_rates' do 26 | expect(bank).to receive(:raw_exchange_rates) 27 | expect(bank).to receive(:parse) 28 | bank.exchange_rates 29 | end 30 | end 31 | 32 | describe '#raw_exchange_rates' do 33 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 34 | stub_request(:get, /.*adib.*/).to_return(body: '', status: 500) 35 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 36 | EGPRates::ResponseError, '500' 37 | end 38 | 39 | it 'raises ResponseError if HTML structure changed', :no_vcr do 40 | stub_request(:get, /.*adib.*/).to_return(body: '', status: 200) 41 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 42 | EGPRates::ResponseError, 'Unknown HTML' 43 | end 44 | 45 | it 'returns <#Enumerator::Lazy> of 5 rows', vcr: { cassette_name: :ADIB } do 46 | lazy_enumerator = bank.send(:raw_exchange_rates) 47 | expect(lazy_enumerator).to be_a Array 48 | expect(lazy_enumerator.size).to eq 5 49 | end 50 | end 51 | 52 | describe '#parse', vcr: { cassette_name: :ADIB } do 53 | let(:raw_data) { bank.send(:raw_exchange_rates) } 54 | 55 | it 'returns sell: hash of selling prices' do 56 | expect(bank.send(:parse, raw_data)[:sell]).to match( 57 | AED: 5.132, 58 | EUR: 19.7548, 59 | GBP: 23.1365, 60 | SAR: 5.0264, 61 | USD: 18.85 62 | ) 63 | end 64 | 65 | it 'returns buy: hash of buying prices' do 66 | expect(bank.send(:parse, raw_data)[:buy]).to match( 67 | AED: 4.9279, 68 | EUR: 18.8403, 69 | GBP: 22.1019, 70 | SAR: 4.8042, 71 | USD: 18.1 72 | ) 73 | end 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /spec/egp_rates/al_ahli_bank_of_kuwait_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::AlAhliBankOfKuwait do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 8 8 | expect(bank.exchange_rates[:sell].size).to eq 8 9 | end 10 | 11 | describe '.new' do 12 | it 'initialize instance variables' do 13 | expect(bank.sym).to eq :AlAhliBankOfKuwait 14 | expect(bank.instance_variable_get(:@uri)).to be_a URI 15 | end 16 | end 17 | 18 | describe '#exchange_rates' do 19 | it 'calls #parse with #raw_exchange_rates' do 20 | expect(bank).to receive(:raw_exchange_rates) 21 | expect(bank).to receive(:parse) 22 | bank.exchange_rates 23 | end 24 | end 25 | 26 | describe '#raw_exchange_rates' do 27 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 28 | stub_request(:get, /.*abkegypt.*/).to_return(body: '', status: 500) 29 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 30 | EGPRates::ResponseError, '500' 31 | end 32 | 33 | it 'raises ResponseError if HTML structure changed', :no_vcr do 34 | stub_request(:get, /.*abkegypt.*/).to_return(body: '', status: 200) 35 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 36 | EGPRates::ResponseError, 'Unknown HTML' 37 | end 38 | 39 | it 'returns <#Enumerator::Lazy> of 8 rows', 40 | vcr: { cassette_name: :AlAhliBankOfKuwait } do 41 | lazy_enumerator = bank.send(:raw_exchange_rates) 42 | expect(lazy_enumerator).to be_a Enumerator::Lazy 43 | expect(lazy_enumerator.size).to eq 8 44 | end 45 | end 46 | 47 | describe '#parse', vcr: { cassette_name: :AlAhliBankOfKuwait } do 48 | let(:raw_data) { bank.send(:raw_exchange_rates) } 49 | 50 | it 'returns sell: hash of selling prices' do 51 | expect(bank.send(:parse, raw_data)[:sell]).to match( 52 | AED: 4.9149, 53 | CHF: 17.8536, 54 | EUR: 19.2666, 55 | GBP: 22.9957, 56 | JPY: 0.159, 57 | KWD: 59.2192, 58 | SAR: 4.8127, 59 | USD: 18.05 60 | ) 61 | end 62 | 63 | it 'returns buy: hash of buying prices' do 64 | expect(bank.send(:parse, raw_data)[:buy]).to match( 65 | AED: 4.8326, 66 | CHF: 17.5395, 67 | EUR: 18.9375, 68 | GBP: 22.5958, 69 | JPY: 0.1562, 70 | KWD: 58.2349, 71 | SAR: 4.7327, 72 | USD: 17.75 73 | ) 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /lib/egp_rates/banque_misr.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # Banque Misr 4 | class BanqueMisr < EGPRates::Bank 5 | def initialize 6 | @sym = :BanqueMisr 7 | @uri = URI.parse('http://www.banquemisr.com/en/exchangerates') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | private 20 | 21 | # Send the request to the URL and retrun raw data of the response 22 | # @return [Enumerator::Lazy] with the table row in HTML that evaluates to 23 | # [ 24 | # ["US DOLLAR", "15.35", "15.91", "15.35", "15.91"], 25 | # ["EURO", "16.6256", "17.3698", "16.633898", "17.378493"] 26 | # ... 27 | # ] 28 | # 29 | def raw_exchange_rates 30 | # BanqueMisr provide 18 currencies (17 Used and CYPRUS POUND) 31 | # But they have 2 for headers 32 | table_rows = Oga.parse_html(response.body).css('.exchangeRates tbody tr') 33 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 20 34 | # remove the first 2 headers of the array and the last element 35 | # which is CYPRUS POUND which is not used anymore 36 | table_rows.lazy.drop(2).take(17).map(&:children).map do |cell| 37 | cell.map(&:text) 38 | end 39 | end 40 | 41 | # Convert currency string to ISO symbol 42 | # @param currency [String] "US Dollar" 43 | # @return [Symbol] :USD ISO currency name 44 | # rubocop:disable Metrics/CyclomaticComplexity 45 | def currency_symbol(currency) 46 | case currency 47 | when /UAE DIRHAM/ then :AED 48 | when /AUSTRALIA/ then :AUD 49 | when /BAHRAIN/ then :BHD 50 | when /CANADA/ then :CAD 51 | when /SWISS/ then :CHF 52 | when /DENMARK/ then :DKK 53 | when /EURO/ then :EUR 54 | when /GB POUND/ then :GBP 55 | when /JORDANIAN/ then :JOD 56 | when /JAPAN/ then :JPY 57 | when /KUWAIT/ then :KWD 58 | when /NORWAY/ then :NOK 59 | when /OMAN/ then :OMR 60 | when /QATARI/ then :QAR 61 | when /SAUDI/ then :SAR 62 | when /SWEDISH/ then :SEK 63 | when /US DOLLAR/ then :USD 64 | else fail ResponseError, "Unknown currency #{currency}" 65 | end 66 | end 67 | # rubocop:enable Metrics/CyclomaticComplexity 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/egp_rates/adib.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # Abu Dhabi Islamic Bank (ADIB) 4 | class ADIB < EGPRates::Bank 5 | def initialize 6 | @sym = :ADIB 7 | @uri = URI.parse('https://www.adib.eg/') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | private 20 | 21 | # Send the request to the URL and retrun raw data of the response 22 | # @return [Array] containing image url (currency) and exchange rates 23 | # [ 24 | # ["/media/246206/usd.png", [["Buy: 18.1000", "Sell: 18.85"]]], 25 | # ["/media/246211/gbp.png", [["Buy: 22.1019", "Sell: 23.1365"]]] 26 | # ... 27 | # ] 28 | def raw_exchange_rates 29 | table_rows = Oga.parse_html(response.body).css('#ratesContainer li') 30 | # ADIB porvide 5 currencies on the home page (and 4 rows of info) 31 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 5 32 | currencies(table_rows).zip(rates(table_rows)) 33 | end 34 | 35 | 36 | # Extract the currencies from the image components src attribute 37 | # @return [Array] containing the URL to image of the currency 38 | def currencies(table_rows) 39 | table_rows.lazy.map do |e| 40 | e.css('img').map { |img| img.attribute('src').value } 41 | end.force.flatten 42 | end 43 | 44 | # Extract the text descriping the exchange rates from content

nodes 45 | # @return [Array] text description for buy/sell rates 46 | def rates(table_rows) 47 | table_rows.map do |e| 48 | e.css('.content').map(&:text).map(&:strip).map do |txt| 49 | txt.split("\n").map(&:strip) 50 | end 51 | end 52 | end 53 | 54 | # Parse the #raw_exchange_rates returned in response 55 | # @param [Array] of the raw_data scraped 56 | # @return [Hash] of exchange rates for selling and buying 57 | # { 58 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 59 | # { buy: { SYM: rate }, { SYM: rate }, ... } 60 | # } 61 | def parse(raw_data) 62 | raw_data.each_with_object(sell: {}, buy: {}) do |row, result| 63 | currency = currency_symbol(row[0]) 64 | sell_rate = row[1][0][1][5..-1].to_f 65 | buy_rate = row[1][0][0][4..-1].to_f 66 | 67 | result[:sell][currency] = sell_rate 68 | result[:buy][currency] = buy_rate 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/egp_rates/cib.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # Commercial International Bank (CIB) 4 | class CIB < EGPRates::Bank 5 | def initialize 6 | @sym = :CIB 7 | @uri = URI.parse('http://www.cibeg.com/_layouts/15/LINKDev.CIB.CurrenciesFunds/FundsCurrencies.aspx/GetCurrencies') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | private 20 | 21 | # Send the request to URL and return the JSON response 22 | # @return [Hash] JSON response of the exchange rates 23 | # { 24 | # "d"=> [ 25 | # { 26 | # "__type"=>"LINKDev.CIB.CurrenciesFunds.CIBFund.CurrencyObject", 27 | # "CurrencyID"=>"USD", 28 | # "BuyRate"=>15.9, 29 | # "SellRate"=>16.05 30 | # }, { 31 | # "__type"=>"LINKDev.CIB.CurrenciesFunds.CIBFund.CurrencyObject", 32 | # "CurrencyID"=>"EUR", 33 | # "BuyRate"=>17.1904, 34 | # "SellRate"=>17.5234 35 | # }, { 36 | # ... 37 | # } 38 | # ] 39 | # } 40 | def raw_exchange_rates 41 | req = Net::HTTP::Post.new(@uri, 'Content-Type' => 'application/json') 42 | req.body = { lang: :en }.to_json 43 | response = Net::HTTP.start(@uri.hostname, @uri.port) do |http| 44 | http.request(req) 45 | end 46 | fail ResponseError, response.code unless response.is_a? Net::HTTPSuccess 47 | response = JSON.parse(response.body) 48 | 49 | # CIB provide 6 currencies only 50 | unless response['d'] && response['d'].size >= 6 51 | fail ResponseError, "Unknown JSON #{response}" 52 | end 53 | 54 | response 55 | rescue JSON::ParserError 56 | raise ResponseError, "Unknown JSON: #{response.body}" 57 | end 58 | 59 | # Parse the #raw_exchange_rates returned in response 60 | # @param [Array] of the raw_data scraped 61 | # @return [Hash] of exchange rates for selling and buying 62 | # { 63 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 64 | # { buy: { SYM: rate }, { SYM: rate }, ... } 65 | # } 66 | def parse(raw_data) 67 | raw_data['d'].each_with_object(sell: {}, buy: {}) do |obj, result| 68 | sell_rate = obj['SellRate'] 69 | buy_rate = obj['BuyRate'] 70 | currency = obj['CurrencyID'].to_sym 71 | 72 | result[:sell][currency] = sell_rate 73 | result[:buy][currency] = buy_rate 74 | end 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /spec/egp_rates/blom_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::Blom do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 14 8 | expect(bank.exchange_rates[:sell].size).to eq 14 9 | end 10 | 11 | describe '.new' do 12 | it 'initialize instance variables' do 13 | expect(bank.sym).to eq :Blom 14 | expect(bank.instance_variable_get(:@uri)).to be_a URI 15 | end 16 | end 17 | 18 | describe '#exchange_rates' do 19 | it 'calls #parse with #raw_exchange_rates' do 20 | expect(bank).to receive(:raw_exchange_rates) 21 | expect(bank).to receive(:parse) 22 | bank.exchange_rates 23 | end 24 | end 25 | 26 | describe '#raw_exchange_rates' do 27 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 28 | stub_request(:get, /.*blom.*/).to_return(body: '', status: 500) 29 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 30 | EGPRates::ResponseError, '500' 31 | end 32 | 33 | it 'raises ResponseError if HTML structure changed', :no_vcr do 34 | stub_request(:get, /.*blom.*/).to_return(body: '', status: 200) 35 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 36 | EGPRates::ResponseError, 'Unknown HTML' 37 | end 38 | 39 | it 'returns <#Enumerator::Lazy> of 14 rows', 40 | vcr: { cassette_name: :Blom } do 41 | lazy_enumerator = bank.send(:raw_exchange_rates) 42 | expect(lazy_enumerator).to be_a Enumerator::Lazy 43 | expect(lazy_enumerator.size).to eq 14 44 | end 45 | end 46 | 47 | describe '#parse', vcr: { cassette_name: :Blom } do 48 | let(:raw_data) { bank.send(:raw_exchange_rates) } 49 | 50 | it 'returns sell: hash of selling prices' do 51 | expect(bank.send(:parse, raw_data)[:sell]).to match( 52 | AED: 4.9006, 53 | BHD: 0.0, 54 | CAD: 13.5788, 55 | CHF: 17.8625, 56 | DKK: 2.5862, 57 | EUR: 19.2402, 58 | GBP: 22.9266, 59 | JPY: 15.88, 60 | KWD: 59.0164, 61 | NOK: 2.143, 62 | QAR: 0.0, 63 | SAR: 4.7992, 64 | SEK: 1.9644, 65 | USD: 18.0 66 | ) 67 | end 68 | 69 | it 'returns buy: hash of buying prices' do 70 | expect(bank.send(:parse, raw_data)[:buy]).to match( 71 | AED: 4.7781, 72 | BHD: 0.0, 73 | CAD: 13.1757, 74 | CHF: 17.3385, 75 | DKK: 2.5066, 76 | EUR: 18.6486, 77 | GBP: 22.0621, 78 | JPY: 15.3664, 79 | KWD: 57.541, 80 | NOK: 2.0741, 81 | QAR: 0.0, 82 | SAR: 4.6788, 83 | SEK: 1.9022, 84 | USD: 17.55 85 | ) 86 | end 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /lib/egp_rates.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'oga' 4 | require 'json' 5 | require 'uri' 6 | require 'net/http' 7 | require 'thread' 8 | require 'egp_rates/bank' 9 | require 'egp_rates/cbe' 10 | require 'egp_rates/nbe' 11 | require 'egp_rates/cib' 12 | require 'egp_rates/aaib' 13 | require 'egp_rates/banque_misr' 14 | require 'egp_rates/banque_du_caire' 15 | require 'egp_rates/suez_canal_bank' 16 | require 'egp_rates/al_baraka_bank' 17 | require 'egp_rates/al_ahli_bank_of_kuwait' 18 | require 'egp_rates/midb' 19 | require 'egp_rates/ube' 20 | require 'egp_rates/cae' 21 | require 'egp_rates/edbe' 22 | require 'egp_rates/alex_bank' 23 | require 'egp_rates/blom' 24 | require 'egp_rates/adib' 25 | require 'egp_rates/egb' 26 | require 'egp_rates/nbg' 27 | 28 | # Base Module 29 | module EGPRates 30 | # Class Idicating HTTPResponseErrors when unexpected behaviour encountered 31 | # while scraping the [Bank] data 32 | class ResponseError < StandardError 33 | end 34 | 35 | # Threaded execution to get the exchange rates from different banks 36 | # @return [Hash] of the exchange rates of different banks 37 | # { 38 | # BANK: { 39 | # sell: { SYM: rate, SYM: rate }, 40 | # buy: { SYM: rate, SYM: rate } 41 | # }, 42 | # BANK: { 43 | # sell: { SYM: rate, SYM: rate }, 44 | # buy: { SYM: rate, SYM: rate } 45 | # }, 46 | # } 47 | def self.exchange_rates 48 | semaphore = Mutex.new 49 | # Fetch all the constants (Banks) implemented 50 | (constants - [:Bank, :ResponseError]).each_with_object({}) do |klass, rates| 51 | Thread.new do 52 | bank = EGPRates.const_get(klass).new 53 | 54 | semaphore.synchronize do 55 | begin 56 | rates[klass] = bank.exchange_rates 57 | rescue ResponseError 58 | rates[klass] = 'Failed to get exchange rates' 59 | end 60 | end 61 | end.join 62 | end 63 | end 64 | 65 | # Return the exchange rate of a single currency from different banks 66 | # @param [Symbol] sym, 3 charachter ISO symbol of the currency 67 | # @param [Boolean] cache_result, a boolean to indicate whether or not 68 | # the result of the exchange_rates call be cached 69 | # @return [Hash] of the exchange rates in different banks for a specific 70 | # currency 71 | def self.exchange_rate(sym = :USD, cache_result = true) 72 | @exchange_rates ||= exchange_rates if cache_result 73 | @exchange_rates = exchange_rates unless cache_result 74 | 75 | @exchange_rates.each_with_object({}) do |rates, result| 76 | begin 77 | result[rates[0]] = { 78 | sell: rates[1][:sell].fetch(sym.upcase, 'N/A'), 79 | buy: rates[1][:buy].fetch(sym.upcase, 'N/A') 80 | } 81 | rescue TypeError 82 | result[rates[0]] = rates[1] 83 | end 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /lib/egp_rates/nbe.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # National Bank of Egypt 4 | class NBE < EGPRates::Bank 5 | def initialize 6 | @sym = :NBE 7 | @uri = URI.parse('http://www.nbe.com.eg/en/ExchangeRate.aspx') 8 | end 9 | 10 | # @return [Hash] of exchange rates for selling and buying 11 | # { 12 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 13 | # { buy: { SYM: rate }, { SYM: rate }, ... } 14 | # } 15 | def exchange_rates 16 | @exchange_rates ||= parse(raw_exchange_rates) 17 | end 18 | 19 | private 20 | 21 | # Send the request to the URL and retrun raw data of the response 22 | # @return [Enumerator::Lazy] with the table row in HTML that evaluates to 23 | # [ 24 | # "\r\n\t\t", 25 | # "US DOLLAR", 26 | # "USD ", 27 | # "\r\n\t\t\t\t\t\t\t\t\t15.9\r\n\t\t\t\t\t\t\t\t", 28 | # "\r\n\t\t\t\t\t\t\t\t\t16.05\r\n\t\t\t\t\t\t\t\t", 29 | # "\r\n\t\t\t\t\t\t\t\t\t15.9\r\n\t\t\t\t\t\t\t\t", 30 | # "\r\n\t\t\t\t\t\t\t\t\t16.05\r\n\t\t\t\t\t\t\t\t", 31 | # "\r\n\t" 32 | # ], [ 33 | # "\r\n\t\t", 34 | # "EURO", 35 | # "EUR ", 36 | # "\r\n\t\t\t\t\t\t\t\t\t17.2213\r\n\t\t\t\t\t\t\t\t", 37 | # "\r\n\t\t\t\t\t\t\t\t\t17.5314\r\n\t\t\t\t\t\t\t\t", 38 | # "\r\n\t\t\t\t\t\t\t\t\t17.2213\r\n\t\t\t\t\t\t\t\t", 39 | # "\r\n\t\t\t\t\t\t\t\t\t17.5314\r\n\t\t\t\t\t\t\t\t", 40 | # "\r\n\t" 41 | # ], [ 42 | # ... 43 | # ] 44 | def raw_exchange_rates 45 | table_rows = Oga.parse_html(response.body).css('#idts_content tr') 46 | # NBE provide 17 currencies only 47 | fail ResponseError, 'Unknown HTML' unless table_rows&.size == 18 48 | # Drop 1 remove the table headers 49 | table_rows.lazy.drop(1).map(&:children).map { |cell| cell.map(&:text) } 50 | end 51 | 52 | # Parse the #raw_exchange_rates returned in response 53 | # @param [Array] of the raw_data scraped 54 | # @return [Hash] of exchange rates for selling and buying 55 | # { 56 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 57 | # { buy: { SYM: rate }, { SYM: rate }, ... } 58 | # } 59 | def parse(raw_data) 60 | raw_data.each_with_object(sell: {}, buy: {}) do |row, result| 61 | sell_rate = row[4].strip.to_f 62 | buy_rate = row[3].strip.to_f 63 | currency = row[2].strip.to_sym 64 | # Bahraini Dinar is BHD not BAD 65 | # Also Qatar Rial is QAR not QTR 66 | # Changing them for consistency 67 | if currency == :BAD 68 | currency = :BHD 69 | elsif currency == :QTR 70 | currency = :QAR 71 | end 72 | 73 | result[:sell][currency] = sell_rate 74 | result[:buy][currency] = buy_rate 75 | end 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /spec/egp_rates/nbe_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::NBE do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 17 8 | expect(bank.exchange_rates[:sell].size).to eq 17 9 | end 10 | 11 | describe '.new' do 12 | it 'initialize instance variables' do 13 | expect(bank.sym).to eq :NBE 14 | expect(bank.instance_variable_get(:@uri)).to be_a URI 15 | end 16 | end 17 | 18 | describe '#exchange_rates' do 19 | it 'calls #parse with #raw_exchange_rates' do 20 | expect(bank).to receive(:raw_exchange_rates) 21 | expect(bank).to receive(:parse) 22 | bank.exchange_rates 23 | end 24 | end 25 | 26 | describe '#raw_exchange_rates' do 27 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 28 | stub_request(:get, /.*nbe.*/).to_return(body: '', status: 500) 29 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 30 | EGPRates::ResponseError, '500' 31 | end 32 | 33 | it 'raises ResponseError if HTML structure changed', :no_vcr do 34 | stub_request(:get, /.*nbe.*/).to_return(body: '', status: 200) 35 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 36 | EGPRates::ResponseError, 'Unknown HTML' 37 | end 38 | 39 | it 'returns <#Enumerator::Lazy> of 17 rows', vcr: { cassette_name: :NBE } do 40 | lazy_enumerator = bank.send(:raw_exchange_rates) 41 | expect(lazy_enumerator).to be_a Enumerator::Lazy 42 | expect(lazy_enumerator.size).to eq 17 43 | end 44 | end 45 | 46 | describe '#parse', vcr: { cassette_name: :NBE } do 47 | let(:raw_data) { bank.send(:raw_exchange_rates) } 48 | 49 | it 'returns sell: hash of selling prices' do 50 | expect(bank.send(:parse, raw_data)[:sell]).to match( 51 | AED: 4.8327, 52 | AUD: 13.2557, 53 | BHD: 47.086, 54 | CAD: 13.3902, 55 | CHF: 17.6144, 56 | DKK: 2.5503, 57 | EUR: 18.973, 58 | GBP: 22.6082, 59 | JOD: 25.0565, 60 | JPY: 15.6595, 61 | KWD: 58.2254, 62 | NOK: 2.1132, 63 | OMR: 46.1506, 64 | QAR: 4.8748, 65 | SAR: 4.7327, 66 | SEK: 1.9331, 67 | USD: 17.75 68 | ) 69 | end 70 | 71 | it 'returns buy: hash of buying prices' do 72 | expect(bank.send(:parse, raw_data)[:buy]).to match( 73 | AED: 4.7645, 74 | AUD: 12.9535, 75 | BHD: 46.4166, 76 | CAD: 13.1381, 77 | CHF: 17.2891, 78 | DKK: 2.4995, 79 | EUR: 18.5955, 80 | GBP: 21.9993, 81 | JOD: 24.6827, 82 | JPY: 15.3227, 83 | KWD: 57.377, 84 | NOK: 2.0682, 85 | OMR: 45.4133, 86 | QAR: 4.8053, 87 | SAR: 4.6654, 88 | SEK: 1.8968, 89 | USD: 17.5 90 | ) 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /spec/egp_rates/cae_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::CAE do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 17 8 | expect(bank.exchange_rates[:sell].size).to eq 17 9 | end 10 | 11 | describe '.new' do 12 | it 'initialize instance variables' do 13 | expect(bank.sym).to eq :CAE 14 | expect(bank.instance_variable_get(:@uri)).to be_a URI 15 | end 16 | end 17 | 18 | describe '#exchange_rates' do 19 | it 'calls #parse with #raw_exchange_rates' do 20 | expect(bank).to receive(:raw_exchange_rates) 21 | expect(bank).to receive(:parse) 22 | bank.exchange_rates 23 | end 24 | end 25 | 26 | describe '#raw_exchange_rates' do 27 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 28 | stub_request(:get, /.*ca-egypt.*/).to_return(body: '', status: 500) 29 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 30 | EGPRates::ResponseError, '500' 31 | end 32 | 33 | it 'raises ResponseError if HTML structure changed', :no_vcr do 34 | stub_request(:get, /.*ca-egypt.*/).to_return(body: '', status: 200) 35 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 36 | EGPRates::ResponseError, 'Unknown HTML' 37 | end 38 | 39 | it 'returns <#Enumerator::Lazy> of 17 rows', vcr: { cassette_name: :CAE } do 40 | lazy_enumerator = bank.send(:raw_exchange_rates) 41 | expect(lazy_enumerator).to be_a Enumerator::Lazy 42 | expect(lazy_enumerator.size).to eq 17 43 | end 44 | end 45 | 46 | describe '#parse', vcr: { cassette_name: :CAE } do 47 | let(:raw_data) { bank.send(:raw_exchange_rates) } 48 | 49 | it 'returns sell: hash of selling prices' do 50 | expect(bank.send(:parse, raw_data)[:sell]).to match( 51 | AED: 4.969, 52 | AUD: 13.5171, 53 | BHD: 48.0106, 54 | CAD: 13.6542, 55 | CHF: 17.9617, 56 | DKK: 2.6006, 57 | EUR: 19.3471, 58 | GBP: 23.054, 59 | JOD: 25.493, 60 | JPY: 0.1597, 61 | KWD: 59.3735, 62 | NOK: 2.1549, 63 | OMR: 47.0118, 64 | QAR: 4.9703, 65 | SAR: 4.8264, 66 | SEK: 1.9712, 67 | USD: 18.1 68 | ) 69 | end 70 | 71 | it 'returns buy: hash of buying prices' do 72 | expect(bank.send(:parse, raw_data)[:buy]).to match( 73 | AED: 4.8326, 74 | AUD: 13.0645, 75 | BHD: 46.7922, 76 | CAD: 13.2508, 77 | CHF: 17.4373, 78 | DKK: 2.5209, 79 | EUR: 18.7549, 80 | GBP: 22.1878, 81 | JOD: 24.8592, 82 | JPY: 0.1545, 83 | KWD: 57.8689, 84 | NOK: 2.0842, 85 | OMR: 45.8382, 86 | QAR: 4.8465, 87 | SAR: 4.7057, 88 | SEK: 1.9131, 89 | USD: 17.65 90 | ) 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /spec/egp_rates/egb_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::EGB do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 17 8 | expect(bank.exchange_rates[:sell].size).to eq 17 9 | end 10 | 11 | describe '.new' do 12 | it 'initialize instance variables' do 13 | expect(bank.sym).to eq :EGB 14 | expect(bank.instance_variable_get(:@uri)).to be_a URI 15 | end 16 | end 17 | 18 | describe '#exchange_rates' do 19 | it 'calls #parse with #raw_exchange_rates' do 20 | expect(bank).to receive(:raw_exchange_rates) 21 | expect(bank).to receive(:parse) 22 | bank.exchange_rates 23 | end 24 | end 25 | 26 | describe '#raw_exchange_rates' do 27 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 28 | stub_request(:get, /.*eg-bank.*/).to_return(body: '', status: 500) 29 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 30 | EGPRates::ResponseError, '500' 31 | end 32 | 33 | it 'raises ResponseError if HTML structure changed', :no_vcr do 34 | stub_request(:get, /.*eg-bank.*/).to_return(body: '', status: 200) 35 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 36 | EGPRates::ResponseError, 'Unknown HTML' 37 | end 38 | 39 | it 'returns <#Enumerator::Lazy> of 17 rows', vcr: { cassette_name: :EGB } do 40 | lazy_enumerator = bank.send(:raw_exchange_rates) 41 | expect(lazy_enumerator).to be_a Enumerator::Lazy 42 | expect(lazy_enumerator.size).to eq 17 43 | end 44 | end 45 | 46 | describe '#parse', vcr: { cassette_name: :EGB } do 47 | let(:raw_data) { bank.send(:raw_exchange_rates) } 48 | 49 | it 'returns sell: hash of selling prices' do 50 | expect(bank.send(:parse, raw_data)[:sell]).to match( 51 | AED: 4.9421, 52 | AUD: 13.5544, 53 | BHD: 48.156, 54 | CAD: 13.6971, 55 | CHF: 18.0238, 56 | DKK: 2.6084, 57 | EUR: 19.4005, 58 | GBP: 23.1177, 59 | JOD: 25.6356, 60 | JPY: 0.1602, 61 | KWD: 59.5472, 62 | NOK: 2.1634, 63 | OMR: 47.1551, 64 | QAR: 4.9853, 65 | SAR: 4.8394, 66 | SEK: 1.9775, 67 | USD: 18.15 68 | ) 69 | end 70 | 71 | it 'returns buy: hash of buying prices' do 72 | expect(bank.send(:parse, raw_data)[:buy]).to match( 73 | AED: 4.7917, 74 | AUD: 13.0152, 75 | BHD: 46.6844, 76 | CAD: 13.2132, 77 | CHF: 17.3879, 78 | DKK: 2.5137, 79 | EUR: 18.6965, 80 | GBP: 22.1144, 81 | JOD: 24.7887, 82 | JPY: 0.1541, 83 | KWD: 57.7049, 84 | NOK: 2.08, 85 | OMR: 45.7143, 86 | QAR: 4.8329, 87 | SAR: 4.6926, 88 | SEK: 1.9077, 89 | USD: 17.6 90 | ) 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /spec/egp_rates/al_baraka_bank_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::AlBarakaBank do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 7 8 | expect(bank.exchange_rates[:sell].size).to eq 7 9 | end 10 | 11 | describe '.new' do 12 | it 'initialize instance variables' do 13 | expect(bank.sym).to eq :AlBarakaBank 14 | expect(bank.instance_variable_get(:@uri)).to be_a URI 15 | end 16 | end 17 | 18 | describe '#exchange_rates' do 19 | it 'calls #parse with #raw_exchange_rates' do 20 | expect(bank).to receive(:raw_exchange_rates) 21 | expect(bank).to receive(:parse) 22 | bank.exchange_rates 23 | end 24 | end 25 | 26 | describe '#raw_exchange_rates' do 27 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 28 | stub_request(:get, /.*albaraka.*/).to_return(body: '', status: 500) 29 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 30 | EGPRates::ResponseError, '500' 31 | end 32 | 33 | it 'raises ResponseError if HTML structure changed', :no_vcr do 34 | stub_request(:get, /.*albaraka.*/).to_return(body: '', status: 200) 35 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 36 | EGPRates::ResponseError, 'Unknown HTML' 37 | end 38 | 39 | it 'returns <#Enumerator::Lazy> of 7 rows', 40 | vcr: { cassette_name: :AlBarakaBank } do 41 | lazy_enumerator = bank.send(:raw_exchange_rates) 42 | expect(lazy_enumerator).to be_a Enumerator::Lazy 43 | expect(lazy_enumerator.size).to eq 7 44 | end 45 | end 46 | 47 | describe '#currency_symbol' do 48 | %w(USD EURO GBP CHF JPY SAR BHD)\ 49 | .each do |currency| 50 | it "returns currency :SYM for #{currency}" do 51 | symbols = %i(USD EUR GBP CHF JPY SAR BHD) 52 | expect(symbols).to include(bank.send(:currency_symbol, currency)) 53 | end 54 | end 55 | 56 | it 'raises ResponseError when Unknown Currency' do 57 | expect { bank.send(:currency_symbol, 'Egyptian pound') }.to raise_error\ 58 | EGPRates::ResponseError, 'Unknown currency Egyptian pound' 59 | end 60 | end 61 | 62 | describe '#parse', vcr: { cassette_name: :AlBarakaBank } do 63 | let(:raw_data) { bank.send(:raw_exchange_rates) } 64 | 65 | it 'returns sell: hash of selling prices' do 66 | expect(bank.send(:parse, raw_data)[:sell]).to match( 67 | BHD: 48.3615, 68 | CHF: 18.4535, 69 | EUR: 19.4932, 70 | GBP: 23.2589, 71 | JPY: 16.0489, 72 | SAR: 4.8618, 73 | USD: 18.09 74 | ) 75 | end 76 | 77 | it 'returns buy: hash of buying prices' do 78 | expect(bank.send(:parse, raw_data)[:buy]).to match( 79 | BHD: 46.3908, 80 | CHF: 17.2842, 81 | EUR: 18.6618, 82 | GBP: 22.2669, 83 | JPY: 15.3949, 84 | SAR: 4.588, 85 | USD: 17.74 86 | ) 87 | end 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /spec/egp_rates/cbe_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::CBE do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 9 8 | expect(bank.exchange_rates[:sell].size).to eq 9 9 | end 10 | 11 | describe '.new' do 12 | it 'initialize instance variables' do 13 | expect(bank.sym).to eq :CBE 14 | expect(bank.instance_variable_get(:@uri)).to be_a URI 15 | end 16 | end 17 | 18 | describe '#exchange_rates' do 19 | it 'calls #parse with #raw_exchange_rates' do 20 | expect(bank).to receive(:raw_exchange_rates) 21 | expect(bank).to receive(:parse) 22 | bank.exchange_rates 23 | end 24 | end 25 | 26 | describe '#raw_exchange_rates' do 27 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 28 | stub_request(:get, /.*cbe.*/).to_return(body: '', status: 500) 29 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 30 | EGPRates::ResponseError, '500' 31 | end 32 | 33 | it 'raises ResponseError if HTML structure changed', :no_vcr do 34 | stub_request(:get, /.*cbe.*/).to_return(body: '', status: 200) 35 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 36 | EGPRates::ResponseError, 'Unknown HTML' 37 | end 38 | 39 | it 'returns <#Enumerator::Lazy> of 9 rows', vcr: { cassette_name: :CBE } do 40 | lazy_enumerator = bank.send(:raw_exchange_rates) 41 | expect(lazy_enumerator).to be_a Enumerator::Lazy 42 | expect(lazy_enumerator.size).to eq 9 43 | end 44 | end 45 | 46 | describe '#currency_symbol' do 47 | %W(#{'US Dollar'} Euro Sterling Swiss Japanese Saudi Kuwait UAE Chinese)\ 48 | .each do |currency| 49 | it "returns currency :SYM for #{currency}" do 50 | symbols = %i(USD EUR GBP CHF JPY SAR KWD AED CNY) 51 | expect(symbols).to include(bank.send(:currency_symbol, currency)) 52 | end 53 | end 54 | 55 | it 'raises ResponseError when Unknown Currency' do 56 | expect { bank.send(:currency_symbol, 'Egyptian pound') }.to raise_error\ 57 | EGPRates::ResponseError, 'Unknown currency Egyptian pound' 58 | end 59 | end 60 | 61 | describe '#parse', vcr: { cassette_name: :CBE } do 62 | let(:raw_data) { bank.send(:raw_exchange_rates) } 63 | 64 | it 'returns sell: hash of selling prices' do 65 | expect(bank.send(:parse, raw_data)[:sell]).to match( 66 | AED: 4.9155, 67 | CHF: 17.8556, 68 | CNY: 2.6227, 69 | EUR: 19.2687, 70 | GBP: 22.9982, 71 | JPY: 15.8992, 72 | KWD: 59.2257, 73 | SAR: 4.8132, 74 | USD: 18.052 75 | ) 76 | end 77 | 78 | it 'returns buy: hash of buying prices' do 79 | expect(bank.send(:parse, raw_data)[:buy]).to match( 80 | AED: 4.7994, 81 | CHF: 17.4193, 82 | CNY: 2.5585, 83 | EUR: 18.8076, 84 | GBP: 22.4408, 85 | JPY: 15.5151, 86 | KWD: 57.7977, 87 | SAR: 4.7001, 88 | USD: 17.6283 89 | ) 90 | end 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /spec/egp_rates_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates do 3 | describe '.exchange_rates' do 4 | # rubocop:disable RSpec/VerifiedDoubles 5 | context 'all Banks' do 6 | let(:bank) { double(EGPRates::Bank) } 7 | 8 | it 'calls #exchange_rates on all Bank Classes' do 9 | expect(described_class).to receive(:const_get).exactly(18).times 10 | .and_return bank 11 | expect(bank).to receive(:new).exactly(18).times.and_return bank 12 | expect(bank).to receive(:exchange_rates).exactly(18).times 13 | 14 | described_class.exchange_rates 15 | end 16 | end 17 | # rubocop:enable RSpec/VerifiedDoubles 18 | 19 | context '1 Bank' do 20 | before { allow(described_class).to receive(:constants).and_return [:CBE] } 21 | 22 | it 'returns [Hash] of exchange_rates', vcr: { cassette_name: :CBE } do 23 | exchange_rates = described_class.exchange_rates 24 | 25 | expect(exchange_rates).to include(:CBE) 26 | expect(exchange_rates[:CBE]).to include(:buy, :sell) 27 | expect(exchange_rates[:CBE][:buy].size).to eq 9 28 | expect(exchange_rates[:CBE][:sell].size).to eq 9 29 | end 30 | 31 | it 'returns [Hash] of Bank: "Failed to get exchange rates"', :no_vcr do 32 | stub_request(:get, /.*/).to_return(body: '', status: 500) 33 | 34 | exchange_rates = described_class.exchange_rates 35 | expect(exchange_rates).to match(CBE: 'Failed to get exchange rates') 36 | end 37 | end 38 | end 39 | 40 | describe '.exchange_rate(:SYM, cache_result)' do 41 | before do 42 | described_class.instance_variable_set(:@exchange_rates, nil) 43 | allow(described_class).to receive(:constants).and_return [:NBE] 44 | end 45 | 46 | context 'cache_result = true' do 47 | it 'calls .exchange_rates once' do 48 | expect(described_class).to receive(:exchange_rates).once.and_return({}) 49 | 50 | described_class.exchange_rate(:USD) 51 | described_class.exchange_rate(:USD) 52 | end 53 | 54 | it 'calls .exchange_rates twice' do 55 | expect(described_class).to receive(:exchange_rates).twice.and_return({}) 56 | 57 | described_class.exchange_rate(:USD) 58 | described_class.exchange_rate(:USD, false) 59 | end 60 | 61 | it 'returns [Hash] of Bank: "Failed to get exchange rates"', :no_vcr do 62 | stub_request(:get, /.*/).to_return(body: '', status: 500) 63 | 64 | exchange_rate = described_class.exchange_rate(:USD) 65 | expect(exchange_rate).to match(NBE: 'Failed to get exchange rates') 66 | end 67 | 68 | it 'returns [Hash] of Bank: { sell: rate, buy: rate }', 69 | vcr: { cassette_name: :NBE } do 70 | exchange_rate = described_class.exchange_rate(:USD) 71 | expect(exchange_rate).to match(NBE: { sell: 17.75, buy: 17.5 }) 72 | end 73 | 74 | it 'returns [Hash] of Bank: { sell: "N/A" ... }', 75 | vcr: { cassette_name: :NBE } do 76 | exchange_rate = described_class.exchange_rate(:SYM) 77 | expect(exchange_rate).to match( 78 | NBE: { sell: 'N/A', buy: 'N/A' } 79 | ) 80 | end 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /spec/egp_rates/cib_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::CIB do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 6 8 | expect(bank.exchange_rates[:sell].size).to eq 6 9 | end 10 | 11 | describe '.new' do 12 | it 'initialize instance variables' do 13 | expect(bank.sym).to eq :CIB 14 | expect(bank.instance_variable_get(:@uri)).to be_a URI 15 | end 16 | end 17 | 18 | describe '#exchange_rates' do 19 | it 'calls #parse with #raw_exchange_rates' do 20 | expect(bank).to receive(:raw_exchange_rates) 21 | expect(bank).to receive(:parse) 22 | bank.exchange_rates 23 | end 24 | end 25 | 26 | describe '#raw_exchange_rates' do 27 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 28 | stub_request(:post, /.*cib.*/).to_return(body: '', status: 500) 29 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 30 | EGPRates::ResponseError, '500' 31 | end 32 | 33 | it 'raises ResponseError if JSON respnonse changed', :no_vcr do 34 | stub_request(:post, /.*cib.*/).to_return( 35 | body: { d: { USD: 1 } }.to_json, 36 | status: 200 37 | ) 38 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 39 | EGPRates::ResponseError, /Unknown JSON/ 40 | end 41 | 42 | it 'raises ResponseError if empty JSON respnonse', :no_vcr do 43 | stub_request(:post, /.*cib.*/).to_return(body: '"{}"', status: 200) 44 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 45 | EGPRates::ResponseError, /Unknown JSON/ 46 | end 47 | 48 | it 'raises ResponseError if malformed JSON respnonse', :no_vcr do 49 | stub_request(:post, /.*cib.*/).to_return(body: '{ data', status: 200) 50 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 51 | EGPRates::ResponseError, /Unknown JSON/ 52 | end 53 | 54 | it 'raises ResponseError if empty respnonse', :no_vcr do 55 | stub_request(:post, /.*cib.*/).to_return(body: '""', status: 200) 56 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 57 | EGPRates::ResponseError, /Unknown JSON/ 58 | end 59 | 60 | it 'returns Hash of 6 currencies rates', vcr: { cassette_name: :CIB } do 61 | response = bank.send(:raw_exchange_rates) 62 | expect(response.size).to eq 1 63 | expect(response['d'].size).to eq 6 64 | end 65 | end 66 | 67 | describe '#parse', vcr: { cassette_name: :CIB } do 68 | let(:raw_data) { bank.send(:raw_exchange_rates) } 69 | 70 | it 'returns sell: hash of selling prices' do 71 | expect(bank.send(:parse, raw_data)[:sell]).to match( 72 | CHF: 17.6266, 73 | EUR: 18.973, 74 | GBP: 22.6082, 75 | KWD: 58.2254, 76 | SAR: 4.7331, 77 | USD: 17.75 78 | ) 79 | end 80 | 81 | it 'returns buy: hash of buying prices' do 82 | expect(bank.send(:parse, raw_data)[:buy]).to match( 83 | CHF: 17.2891, 84 | EUR: 18.5903, 85 | GBP: 21.9888, 86 | KWD: 57.3667, 87 | SAR: 4.659, 88 | USD: 17.5 89 | ) 90 | end 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /lib/egp_rates/bank.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module EGPRates 3 | # Class Representing the bank to get the data from 4 | class Bank 5 | attr_reader :sym 6 | 7 | # Abstract method 8 | # Subclasses banks define the logic to get the exchange rates hash 9 | # it should return [Hash] sell: { SYM: rate, ... } , buy: { SYM: rate, ... } 10 | # for the available currencies (represented by :SYM) on the bank pages 11 | def exchange_rates 12 | raise NotImplementedError 13 | end 14 | 15 | private 16 | 17 | # Make a call to the @uri to get the raw_response 18 | # @return [Net::HTTPSuccess] of the raw response 19 | # @raises [ResponseError] if response is not 200 OK 20 | def response 21 | response = Net::HTTP.get_response(@uri) 22 | if response.is_a? Net::HTTPRedirection 23 | response = Net::HTTP.get_response(URI.parse(response['location'])) 24 | end 25 | fail ResponseError, response.code unless response.is_a? Net::HTTPSuccess 26 | response 27 | end 28 | 29 | # Convert currency string to ISO symbol 30 | # @param currency [String] "US Dollar" 31 | # @return [Symbol] :USD ISO currency name 32 | # rubocop:disable Metrics/CyclomaticComplexity 33 | # rubocop:disable Metrics/MethodLength 34 | def currency_symbol(currency) 35 | case currency 36 | when /UAE|EMIRATES|Dirham|AED/i then :AED 37 | when /Australian/i then :AUD 38 | when /Bahrain|BHD/i then :BHD 39 | when /Canadian|CANAD\. Dollar/i then :CAD 40 | when /Swiss|CHF/i then :CHF 41 | when /Chinese/ then :CNY 42 | when /Danish/i then :DKK 43 | when /Euro|EUR/i then :EUR 44 | when /British|Sterl.|GBP/i then :GBP 45 | when /Jordanian/i then :JOD 46 | when /Japanese|JPY|YEN/i then :JPY 47 | when /Kuwait/i then :KWD 48 | when /Norwegian|NORWEG\./i then :NOK 49 | when /Omani/i then :OMR 50 | when /Qatar/i then :QAR 51 | when /SAR|Saudi/i then :SAR 52 | when /Swidish|Swedish/i then :SEK 53 | when /U(\.)?S(\.)? Dollar|USD/i then :USD 54 | else fail ResponseError, "Unknown currency #{currency}" 55 | end 56 | end 57 | # rubocop:enable Metrics/CyclomaticComplexity 58 | # rubocop:enable Metrics/MethodLength 59 | 60 | # Parse the #raw_exchange_rates returned in response 61 | # @param [Array] of the raw_data scraped 62 | # [ 63 | # [ 'Currency_1', 'BuyRate', 'SellRate', ... ], 64 | # [ 'Currency_2', 'BuyRate', 'SellRate', ... ], 65 | # [ 'Currency_3', 'BuyRate', 'SellRate', ... ], 66 | # ... 67 | # ] 68 | # 69 | # @return [Hash] of exchange rates for selling and buying 70 | # { 71 | # { sell: { SYM: rate }, { SYM: rate }, ... }, 72 | # { buy: { SYM: rate }, { SYM: rate }, ... } 73 | # } 74 | def parse(raw_data) 75 | raw_data.each_with_object(sell: {}, buy: {}) do |row, result| 76 | sell_rate = row[2].to_f 77 | buy_rate = row[1].to_f 78 | currency = currency_symbol(row[0]) 79 | 80 | result[:sell][currency] = sell_rate 81 | result[:buy][currency] = buy_rate 82 | end 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [Github](https://github.com/mad-raz). All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /spec/egp_rates/suez_canal_bank_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::SuezCanalBank do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 13 8 | expect(bank.exchange_rates[:sell].size).to eq 13 9 | end 10 | 11 | describe '.new' do 12 | it 'initialize instance variables' do 13 | expect(bank.sym).to eq :SuezCanalBank 14 | expect(bank.instance_variable_get(:@uri)).to be_a URI 15 | end 16 | end 17 | 18 | describe '#exchange_rates' do 19 | it 'calls #parse with #raw_exchange_rates' do 20 | expect(bank).to receive(:raw_exchange_rates) 21 | expect(bank).to receive(:parse) 22 | bank.exchange_rates 23 | end 24 | end 25 | 26 | describe '#raw_exchange_rates' do 27 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 28 | stub_request(:get, /.*scbank.*/).to_return(body: '', status: 500) 29 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 30 | EGPRates::ResponseError, '500' 31 | end 32 | 33 | it 'raises ResponseError if HTML structure changed', :no_vcr do 34 | stub_request(:get, /.*scbank.*/).to_return(body: '', status: 200) 35 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 36 | EGPRates::ResponseError, 'Unknown HTML' 37 | end 38 | 39 | it 'returns <#Enumerator::Lazy> of 13 rows', 40 | vcr: { cassette_name: :SuezCanalBank } do 41 | lazy_enumerator = bank.send(:raw_exchange_rates) 42 | expect(lazy_enumerator).to be_a Enumerator::Lazy 43 | expect(lazy_enumerator.size).to eq 13 44 | end 45 | end 46 | 47 | describe '#currency_symbol' do 48 | %W(#{'UAE Dirham'} Australian Canadian Swedish Danish EUR YEN Swiss 49 | Sterling Kuwaiti Norwegian Saudi #{'US Dollar'}) 50 | .each do |currency| 51 | it "returns currency :SYM for #{currency}" do 52 | symbols = %i(USD AUD GBP CAD DKK AED EUR CHF SEK JPY NOK SAR KWD) 53 | expect(symbols).to include(bank.send(:currency_symbol, currency)) 54 | end 55 | end 56 | 57 | it 'raises ResponseError when Unknown Currency' do 58 | expect { bank.send(:currency_symbol, 'Egyptian Pound') }.to raise_error\ 59 | EGPRates::ResponseError, 'Unknown currency Egyptian Pound' 60 | end 61 | end 62 | 63 | describe '#parse', vcr: { cassette_name: :SuezCanalBank } do 64 | let(:raw_data) { bank.send(:raw_exchange_rates) } 65 | 66 | it 'returns sell: hash of selling prices' do 67 | expect(bank.send(:parse, raw_data)[:sell]).to match( 68 | AED: 0.0, 69 | AUD: 0.0, 70 | CAD: 0.0, 71 | CHF: 17.8749, 72 | DKK: 0.0, 73 | EUR: 19.2402, 74 | GBP: 22.9266, 75 | JPY: 0.0, 76 | KWD: 0.0, 77 | NOK: 0.0, 78 | SAR: 4.8003, 79 | SEK: 0.0, 80 | USD: 18.0 81 | ) 82 | end 83 | 84 | it 'returns buy: hash of buying prices' do 85 | expect(bank.send(:parse, raw_data)[:buy]).to match( 86 | AED: 0.0, 87 | AUD: 0.0, 88 | CAD: 0.0, 89 | CHF: 17.3879, 90 | DKK: 0.0, 91 | EUR: 18.6965, 92 | GBP: 22.1144, 93 | JPY: 0.0, 94 | KWD: 0.0, 95 | NOK: 0.0, 96 | SAR: 4.6714, 97 | SEK: 0.0, 98 | USD: 17.6 99 | ) 100 | end 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /spec/egp_rates/aaib_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::AAIB do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 7 8 | expect(bank.exchange_rates[:sell].size).to eq 7 9 | end 10 | 11 | describe '.new' do 12 | it 'initialize instance variables' do 13 | expect(bank.sym).to eq :AAIB 14 | expect(bank.instance_variable_get(:@uri)).to be_a URI 15 | end 16 | end 17 | 18 | describe '#response' do 19 | it 'Follows redirects' do 20 | stub_request(:get, /.*aaib.*/).to_return( 21 | status: 301, 22 | headers: { 23 | Location: 'http://aaib.com/services/rates' 24 | } 25 | ) 26 | expect(Net::HTTP).to receive(:get_response).twice.and_call_original 27 | expect { bank.send(:response) }.to raise_error \ 28 | EGPRates::ResponseError, '301' 29 | end 30 | end 31 | 32 | describe '#exchange_rates' do 33 | it 'calls #parse with #raw_exchange_rates' do 34 | expect(bank).to receive(:raw_exchange_rates) 35 | expect(bank).to receive(:parse) 36 | bank.exchange_rates 37 | end 38 | end 39 | 40 | describe '#raw_exchange_rates' do 41 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 42 | stub_request(:get, /.*aaib.*/).to_return(body: '', status: 500) 43 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 44 | EGPRates::ResponseError, '500' 45 | end 46 | 47 | it 'raises ResponseError if HTML structure changed', :no_vcr do 48 | stub_request(:get, /.*aaib.*/).to_return(body: '', status: 200) 49 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 50 | EGPRates::ResponseError, 'Unknown HTML' 51 | end 52 | 53 | it 'returns <#Enumerator::Lazy> of 7 rows', vcr: { cassette_name: :AAIB } do 54 | lazy_enumerator = bank.send(:raw_exchange_rates) 55 | expect(lazy_enumerator).to be_a Enumerator::Lazy 56 | expect(lazy_enumerator.size).to eq 7 57 | end 58 | end 59 | 60 | describe '#currency_symbol' do 61 | %W(#{'US DOLLAR'} EURO STERLING SWISS SAUDI KUWAITI DIRHAM)\ 62 | .each do |currency| 63 | it "returns currency :SYM for #{currency}" do 64 | symbols = %i(USD EUR GBP CHF SAR KWD AED) 65 | expect(symbols).to include(bank.send(:currency_symbol, currency)) 66 | end 67 | end 68 | 69 | it 'raises ResponseError when Unknown Currency' do 70 | expect { bank.send(:currency_symbol, 'EGYPTIAN POUND') }.to raise_error\ 71 | EGPRates::ResponseError, 'Unknown currency EGYPTIAN POUND' 72 | end 73 | end 74 | 75 | describe '#parse', vcr: { cassette_name: :AAIB } do 76 | let(:raw_data) { bank.send(:raw_exchange_rates) } 77 | 78 | it 'returns sell: hash of selling prices' do 79 | expect(bank.send(:parse, raw_data)[:sell]).to match( 80 | AED: 4.8788, 81 | CHF: 17.7831, 82 | EUR: 19.1547, 83 | GBP: 22.8247, 84 | KWD: 59.4954, 85 | SAR: 4.7783, 86 | USD: 17.92 87 | ) 88 | end 89 | 90 | it 'returns buy: hash of buying prices' do 91 | expect(bank.send(:parse, raw_data)[:buy]).to match( 92 | AED: 4.778, 93 | CHF: 17.4373, 94 | EUR: 18.7549, 95 | GBP: 22.1878, 96 | KWD: 57.9515, 97 | SAR: 4.6797, 98 | USD: 17.65 99 | ) 100 | end 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /spec/egp_rates/alex_bank_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::AlexBank do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 17 8 | expect(bank.exchange_rates[:sell].size).to eq 17 9 | end 10 | 11 | describe '.new' do 12 | it 'initialize instance variables' do 13 | expect(bank.sym).to eq :AlexBank 14 | expect(bank.instance_variable_get(:@uri)).to be_a URI 15 | end 16 | end 17 | 18 | describe '#exchange_rates' do 19 | it 'calls #parse with #raw_exchange_rates' do 20 | expect(bank).to receive(:raw_exchange_rates) 21 | expect(bank).to receive(:parse) 22 | bank.exchange_rates 23 | end 24 | end 25 | 26 | describe '#raw_exchange_rates' do 27 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 28 | stub_request(:get, /.*alexbank.*/).to_return(body: '', status: 500) 29 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 30 | EGPRates::ResponseError, '500' 31 | end 32 | 33 | it 'raises ResponseError if HTML structure changed', :no_vcr do 34 | stub_request(:get, /.*alexbank.*/).to_return(body: '', status: 200) 35 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 36 | EGPRates::ResponseError, 'Unknown HTML' 37 | end 38 | 39 | it 'returns <#Enumerator::Lazy> of 17 rows', 40 | vcr: { cassette_name: :AlexBank } do 41 | lazy_enumerator = bank.send(:raw_exchange_rates) 42 | expect(lazy_enumerator).to be_a Enumerator::Lazy 43 | expect(lazy_enumerator.size).to eq 17 44 | end 45 | end 46 | 47 | describe '#currency_symbol' do 48 | %W(#{'UAE Dirham'} Australian Bahraini Canadian Swidish Danish Euro 49 | Japanese Swiss British Jordanian Kuwaiti Norwegian Omani Qatari Saudi 50 | #{'US Dollar'}) 51 | .each do |currency| 52 | it "returns currency :SYM for #{currency}" do 53 | symbols = %i(USD AUD BHD GBP CAD DKK AED EUR CHF SEK JPY JOD NOK OMR QAR 54 | SAR KWD) 55 | expect(symbols).to include(bank.send(:currency_symbol, currency)) 56 | end 57 | end 58 | 59 | it 'raises ResponseError when Unknown Currency' do 60 | expect { bank.send(:currency_symbol, 'EGYPTIAN POUND') }.to raise_error\ 61 | EGPRates::ResponseError, 'Unknown currency EGYPTIAN POUND' 62 | end 63 | end 64 | 65 | describe '#parse', vcr: { cassette_name: :AlexBank } do 66 | let(:raw_data) { bank.send(:raw_exchange_rates) } 67 | 68 | it 'returns sell: hash of selling prices' do 69 | expect(bank.send(:parse, raw_data)[:sell]).to match( 70 | AED: 4.91437, 71 | AUD: 13.47974, 72 | BHD: 0.0, 73 | CAD: 13.61648, 74 | CHF: 17.91208, 75 | DKK: 2.59343, 76 | EUR: 19.29365, 77 | GBP: 22.97765, 78 | JOD: 0.0, 79 | JPY: 15.92413, 80 | KWD: 59.21916, 81 | NOK: 2.14896, 82 | OMR: 0.0, 83 | QAR: 0.0, 84 | SAR: 4.81269, 85 | SEK: 1.9658, 86 | USD: 18.05 87 | ) 88 | end 89 | 90 | it 'returns buy: hash of buying prices' do 91 | expect(bank.send(:parse, raw_data)[:buy]).to match( 92 | AED: 4.77717, 93 | AUD: 13.04232, 94 | BHD: 0.0, 95 | CAD: 13.22823, 96 | CHF: 17.40763, 97 | DKK: 2.5166, 98 | EUR: 18.72301, 99 | GBP: 22.44788, 100 | JOD: 0.0, 101 | JPY: 15.42772, 102 | KWD: 57.29892, 103 | NOK: 2.08237, 104 | OMR: 0.0, 105 | QAR: 0.0, 106 | SAR: 4.68342, 107 | SEK: 1.90982, 108 | USD: 17.62 109 | ) 110 | end 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /spec/egp_rates/banque_du_caire_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::BanqueDuCaire do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 17 8 | expect(bank.exchange_rates[:sell].size).to eq 17 9 | end 10 | 11 | describe '.new' do 12 | it 'initialize instance variables' do 13 | expect(bank.sym).to eq :BanqueDuCaire 14 | expect(bank.instance_variable_get(:@uri)).to be_a URI 15 | end 16 | end 17 | 18 | describe '#exchange_rates' do 19 | it 'calls #parse with #raw_exchange_rates' do 20 | expect(bank).to receive(:raw_exchange_rates) 21 | expect(bank).to receive(:parse) 22 | bank.exchange_rates 23 | end 24 | end 25 | 26 | describe '#raw_exchange_rates' do 27 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 28 | stub_request(:get, /.*banqueducaire.*/).to_return(body: '', status: 500) 29 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 30 | EGPRates::ResponseError, '500' 31 | end 32 | 33 | it 'raises ResponseError if HTML structure changed', :no_vcr do 34 | stub_request(:get, /.*banqueducaire.*/).to_return(body: '', status: 200) 35 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 36 | EGPRates::ResponseError, 'Unknown HTML' 37 | end 38 | 39 | it 'returns <#Enumerator::Lazy> of 17 rows', 40 | vcr: { cassette_name: :BanqueDuCaire } do 41 | lazy_enumerator = bank.send(:raw_exchange_rates) 42 | expect(lazy_enumerator).to be_a Enumerator::Lazy 43 | expect(lazy_enumerator.size).to eq 17 44 | end 45 | end 46 | 47 | describe '#currency_symbol' do 48 | %W(#{'US DOLLAR'} AUSTRALIAN BAHRAIN BRITISH CANADIAN DANISH EMIRATES EURO 49 | JAPANESE JORDANIAN NORWEGIAN OMANI QATAR SAUDI KUWAITI SWISS SWEDISH) 50 | .each do |currency| 51 | it "returns currency :SYM for #{currency}" do 52 | symbols = %i(USD AUD BHD GBP CAD DKK AED EUR CHF SEK JPY JOD NOK OMR QAR 53 | SAR KWD) 54 | expect(symbols).to include(bank.send(:currency_symbol, currency)) 55 | end 56 | end 57 | 58 | it 'raises ResponseError when Unknown Currency' do 59 | expect { bank.send(:currency_symbol, 'EGYPTIAN POUND') }.to raise_error\ 60 | EGPRates::ResponseError, 'Unknown currency EGYPTIAN POUND' 61 | end 62 | end 63 | 64 | describe '#parse', vcr: { cassette_name: :BanqueDuCaire } do 65 | let(:raw_data) { bank.send(:raw_exchange_rates) } 66 | 67 | it 'returns sell: hash of selling prices' do 68 | expect(bank.send(:parse, raw_data)[:sell]).to match( 69 | AED: 4.8332, 70 | AUD: 13.2557, 71 | BHD: 47.088, 72 | CAD: 13.3952, 73 | CHF: 17.6266, 74 | DKK: 2.5509, 75 | EUR: 18.973, 76 | GBP: 22.6082, 77 | JOD: 25.071, 78 | JPY: 15.6678, 79 | KWD: 58.244, 80 | NOK: 2.1157, 81 | OMR: 46.11, 82 | QAR: 4.876, 83 | SAR: 4.7352, 84 | SEK: 1.934, 85 | USD: 17.75 86 | ) 87 | end 88 | 89 | it 'returns buy: hash of buying prices' do 90 | expect(bank.send(:parse, raw_data)[:buy]).to match( 91 | AED: 4.7587, 92 | AUD: 12.9413, 93 | BHD: 44.872, 94 | CAD: 13.1381, 95 | CHF: 17.2891, 96 | DKK: 2.4995, 97 | EUR: 18.5903, 98 | GBP: 21.9888, 99 | JOD: 24.39, 100 | JPY: 15.3227, 101 | KWD: 55.476, 102 | NOK: 2.0682, 103 | OMR: 43.75, 104 | QAR: 4.781, 105 | SAR: 4.6174, 106 | SEK: 1.8968, 107 | USD: 17.5 108 | ) 109 | end 110 | end 111 | end 112 | -------------------------------------------------------------------------------- /spec/egp_rates/banque_misr_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | describe EGPRates::BanqueMisr do 3 | subject(:bank) { described_class.new } 4 | 5 | it 'Live Testing', :live do 6 | expect(bank.exchange_rates).to include(:buy, :sell) 7 | expect(bank.exchange_rates[:buy].size).to eq 17 8 | expect(bank.exchange_rates[:sell].size).to eq 17 9 | expect(bank.exchange_rates[:buy].keys).to include( 10 | :USD, :AUD, :BHD, :GBP, :CAD, :DKK, :AED, :EUR, :CHF, :SEK, 11 | :JPY, :JOD, :NOK, :OMR, :QAR, :SAR, :KWD 12 | ) 13 | expect(bank.exchange_rates[:sell].keys).to include( 14 | :USD, :AUD, :BHD, :GBP, :CAD, :DKK, :AED, :EUR, :CHF, :SEK, 15 | :JPY, :JOD, :NOK, :OMR, :QAR, :SAR, :KWD 16 | ) 17 | end 18 | 19 | describe '.new' do 20 | it 'initialize instance variables' do 21 | expect(bank.sym).to eq :BanqueMisr 22 | expect(bank.instance_variable_get(:@uri)).to be_a URI 23 | end 24 | end 25 | 26 | describe '#exchange_rates' do 27 | it 'calls #parse with #raw_exchange_rates' do 28 | expect(bank).to receive(:raw_exchange_rates) 29 | expect(bank).to receive(:parse) 30 | bank.exchange_rates 31 | end 32 | end 33 | 34 | describe '#raw_exchange_rates' do 35 | it 'raises ResponseError unless Net::HTTPSuccess', :no_vcr do 36 | stub_request(:get, /.*banquemisr.*/).to_return(body: '', status: 500) 37 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 38 | EGPRates::ResponseError, '500' 39 | end 40 | 41 | it 'raises ResponseError if HTML structure changed', :no_vcr do 42 | stub_request(:get, /.*banquemisr.*/).to_return(body: '', status: 200) 43 | expect { bank.send(:raw_exchange_rates) }.to raise_error\ 44 | EGPRates::ResponseError, 'Unknown HTML' 45 | end 46 | 47 | it 'returns <#Enumerator::Lazy> of 17 rows', 48 | vcr: { cassette_name: :BanqueMisr } do 49 | lazy_enumerator = bank.send(:raw_exchange_rates) 50 | expect(lazy_enumerator).to be_a Enumerator::Lazy 51 | expect(lazy_enumerator.size).to eq 17 52 | end 53 | end 54 | 55 | describe '#currency_symbol' do 56 | %W(#{'UAE DIRHAM'} AUSTRALIA BAHRAIN CANADA SWEDISH DENMARK EURO JAPAN SWISS 57 | #{'GB POUND'} JORDANIAN KUWAIT NORWAY OMAN QATARI SAUDI #{'US DOLLAR'}) 58 | .each do |currency| 59 | it "returns currency :SYM for #{currency}" do 60 | symbols = %i(USD AUD BHD GBP CAD DKK AED EUR CHF SEK JPY JOD NOK OMR QAR 61 | SAR KWD) 62 | expect(symbols).to include(bank.send(:currency_symbol, currency)) 63 | end 64 | end 65 | 66 | it 'raises ResponseError when Unknown Currency' do 67 | expect { bank.send(:currency_symbol, 'EGYPTIAN POUND') }.to raise_error\ 68 | EGPRates::ResponseError, 'Unknown currency EGYPTIAN POUND' 69 | end 70 | end 71 | 72 | describe '#parse', vcr: { cassette_name: :BanqueMisr } do 73 | let(:raw_data) { bank.send(:raw_exchange_rates) } 74 | 75 | it 'returns sell: hash of selling prices' do 76 | expect(bank.send(:parse, raw_data)[:sell]).to match( 77 | AED: 4.8303, 78 | AUD: 13.2491, 79 | BHD: 47.0624, 80 | CAD: 13.3835, 81 | CHF: 17.6056, 82 | DKK: 2.549, 83 | EUR: 18.9635, 84 | GBP: 22.5969, 85 | JOD: 25.0439, 86 | JPY: 15.6516, 87 | KWD: 58.1962, 88 | NOK: 2.1122, 89 | OMR: 46.1276, 90 | QAR: 4.8723, 91 | SAR: 4.7303, 92 | SEK: 1.9322, 93 | USD: 17.75 94 | ) 95 | end 96 | 97 | it 'returns buy: hash of buying prices' do 98 | expect(bank.send(:parse, raw_data)[:buy]).to match( 99 | AED: 4.7645, 100 | AUD: 12.9535, 101 | BHD: 46.4166, 102 | CAD: 13.1381, 103 | CHF: 17.2891, 104 | DKK: 2.4995, 105 | EUR: 18.5955, 106 | GBP: 21.9993, 107 | JOD: 24.6827, 108 | JPY: 15.3227, 109 | KWD: 57.377, 110 | NOK: 2.0682, 111 | OMR: 45.4133, 112 | QAR: 4.8053, 113 | SAR: 4.6654, 114 | SEK: 1.8968, 115 | USD: 17.5 116 | ) 117 | end 118 | end 119 | end 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EGPRates 2 | [![wercker status](https://app.wercker.com/status/d6ca4529f0d563e82898ace1f2b3de25/s/master "wercker status")](https://app.wercker.com/project/byKey/d6ca4529f0d563e82898ace1f2b3de25) 3 | [![Code Climate](https://codeclimate.com/github/mad-raz/EGP-Rates/badges/gpa.svg)](https://codeclimate.com/github/mad-raz/EGP-Rates) 4 | [![Test Coverage](https://codeclimate.com/github/mad-raz/EGP-Rates/badges/coverage.svg)](https://codeclimate.com/github/mad-raz/EGP-Rates/coverage) 5 | [![LICENSE](https://img.shields.io/badge/licence-MIT-blue.svg)](/LICENSE.md) 6 | 7 | [CLI available here](https://github.com/mad-raz/EGP-Rates-CLI) 8 | 9 | ## Installation 10 | 11 | Add this line to your application's Gemfile: 12 | ```ruby 13 | gem 'EGP_Rates' 14 | ``` 15 | And then execute: 16 | ```sh 17 | $ bundle 18 | ``` 19 | Or install it yourself as: 20 | ```sh 21 | $ gem install EGP_Rates 22 | ``` 23 | 24 | ## Usage 25 | - [Get all the available banks rates](/lib/egp_rates.rb) 26 | - [Central Bank of Egypt (CBE)](/lib/egp_rates/cbe.rb) 27 | - [National Bank of Egypt (NBE)](/lib/egp_rates/nbe.rb) 28 | - [Commercial International Bank (CIB)](/lib/egp_rates/cib.rb) 29 | - [Arab African International Bank (AAIB)](/lib/egp_rates/aaib.rb) 30 | - [Banque Du Caire](/lib/egp_rates/banque_du_caire.rb) 31 | - [Banque Misr](/lib/egp_rates/banque_misr.rb) 32 | - [Suez Canal Bank](/lib/egp_rates/suez_canal_bank.rb) 33 | - [Al Baraka Bank](/lib/egp_rates/al_baraka_bank.rb) 34 | - [Al Ahli Bank of Kuwait](/lib/egp_rates/al_ahli_bank_of_kuwait.rb) 35 | - [Misr Iran Development Bank (MIDB)](/lib/egp_rates/midb.rb) 36 | - [The United Bank of Egypt (UBE)](/lib/egp_rates/ube.rb) 37 | - [Crédit Agricole Egypt (CAE)](/lib/egp_rates/cae.rb) 38 | - [Export Development Bank of Egypt (EDBE)](/lib/egp_rates/edbe.rb) 39 | - [Bank of Alexandria (AlexBank)](/lib/egp_rates/alex_bank.rb) 40 | - [Blom Bank Egypt (Blom)](/lib/egp_rates/blom.rb) 41 | - [Abu Dhabi Islamic Bank (ADIB)](/lib/egp_rates/adib.rb) 42 | - [Egyptian Gulf Bank (EGB)](/lib/egp_rates/egb.rb) 43 | - [National Bank of Greece (NBG)](/lib/egp_rates/nbg.rb) 44 | 45 | ```rb 46 | require 'egp_rates' 47 | # All Available Banks Data (Threaded execution) 48 | # For all the currencies that the currently showing on their pages 49 | EGPRates.exchange_rates 50 | 51 | # All Available Banks Data about specific currency 52 | # (by default it caches the response for later use) 53 | EGPRates.exchange_rate :USD # call and cache response 54 | EGPRates.exchange_rate :eur # from cached response 55 | EGPRates.exchange_rate :EUR, false # refresh cache 56 | 57 | # Specific Bank Data 58 | EGPRates::CBE.new.exchange_rates 59 | EGPRates::NBE.new.exchange_rates 60 | EGPRates::CIB.new.exchange_rates 61 | EGPRates::AAIB.new.exchange_rates 62 | EGPRates::BanqueDuCaire.new.exchange_rates 63 | EGPRates::BanqueMisr.new.exchange_rates 64 | EGPRates::SuezCanalBank.new.exchange_rates 65 | EGPRates::AlBarakaBank.new.exchange_rates 66 | EGPRates::AlAhliBankOfKuwait.new.exchange_rates 67 | EGPRates::MIDB.new.exchange_rates 68 | EGPRates::UBE.new.exchange_rates 69 | EGPRates::CAE.new.exchange_rates 70 | EGPRates::EDBE.new.exchange_rates 71 | EGPRates::AlexBank.new.exchange_rates 72 | EGPRates::Blom.new.exchange_rates 73 | EGPRates::ADIB.new.exchange_rates 74 | EGPRates::EGB.new.exchange_rates 75 | EGPRates::NBG.new.exchange_rates 76 | ``` 77 | 78 | ## Development 79 | - clone the repo 80 | - to install dependencies run `bundle install` 81 | - to run the test suite `bundle exec rake spec` (local testing and live) 82 | - to run the live test suite `bundle exec rake spec_live` 83 | (or `bundle exec rspec -t live`) 84 | - to run the local test suite `bundle exec rake spec_local` 85 | (or `bundle exec rspec -t ~live`) 86 | - to run rubocop linter `bundle exec rake rubocop` 87 | 88 | ## Contributing 89 | 90 | Bug reports and pull requests are welcome on GitHub at 91 | https://github.com/mad-raz/EGP-Rates 92 | 93 | This project is intended to be a safe, 94 | welcoming space for collaboration, 95 | and contributors are expected to adhere to the 96 | Contributor Covenant [code of conduct.](/CODE_OF_CONDUCT.md) 97 | 98 | - Read the previous [code of conduct](/CODE_OF_CONDUCT.md) once again. 99 | - Write clean code. 100 | - Write clean tests. 101 | - Make sure your code is covered by test. 102 | - Make sure you follow the code style mentioned by 103 | [rubocop](http://batsov.com/rubocop/) (run `bundle exec rake rubocop`) 104 | - A pre-commit hook included with repo can be used to remember rubocop 105 | it won't disable commits, but will remind you of violations. 106 | you can set it up using `chmod +x pre-commit && cp pre-commit .git/hooks/` 107 | - Be nice to your fellow human-beings/bots contributing to this repo. 108 | 109 | ## License 110 | 111 | The project is available as open source under the terms of the 112 | [MIT License](/LICENSE.md) 113 | --------------------------------------------------------------------------------