frankfurter/lib/provider/adapters/boja.rb

113 lines
3.1 KiB
Ruby
Raw Normal View History

2026-05-21 01:38:14 -06:00
# frozen_string_literal: true
require "json"
require "net/http"
require "provider/adapters/adapter"
class Provider
module Adapters
# Bank of Jamaica daily counter rates in JMD.
# Fetches from a WPDataTables endpoint (table_id=134), which requires a nonce extracted from the page HTML.
class BOJA < Adapter
PAGE_URL = "https://boj.org.jm/market/foreign-exchange/counter-rates/"
BASE_URL = "https://boj.org.jm/wp-admin/admin-ajax.php"
TABLE_ID = 134
NONCE_PATTERN = /wdtNonceFrontendServerSide_#{TABLE_ID}"\s+value="([^"]+)"/
CURRENCY_MAP = {
"U.S. DOLLAR" => "USD",
"GREAT BRITAIN POUND" => "GBP",
"CANADA DOLLAR" => "CAD",
"EURO" => "EUR",
"JAPANESE YEN" => "JPY",
"SWISS FRANC" => "CHF",
"AUSTRALIAN DOLLAR" => "AUD",
"DANISH KRONE" => "DKK",
"NORWEGIAN KRONE" => "NOK",
"SWEDISH KRONA" => "SEK",
"HONG KONG DOLLAR" => "HKD",
"BARBADOS DOLLAR" => "BBD",
"BELIZE DOLLAR" => "BZD",
"T&T DOLLAR" => "TTD",
"BAHAMAS DOLLAR" => "BSD",
"CAYMAN DOLLAR" => "KYD",
"GUYANA DOLLAR" => "GYD",
"E. C. DOLLAR" => "XCD",
"DOMINICAN REP. PESO" => "DOP",
"GIBRALTAR POUND" => "GIP",
"NORTHERN IRELAND POUND" => "GBP",
}.freeze
def fetch(after: nil, upto: nil)
dataset = fetch_table
dataset = dataset.select { |r| r[:date] >= after } if after
dataset = dataset.select { |r| r[:date] <= upto } if upto
dataset
end
def parse(json)
data = json.is_a?(String) ? JSON.parse(json) : json
rows = data["data"]
return [] unless rows.is_a?(Array)
rows.filter_map do |row|
parse_row(row)
end
end
private
def fetch_table
nonce = fetch_nonce
return [] unless nonce
uri = URI("#{BASE_URL}?action=get_wdtable&table_id=#{TABLE_ID}")
request = Net::HTTP::Post.new(uri)
request.set_form_data("draw" => "1", "start" => "0", "length" => "-1", "wdtNonce" => nonce)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
parse(response.body)
end
def fetch_nonce
uri = URI(PAGE_URL)
response = Net::HTTP.get(uri)
match = response.match(NONCE_PATTERN)
match&.captures&.first
end
def parse_row(row)
date_str, currency_name, sell_rate, buy_rate, = row
return unless date_str && currency_name
date = Date.strptime(date_str.strip, "%d %b %Y")
iso = resolve_currency(currency_name.strip)
return unless iso
rate_value = compute_mid(sell_rate, buy_rate)
return unless rate_value && !rate_value.zero?
{ date:, base: iso, quote: "JMD", rate: rate_value }
end
def compute_mid(sell, buy)
s = Float(sell)
b = Float(buy)
return s if b.zero?
(s + b) / 2.0
end
def resolve_currency(name)
CURRENCY_MAP[name.upcase]
end
end
end
end