class TableData::Table

This class represents the tabular data.

Constants

DefaultOptions

The default options for TableData::Table#initialize

Attributes

accessor_columns[R]

@return [Hash<Symbol => Integer>] A hash mapping column accessor names to the column index

accessors[R]

@return [Array<Symbol>] An array of all named accessors

data[R]

@private The internal data structure. Do not modify.

Public Class Methods

from_file(path, options=nil) click to toggle source

@option options [Symbol] :file_type

The file type. Nil for auto-detection (which uses the extension of the
filename), or one of :csv, :xls or :xlsx

@option options [Symbol] :table_class

The class to use for this table. Defaults to self (TableData::Table)

All other options are passed on to Parser.parse_csv, .parse_xls or parse_xlsx, which in turn passes remaining options on to Table#initialize

@return [TableData::Table]

# File lib/tabledata/table.rb, line 34
def self.from_file(path, options=nil)
  options ||= {}
  options[:table_class] ||= self
  options[:file_type]   ||= Detection.file_type_from_path(path)

  case options[:file_type]
    when :csv then Parser.parse_csv(path, options)
    when :xls then Parser.parse_xls(path, options)
    when :xlsx then Parser.parse_xlsx(path, options)
    else raise InvalidFileType, "Unknown file format #{options[:file_type].inspect}"
  end
end
new(data=[], options=nil) click to toggle source
# File lib/tabledata/table.rb, line 57
def initialize(data=[], options=nil)
  options           = options ? self.class::DefaultOptions.merge(options) : self.class::DefaultOptions.dup
  column_count      = data.first ? data.first.size : 0
  @has_header       = options.delete(:has_header) ? true : false
  @data             = data
  @rows             = data.map.with_index { |row, index|
    raise InvalidColumnCount, "Invalid column count in row #{index} (#{column_count} expected, but has #{row.size})" if index > 0 && row.size != column_count
    raise ArgumentError, "Row must be provided as Array, but got #{row.class} in row #{index}" unless row.is_a?(Array)

    Row.new(self, index, row)
  }
  @column_count     = nil
  @header_columns   = nil
  @accessor_columns = {}
  @column_accessors  = {}
  @accessors        = [].freeze
  self.accessors    = options.delete(:accessors)
end

Public Instance Methods

<<(row) click to toggle source
# File lib/tabledata/table.rb, line 174
def <<(row)
  index  = @data.size

  raise InvalidColumnCount, "Invalid column count in row #{index} (#{@data.first.size} expected, but has #{row.size})" if @data.first && row.size != @data.first.size
  raise ArgumentError, "Row must be provided as Array, but got #{row.class} in row #{index}" unless row.is_a?(Array)

  @data << row
  @rows << Row.new(self, index, row)

  self
end
[](*args) click to toggle source

Array#[] like access to the rows in the body of the table.

@return [Array<TableData::Row>]

# File lib/tabledata/table.rb, line 107
def [](*args)
  body[*args]
end
accessors=(accessors) click to toggle source

@param [Array<Symbol>] accessors

Define the name of the accessors used in TableData::Row.

# File lib/tabledata/table.rb, line 79
def accessors=(accessors)
  if accessors
    @accessors = accessors.map(&:to_sym).freeze
    @accessors.each_with_index do |name, idx|
      @accessor_columns[name] = idx
    end
    @column_accessors  = @accessor_columns.invert
  else
    @accessors = [].freeze
    @accessor_columns.clear
    @column_accessors  = @accessor_columns.clear
  end
end
accessors?() click to toggle source
# File lib/tabledata/table.rb, line 158
def accessors?
  !@accessors.empty?
end
body() click to toggle source
# File lib/tabledata/table.rb, line 170
def body
  headers? ? @rows[1..-1] : @rows
end
cell(row, column, default=nil) { |self, row, column| ... } click to toggle source
# File lib/tabledata/table.rb, line 111
def cell(row, column, default=nil)
  row_data = row(row)

  if row_data
    row_data.at(column)
  elsif block_given?
    yield(self, row, column)
  else
    default
  end
end
column(index) click to toggle source
# File lib/tabledata/table.rb, line 141
def column(index)
  Column.new(self, index)
end
column_accessor(index) click to toggle source
# File lib/tabledata/table.rb, line 127
def column_accessor(index)
  @column_accessors[index]
end
column_count() click to toggle source

@return [Integer] The number of columns

# File lib/tabledata/table.rb, line 100
def column_count
  @data.first ? @data.first.size : 0
end
column_name(index) click to toggle source
# File lib/tabledata/table.rb, line 131
def column_name(index)
  h = headers

  h && h.at(index)
end
columns() click to toggle source
# File lib/tabledata/table.rb, line 137
def columns
  Array.new(column_count) { |col| column(col) }
end
each(&block) click to toggle source

Iterate over all rows in the body

@see TableData::Table#each_row A method which iterates over all rows, including headers

@yield [row] @yieldparam [TableData::Row]

@return [self]

# File lib/tabledata/table.rb, line 194
def each(&block)
  return enum_for(__method__) unless block

  body.each(&block)

  self
end
each_column() { |column(i)| ... } click to toggle source

Iterate over all columns

@yield [column] @yieldparam [TableData::Column]

@return [self]

# File lib/tabledata/table.rb, line 224
def each_column
  return enum_for(__method__) unless block

  column_count.times do |i|
    yield column(i)
  end

  self
end
each_row(&block) click to toggle source

Iterate over all rows, header and body

@see TableData::Table#each A method which iterates only over body-rows

@yield [row] @yieldparam [TableData::Row]

@return [self]

# File lib/tabledata/table.rb, line 210
def each_row(&block)
  return enum_for(__method__) unless block

  @data.each(&block)

  self
end
format(format_id, options=nil) click to toggle source
# File lib/tabledata/table.rb, line 242
def format(format_id, options=nil)
  Presenter.present(self, format_id, options)
end
headers() click to toggle source
# File lib/tabledata/table.rb, line 166
def headers
  headers? ? @rows.first : nil
end
headers?() click to toggle source
# File lib/tabledata/table.rb, line 162
def headers?
  @has_header
end
index_for_accessor(name) click to toggle source
# File lib/tabledata/table.rb, line 145
def index_for_accessor(name)
  @accessor_columns[name.to_sym]
end
index_for_header(name) click to toggle source
# File lib/tabledata/table.rb, line 149
def index_for_header(name)
  if @has_header && @data.first then
    @header_columns ||= Hash[@data.first.each_with_index.to_a]
    @header_columns[name]
  else
    nil
  end
end
inspect() click to toggle source
# File lib/tabledata/table.rb, line 246
def inspect
  sprintf "#<%s headers: %p, cols: %d, rows: %d>", self.class, headers?, column_count, size
end
length()
Alias for: size
row(row) click to toggle source
# File lib/tabledata/table.rb, line 123
def row(row)
  @rows[row]
end
size() click to toggle source

The number of rows, excluding headers

# File lib/tabledata/table.rb, line 94
def size
  @data.size - (@has_header ? 1 : 0)
end
Also aliased as: length
to_a() click to toggle source
# File lib/tabledata/table.rb, line 238
def to_a
  @data
end
to_nested_array() click to toggle source
# File lib/tabledata/table.rb, line 234
def to_nested_array
  to_a.map(&:to_a)
end