Лучшее место для проверки заголовков CSV-файла в kiba ETL

Мне нужно проверить, что:

  • строка заголовка присутствует
  • заголовок содержит специфический набор заголовков

Какое лучшее место для этого. У меня есть какое-то возможное решение, но я не знаю более идиоматического

  • Проверьте перед запуском полного ETL для примера перед Kiba.parse блок
  • Проверьте в pre_process блок внутри ETL
  • проверьте в источнике ETL. Я предпочитаю этот, так как он будет более многоразовым (необходимо передать обязательное поле в качестве параметров)

Обратите внимание, что даже если я могу проверить в transform заблокировать какие поля доступны на rowЭто решение кажется не очень эффективным, так как оно будет работать для каждой строки.

Любые советы приветствуются

1 ответ

Решение

Существуют различные и совершенно идиоматические способы достижения этой цели:

На уровне источника (передавая массив заголовков)

Ты можешь использовать CSV без headers: true, что дает возможность точно проверить заголовки:

class CSVSource
  def initialize(filename:, csv_options:, expected_headers:)
  # SNIP

  def each
    CSV.foreach(filename, csv_options).with_index do |row, file_row_index|
      if file_row_index == 0
        check_headers!(actual: row.to_a, expected: expected_headers)
        next # do not propagate the headers row
      else
        yield(Hash[expected_headers.zip(row.to_a)])
      end
    end
  end

  def check_headers!(actual:, expected:)
  # SNIP - verify uniqueness, presence, raise a clear message if needed
end     

На уровне источника (позволяя вызывающей стороне определить поведение, используя лямбду)

class CSVSource
  def initialize(after_headers_read_callback:, ...)
    @after_headers_read_callback = ...

  def each
    CSV.foreach(filename, csv_options).with_index do |row, file_row_index|
      if file_row_index == 0
        @after_headers_read_callback.call(row.to_a)
        next
      end
      # ...
    end
  end

Лямбда позволяет вызывающей стороне определять свои собственные проверки, поднимать при необходимости и т. Д., Что лучше для повторного использования.

На уровне трансформации

Если вы хотите дополнительно отделить компоненты (например, отделить обработку заголовков от факта, что строки происходят из источника CSV), вы можете использовать преобразование.

Я обычно использую этот дизайн, который позволяет лучше использовать повторно (здесь с источником CSV, который даст немного метаданных):

def transform_array_rows_to_hash_rows(after_headers_read_callback:)
  transform do |row|
    if row.fetch(:file_row_index) == 0
      @headers = row.fetch(:row)
      after_headers_read_callback.call(@headers)
      nil
    else
      Hash[@headers.zip(row.fetch(:row))].merge(
        filename: row.fetch(:filename),
        file_row_index: row.fetch(:file_row_index)
      )
    end
  end
end

Что не рекомендуется

Во всех случаях избегайте какой-либо обработки в Kiba.parse сам. Это лучший дизайн, чтобы гарантировать, что IO будет происходить только когда вы звоните Kiba.run (поскольку он будет более ориентирован на будущее и будет поддерживать функции самоанализа в более поздних версиях Kiba).

Кроме того, используя pre_process не рекомендуется (хотя это будет работать), потому что это приведет к небольшому дублированию и т. д.

Надеюсь, это поможет, и дайте мне знать, если это не ясно!

Другие вопросы по тегам