frankfurter/lib/provider/adapters/ecb.rb
BadMark d0a43e357a
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / publish (push) Has been cancelled
CI/CD Pipeline / release (push) Has been cancelled
up
2026-05-21 01:38:14 -06:00

89 lines
2.3 KiB
Ruby

# frozen_string_literal: true
require "csv"
require "net/http"
require "provider/adapters/adapter"
class Provider
module Adapters
class ECB < Adapter
SDMX_URL = "https://data-api.ecb.europa.eu/service/data/EXR/D..EUR.SP00.A"
class << self
def backfill_range = 365
end
def fetch(after: nil, upto: nil)
url = URI(SDMX_URL)
params = { format: "csvdata" }
params[:startPeriod] = after.to_s if after
params[:endPeriod] = upto.to_s if upto
url.query = URI.encode_www_form(params)
dataset = []
stream_csv(url) do |row|
record = parse_row(row)
dataset << record if record
end
dataset
end
def parse(csv)
CSV.parse(csv, headers: true, liberal_parsing: true).filter_map do |row|
parse_row(row)
end
end
private
def parse_row(row)
return unless row["FREQ"] == "D"
quote = row["CURRENCY"]
return unless quote&.match?(/\A[A-Z]{3}\z/)
value = row["OBS_VALUE"]
return unless value
rate = Float(value)
date = Date.parse(row["TIME_PERIOD"])
{ date:, base: "EUR", quote:, rate: }
end
def stream_csv(url)
uri = URI(url)
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
request = Net::HTTP::Get.new(uri)
http.request(request) do |response|
headers = nil
buffer = +""
response.read_body do |chunk|
buffer << chunk
while (line_end = buffer.index("\n"))
line = buffer.slice!(0..line_end)
if headers.nil?
headers = CSV.parse_line(line, liberal_parsing: true)
else
values = CSV.parse_line(line, liberal_parsing: true)
next unless values
row = CSV::Row.new(headers, values)
yield row
end
end
end
# Process remaining buffer
if headers && !buffer.empty?
values = CSV.parse_line(buffer, liberal_parsing: true)
yield CSV::Row.new(headers, values) if values
end
end
end
end
end
end
end