Class: CsvRowModel::Import::Csv

Inherits:
Object
  • Object
show all
Includes:
ActiveModel::Validations
Defined in:
lib/csv_row_model/import/csv.rb

Overview

Abstraction of Ruby's CSV library. Keeps current row and index, skips empty rows, handles errors.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file_path) ⇒ Csv

Returns a new instance of Csv.



21
22
23
24
# File 'lib/csv_row_model/import/csv.rb', line 21

def initialize(file_path)
  @file_path = file_path
  reset
end

Instance Attribute Details

#current_rowArray? (readonly)

Returns the current row, or nil at the beginning or end of file.

Returns:

  • (Array, nil)

    the current row, or nil at the beginning or end of file



10
11
12
# File 'lib/csv_row_model/import/csv.rb', line 10

def current_row
  @current_row
end

#file_pathString (readonly)

Returns the file path of the CSV.

Returns:

  • (String)

    the file path of the CSV



6
7
8
# File 'lib/csv_row_model/import/csv.rb', line 6

def file_path
  @file_path
end

#indexInteger? (readonly)

Return -1 at start of file, 0 to infinity is index of row_model, nil is end of file (row is also nil)

Returns:

  • (Integer, nil)

    return -1 at start of file, 0 to infinity is index of row_model, nil is end of file (row is also nil)



8
9
10
# File 'lib/csv_row_model/import/csv.rb', line 8

def index
  @index
end

#skipped_rowsHash{Integer => Symbol} (readonly)

Returns hash of skipped rows from last change in position, index => :reason.

Returns:

  • (Hash{Integer => Symbol})

    hash of skipped rows from last change in position, index => :reason



12
13
14
# File 'lib/csv_row_model/import/csv.rb', line 12

def skipped_rows
  @skipped_rows
end

Instance Method Details

#_read_row(skipped_rows = {}, index = @index, ruby_csv = @ruby_csv) ⇒ Object (protected)



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/csv_row_model/import/csv.rb', line 102

def _read_row(skipped_rows={}, index=@index, ruby_csv=@ruby_csv)
  return unless valid?

  row = ruby_csv.readline
  raise "empty?" if row.try(:empty?)

  index += 1 if index

  yield row if block_given?
  row
rescue Exception => e
  index += 1 if index
  yield [] if block_given?

  # thanks to the ruby CSV library, quotes can still escape to the next line
  reason = case e.message
             when "empty?"; :empty
             when /Illegal quoting/i; :illegal_quote
             when /Unclosed quoted/i; :unclosed_quote
           end

  skipped_rows.merge!(index => reason)

  retry
end

#_ruby_csvObject (protected)



98
99
100
# File 'lib/csv_row_model/import/csv.rb', line 98

def _ruby_csv
  CSV.open(file_path)
end

#end_of_file?Boolean

Returns true, if the current position is at the end of the file.

Returns:

  • (Boolean)

    true, if the current position is at the end of the file



66
67
68
# File 'lib/csv_row_model/import/csv.rb', line 66

def end_of_file?
  index.nil?
end

#headerArray?

Returns the header without changing the position of the CSV

Returns:

  • (Array, nil)

    the header



40
41
42
43
44
45
46
47
48
# File 'lib/csv_row_model/import/csv.rb', line 40

def header
  return unless valid?
  return @header if @header

  ruby_csv = _ruby_csv
  @header = _read_row({}, 0, ruby_csv)
  ruby_csv.close
  @header
end

#increment_index(current_row) ⇒ Object (protected)



128
129
130
# File 'lib/csv_row_model/import/csv.rb', line 128

def increment_index(current_row)
  current_row.nil? ? set_end_of_file : @index += 1
end

#next_rowArray?

Returns the next row without changing the position of the CSV

Returns:

  • (Array, nil)

    the next row, or nil at the end of file



72
73
74
75
# File 'lib/csv_row_model/import/csv.rb', line 72

def next_row
  @next_skipped_rows = {}
  @next_row ||= _read_row(@next_skipped_rows)
end

#read_rowArray?

Returns the next row, while changing the position of the CSV

Returns:

  • (Array, nil)

    the changed current row, or nil at the end of file



79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/csv_row_model/import/csv.rb', line 79

def read_row
  if @next_row
    @current_row, @skipped_rows = @next_row, @next_skipped_rows
    @next_row = nil
    increment_index(@current_row)
  else
    @skipped_rows = {}
    @current_row = _read_row(@skipped_rows) do |row|
      increment_index(row)
    end
  end
  current_row
end

#resetObject

Resets the file to the start of file



51
52
53
54
55
56
57
58
# File 'lib/csv_row_model/import/csv.rb', line 51

def reset
  return unless valid?

  @index = -1
  @current_row = @next_row = @skipped_rows = @next_skipped_rows = nil
  @ruby_csv = _ruby_csv
  true
end

#set_end_of_fileObject (protected)



94
95
96
# File 'lib/csv_row_model/import/csv.rb', line 94

def set_end_of_file
  @current_row = @index = nil
end

#sizeInteger

Returns:

  • (Integer)

    the number of rows in the file, including empty new lines



28
29
30
# File 'lib/csv_row_model/import/csv.rb', line 28

def size
  @size ||= `wc -l #{file_path}`.split[0].to_i + 1
end

#skip_headerBoolean, Array

If the current position is at the header, skip it and return it. Otherwise, only return false.

Returns:

  • (Boolean, Array)

    returns false, if header is already skipped, otherwise returns the header



34
35
36
# File 'lib/csv_row_model/import/csv.rb', line 34

def skip_header
  start_of_file? ? (@header = read_row) : false
end

#start_of_file?Boolean

Returns true, if the current position is at the start of the file.

Returns:

  • (Boolean)

    true, if the current position is at the start of the file



61
62
63
# File 'lib/csv_row_model/import/csv.rb', line 61

def start_of_file?
  index == -1
end