lib/fat_table/table.rb in fat_table-0.9.3 vs lib/fat_table/table.rb in fat_table-0.9.5
- old
+ new
@@ -154,18 +154,22 @@
# will be taken from the first CSV row and converted to symbols.
def self.from_csv_file(fname, **types)
File.open(fname, 'r') do |io|
from_csv_io(io, **types)
end
+ rescue NoTable
+ raise NoTable, "no table found in CSV file '#{fname}'"
end
# :category: Constructors
# Construct a Table from a CSV string +str+, treated in the same manner as
# the input from a CSV file in ::from_org_file.
- def self.from_csv_string(str, **types)
- from_csv_io(StringIO.new(str), **types)
+ def self.from_csv_string(str, has_headers: true, **)
+ from_csv_io(StringIO.new(str), has_headers:, **)
+ rescue NoTable
+ raise NoTable, "no table found in string '#{str[0..20]}...'"
end
# :category: Constructors
# Construct a Table from the first table found in the given Emacs org-mode
@@ -174,18 +178,22 @@
# etc. are created.
def self.from_org_file(fname, **types)
File.open(fname, 'r') do |io|
from_org_io(io, **types)
end
+ rescue NoTable
+ raise NoTable, "no table found in file '#{fname}'"
end
# :category: Constructors
# Construct a Table from a string +str+, treated in the same manner as the
# contents of an org-mode file in ::from_org_file.
def self.from_org_string(str, **types)
from_org_io(StringIO.new(str), **types)
+ rescue NoTable
+ raise NoTable, "no table found in string '#{str[0..20]...}'"
end
# :category: Constructors
# Construct a new table from an Array of Arrays +aoa+. By default, with
@@ -273,13 +281,13 @@
private
# Construct table from an array of hashes or an array of any object that
# can respond to #to_h. If an array element is a nil, mark it as a group
# boundary in the Table.
- def from_array_of_hashes(hashes, hlines: false, **types)
+ def from_array_of_hashes(hashes, hlines: false, **)
heads = hashes.first.keys
- result = new(*heads, **types)
+ result = new(*heads, **)
hashes.each do |hsh|
if hsh.nil?
unless hlines
msg = 'found an hline in input: try setting hlines true'
raise UserError, msg
@@ -303,11 +311,11 @@
# element of the outer array is a nil, mark the preceding row as a group
# boundary. Note: In org mode code blocks, by default (:hlines no) all
# hlines are stripped from the table, otherwise (:hlines yes) they are
# indicated with nil elements in the outer array as expected by this
# method when hlines is set true.
- def from_array_of_arrays(rows, hlines: false, **types)
+ def from_array_of_arrays(rows, hlines: false, **)
headers = []
if !hlines
# Take the first row as headers
# Second row et seq as data
headers = rows[0].map(&:to_s).map(&:as_sym)
@@ -322,11 +330,11 @@
# Synthesize headers
# Row 0 et seq are data
headers = (1..rows[0].size).to_a.map { |k| "col_#{k}".as_sym }
first_data_row = 0
end
- result = new(*headers, **types)
+ result = new(*headers, **)
rows[first_data_row..-1].each do |row|
if row.nil?
unless hlines
msg = 'found an hline in input: try setting hlines true'
raise UserError, msg
@@ -340,28 +348,39 @@
end
result.normalize_boundaries
result
end
- def from_csv_io(io, **types)
- result = new(**types)
- ::CSV.new(io, headers: true, header_converters: :symbol,
- skip_blanks: true).each do |row|
- result << row.to_h
+ def from_csv_io(io, has_headers: true, **)
+ result = new(**)
+ if has_headers
+ ::CSV.new(io, headers: has_headers, header_converters: :symbol, skip_blanks: true).each do |row|
+ result << row.to_h
+ end
+ else
+ nfields = io.readline.split(',').size
+ io.seek(0, IO::SEEK_SET)
+ heads = (1..nfields).map {|n| "col_#{n}"}
+ ::CSV.new(io, headers: heads, skip_blanks: true).each do |row|
+ result << row.to_h
+ end
end
result.normalize_boundaries
result
+ rescue StandardError
+ raise NoTable
end
# Form rows of table by reading the first table found in the org file. The
# header row must be marked with an hline (i.e, a row that looks like
# '|---+--...--|') and groups of rows may be marked with hlines to
# indicate group boundaries.
- def from_org_io(io, **types)
+ def from_org_io(io, **)
table_re = /\A\s*\|/
hrule_re = /\A\s*\|[-+]+/
rows = []
+ ncols = nil
table_found = false
header_found = false
io.each do |line|
unless table_found
# Skip through the file until a table is found
@@ -386,14 +405,21 @@
elsif !line.match?(table_re)
# Stop reading at the second hline
break
else
line = line.sub(/\A\s*\|/, '').sub(/\|\s*\z/, '')
- rows << line.split('|').map(&:clean)
+ # Don't include any rows with a size different from that
+ # established by the header row.
+ cols = line.split('|').map(&:clean)
+ ncols ||= cols.size
+ next unless cols.size == ncols
+
+ rows << cols
end
end
- from_array_of_arrays(rows, hlines: true, **types)
+ raise NoTable unless table_found
+ from_array_of_arrays(rows, hlines: true, **)
end
end
###########################################################################
# Attributes
@@ -715,9 +741,10 @@
def normalize_boundaries
unless empty?
self.explicit_boundaries = explicit_boundaries.uniq.sort
end
explicit_boundaries
+ self
end
# Return the explicit_boundaries, augmented by an implicit boundary for
# the end of the table, unless it's already an implicit boundary.
def boundaries