1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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
93
94
95
96
97
98
99
100
101
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
127
|
require "csv"
require "time"
# The CSV class can read csv files (must be separated with commas) which then
# can be handled like spreadsheets. This means you can access cells like A5
# within these files.
# The CSV class provides only string objects. If you want conversions to other
# types you have to do it yourself.
#
# You can pass options to the underlying CSV parse operation, via the
# :csv_options option.
module Roo
class CSV < Roo::Base
attr_reader :filename
# Returns an array with the names of the sheets. In CSV class there is only
# one dummy sheet, because a csv file cannot have more than one sheet.
def sheets
["default"]
end
def cell(row, col, sheet = nil)
sheet ||= default_sheet
read_cells(sheet)
@cell[normalize(row, col)]
end
def celltype(row, col, sheet = nil)
sheet ||= default_sheet
read_cells(sheet)
@cell_type[normalize(row, col)]
end
def cell_postprocessing(_row, _col, value)
value
end
def csv_options
@options[:csv_options] || {}
end
def set_value(row, col, value, _sheet)
@cell[[row, col]] = value
end
def set_type(row, col, type, _sheet)
@cell_type[[row, col]] = type
end
private
TYPE_MAP = {
String => :string,
Float => :float,
Date => :date,
DateTime => :datetime,
}
def celltype_class(value)
TYPE_MAP[value.class]
end
def read_cells(sheet = default_sheet)
sheet ||= default_sheet
return if @cells_read[sheet]
set_row_count(sheet)
set_column_count(sheet)
row_num = 1
each_row csv_options do |row|
row.each_with_index do |elem, col_num|
coordinate = [row_num, col_num + 1]
@cell[coordinate] = elem
@cell_type[coordinate] = celltype_class(elem)
end
row_num += 1
end
@cells_read[sheet] = true
end
def each_row(options, &block)
if uri?(filename)
each_row_using_temp_dir(filename)
elsif is_stream?(filename_or_stream)
::CSV.new(filename_or_stream, options).each(&block)
else
::CSV.foreach(filename, options, &block)
end
end
def each_row_using_tempdir
::Dir.mktmpdir(Roo::TEMP_PREFIX, ENV["ROO_TMP"]) do |tmpdir|
tmp_filename = download_uri(filename, tmpdir)
::CSV.foreach(tmp_filename, options, &block)
end
end
def set_row_count(sheet)
@first_row[sheet] = 1
@last_row[sheet] = ::CSV.readlines(@filename, csv_options).size
@last_row[sheet] = @first_row[sheet] if @last_row[sheet].zero?
nil
end
def set_column_count(sheet)
@first_column[sheet] = 1
@last_column[sheet] = (::CSV.readlines(@filename, csv_options).first || []).size
@last_column[sheet] = @first_column[sheet] if @last_column[sheet].zero?
nil
end
def clean_sheet(sheet)
read_cells(sheet)
@cell.each_pair do |coord, value|
@cell[coord] = sanitize_value(value) if value.is_a?(::String)
end
@cleaned[sheet] = true
end
alias_method :filename_or_stream, :filename
end
end
|