M Gemfile => Gemfile +2 -0
@@ 6,9 6,11 @@ gem "blather", git: "https://github.com/singpolyma/blather.git", branch: "ergono
gem "braintree"
gem "dhall"
gem "em-hiredis"
+gem "em-http-request"
gem "em-pg-client", git: "https://github.com/royaltm/ruby-em-pg-client"
gem "em_promise.rb"
gem "eventmachine"
+gem "money-open-exchange-rates"
group(:development) do
gem "pry-reload"
A lib/btc_sell_prices.rb => lib/btc_sell_prices.rb +57 -0
@@ 0,0 1,57 @@
+# frozen_string_literal: true
+
+require "em-http"
+require "money/bank/open_exchange_rates_bank"
+require "nokogiri"
+
+require_relative "em"
+
+class BTCSellPrices
+ def initialize(redis, oxr_app_id)
+ @redis = redis
+ @oxr = Money::Bank::OpenExchangeRatesBank.new(
+ Money::RatesStore::Memory.new
+ )
+ @oxr.app_id = oxr_app_id
+ end
+
+ def cad
+ fetch_canadianbitcoins.then do |http|
+ canadianbitcoins = Nokogiri::HTML.parse(http.response)
+
+ bitcoin_row = canadianbitcoins.at("#ticker > table > tbody > tr")
+ raise "Bitcoin row has moved" unless bitcoin_row.at("td").text == "Bitcoin"
+
+ BigDecimal.new(
+ bitcoin_row.at("td:nth-of-type(3)").text.match(/^\$(\d+\.\d+)/)[1]
+ )
+ end
+ end
+
+ def usd
+ EMPromise.all([cad, cad_to_usd]).then { |(a, b)| a * b }
+ end
+
+protected
+
+ def fetch_canadianbitcoins
+ EM::HttpRequest.new(
+ "https://www.canadianbitcoins.com",
+ tls: { verify_peer: true }
+ ).get
+ end
+
+ def cad_to_usd
+ @redis.get("cad_to_usd").then do |rate|
+ next rate.to_f if rate
+
+ EM.promise_defer {
+ # OXR gem is not async, so defer to threadpool
+ oxr.update_rates
+ oxr.get_rate("CAD", "USD")
+ }.then do |orate|
+ @redis.set("cad_to_usd", orate, ex: 60 * 60).then { orate }
+ end
+ end
+ end
+end
A test/test_btc_sell_prices.rb => test/test_btc_sell_prices.rb +31 -0
@@ 0,0 1,31 @@
+# frozen_string_literal: true
+
+require "em-hiredis"
+require "test_helper"
+require "btc_sell_prices"
+
+class BTCSellPricesTest < Minitest::Test
+ def setup
+ @redis = Minitest::Mock.new
+ @subject = BTCSellPrices.new(@redis, "")
+ end
+
+ def test_cad
+ stub_request(:get, "https://www.canadianbitcoins.com").to_return(
+ body: "<div id='ticker'><table><tbody><tr>" \
+ "<td>Bitcoin</td><td></td><td>$123.00</td>"
+ )
+ assert_equal BigDecimal.new(123), @subject.cad.sync
+ end
+ em :test_cad
+
+ def test_usd
+ stub_request(:get, "https://www.canadianbitcoins.com").to_return(
+ body: "<div id='ticker'><table><tbody><tr>" \
+ "<td>Bitcoin<td></td><td>$123.00</td>"
+ )
+ @redis.expect(:get, EMPromise.resolve("0.5"), ["cad_to_usd"])
+ assert_equal BigDecimal.new(123) / 2, @subject.usd.sync
+ end
+ em :test_usd
+end