lib/roo/csv.rb in roo-2.6.0 vs lib/roo/csv.rb in roo-2.7.0
- old
+ new
@@ -1,132 +1,127 @@
-require 'csv'
-require 'time'
+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
-class Roo::CSV < Roo::Base
+ # 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
- attr_reader :filename
+ def cell(row, col, sheet = nil)
+ sheet ||= default_sheet
+ read_cells(sheet)
+ @cell[normalize(row, col)]
+ end
- # 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 celltype(row, col, sheet = nil)
+ sheet ||= default_sheet
+ read_cells(sheet)
+ @cell_type[normalize(row, col)]
+ end
- def cell(row, col, sheet=nil)
- sheet ||= default_sheet
- read_cells(sheet)
- @cell[normalize(row,col)]
- end
+ def cell_postprocessing(_row, _col, value)
+ value
+ end
- def celltype(row, col, sheet=nil)
- sheet ||= default_sheet
- read_cells(sheet)
- @cell_type[normalize(row,col)]
- end
+ def csv_options
+ @options[:csv_options] || {}
+ end
- def cell_postprocessing(row,col,value)
- value
- end
+ def set_value(row, col, value, _sheet)
+ @cell[[row, col]] = value
+ end
- def csv_options
- @options[:csv_options] || {}
- end
+ def set_type(row, col, type, _sheet)
+ @cell_type[[row, col]] = type
+ end
- def set_value(row, col, value, _sheet)
- @cell[[row, col]] = value
- end
+ private
- def set_type(row, col, type, _sheet)
- @cell_type[[row, col]] = type
- end
+ TYPE_MAP = {
+ String => :string,
+ Float => :float,
+ Date => :date,
+ DateTime => :datetime,
+ }
- private
+ def celltype_class(value)
+ TYPE_MAP[value.class]
+ end
- TYPE_MAP = {
- String => :string,
- Float => :float,
- Date => :date,
- DateTime => :datetime,
- }
+ 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
- def celltype_class(value)
- TYPE_MAP[value.class]
- end
+ 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
- def each_row(options, &block)
- if uri?(filename)
- ::Dir.mktmpdir(Roo::TEMP_PREFIX, ENV['ROO_TMP']) do |tmpdir|
- tmp_filename = download_uri(filename, tmpdir)
- CSV.foreach(tmp_filename, options, &block)
+ @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
- elsif is_stream?(filename_or_stream)
- CSV.new(filename_or_stream, options).each(&block)
- else
- CSV.foreach(filename, options, &block)
end
- end
- def read_cells(sheet = default_sheet)
- sheet ||= default_sheet
- return if @cells_read[sheet]
- @first_row[sheet] = 1
- @last_row[sheet] = 0
- @first_column[sheet] = 1
- @last_column[sheet] = 1
- rownum = 1
- each_row csv_options do |row|
- row.each_with_index do |elem,i|
- @cell[[rownum,i+1]] = cell_postprocessing rownum,i+1, elem
- @cell_type[[rownum,i+1]] = celltype_class @cell[[rownum,i+1]]
- if i+1 > @last_column[sheet]
- @last_column[sheet] += 1
- 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
- rownum += 1
- @last_row[sheet] += 1
end
- @cells_read[sheet] = true
- #-- adjust @first_row if neccessary
- while !row(@first_row[sheet]).any? and @first_row[sheet] < @last_row[sheet]
- @first_row[sheet] += 1
+
+ def set_row_count(sheet)
+ @first_row[sheet] = 1
+ @last_row[sheet] = ::CSV.readlines(@filename).size
+ @last_row[sheet] = @first_row[sheet] if @last_row[sheet].zero?
+
+ nil
end
- #-- adjust @last_row if neccessary
- while !row(@last_row[sheet]).any? and @last_row[sheet] and
- @last_row[sheet] > @first_row[sheet]
- @last_row[sheet] -= 1
+
+ def set_column_count(sheet)
+ @first_column[sheet] = 1
+ @last_column[sheet] = (::CSV.readlines(@filename).first || []).size
+ @last_column[sheet] = @first_column[sheet] if @last_column[sheet].zero?
+
+ nil
end
- #-- adjust @first_column if neccessary
- while !column(@first_column[sheet]).any? and
- @first_column[sheet] and
- @first_column[sheet] < @last_column[sheet]
- @first_column[sheet] += 1
- end
- #-- adjust @last_column if neccessary
- while !column(@last_column[sheet]).any? and
- @last_column[sheet] and
- @last_column[sheet] > @first_column[sheet]
- @last_column[sheet] -= 1
- end
- end
- def clean_sheet(sheet)
- read_cells(sheet)
+ def clean_sheet(sheet)
+ read_cells(sheet)
- @cell.each_pair do |coord, value|
- @cell[coord] = sanitize_value(value) if value.is_a?(::String)
+ @cell.each_pair do |coord, value|
+ @cell[coord] = sanitize_value(value) if value.is_a?(::String)
+ end
+
+ @cleaned[sheet] = true
end
- @cleaned[sheet] = true
+ alias_method :filename_or_stream, :filename
end
-
- alias_method :filename_or_stream, :filename
end