Class: Reading::Parsing::CSV

Inherits:
Object
  • Object
show all
Defined in:
lib/reading/parsing/csv.rb

Overview

Validates a path or lines (string, file, etc.) of a CSV reading log, then parses it into an array of Items.

Parsing happens in two steps:

(1) Parse a row string into an intermediate hash representing the columns.
    - See parsing/parser.rb, which uses parsing/rows/*
(2) Transform the intermediate hash into an array of hashes structured
    around item attributes rather than CSV columns.
    - See parsing/transformer.rb, which uses parsing/attributes/*

Keeping these steps separate makes the code easier to understand. It was inspired by the Parslet gem: kschiess.github.io/parslet/transform.html

Instance Method Summary collapse

Constructor Details

#initialize(path: nil, lines: nil, config: nil, hash_output: false, item_view: Item::View, error_handler: nil) ⇒ CSV

Validates a path or lines (string, file, etc.) of a CSV reading log, builds the config, and initializes the parser and transformer.

Parameters:

  • path (String) (defaults to: nil)

    path to the CSV file; used if no lines are given.

  • lines (Object) (defaults to: nil)

    an object responding to #each_line with CSV row(s); if nil, path is used instead.

  • config (Hash, Config) (defaults to: nil)

    a custom config which overrides the defaults, e.g. { errors: { styling: :html } }

  • hash_output (Boolean) (defaults to: false)

    whether an array of raw Hashes should be returned, without Items being created from them.

  • item_view (Class, nil, Boolean) (defaults to: Item::View)

    the class that will be used to build each Item’s view object, or nil/false if no view object should be built. If you use a custom view class, the only requirement is that its #initialize take an Item and a full config as arguments.

  • error_handler (Proc) (defaults to: nil)

    if not provided, errors are raised.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/reading/parsing/csv.rb', line 39

def initialize(path: nil, lines: nil, config: nil, hash_output: false, item_view: Item::View, error_handler: nil)
  validate_path_or_lines(path, lines)

  Config.build(config) if config

  @path = path
  @lines = lines
  @hash_output = hash_output
  @item_view = item_view
  @parser = Parser.new
  @transformer = Transformer.new
  @error_handler = error_handler
  @pastel = Pastel.new
end

Instance Method Details

#parseArray<Item>

Parses and transforms the reading log into item data.

Returns:

  • (Array<Item>)

    an array of Items like the template in Config#default_config[:template]. The Items are identical in structure to that Hash (with every inner Hash replaced by a Data for dot access).



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/reading/parsing/csv.rb', line 59

def parse
  input = @lines || File.open(@path)
  items = []

  input.each_line do |line|
    begin
      intermediate = parser.parse_row_to_intermediate_hash(line)

      next if intermediate.empty? # When the row is blank or a comment.

      row_items = transformer.transform_intermediate_hash_to_item_hashes(intermediate)
    rescue Reading::Error => e
      colored_e =
        e.class.new("#{pastel.bright_red(e.message)} in the row #{pastel.bright_yellow(line.chomp)}")

      if error_handler
        error_handler.call(colored_e)
        next
      else
        raise colored_e
      end
    end

    items += row_items
  end

  if hash_output
    items
  else
    items.map { |item_hash| Item.new(item_hash, view: item_view) }
  end
ensure
  input&.close if input.respond_to?(:close)
end