Class: TableData::Table

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/tabledata/table.rb

Overview

This class represents the tabular data.

Constant Summary collapse

DefaultOptions =

The default options for TableData::Table#initialize

{
  has_header: true,
  has_footer: false, # currently unused
  accessors:  [],
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data = [], options = nil) ⇒ Table

Returns a new instance of Table.



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# 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

Instance Attribute Details

#accessor_columnsHash<Symbol => Integer> (readonly)

Returns A hash mapping column accessor names to the column index.

Returns:

  • (Hash<Symbol => Integer>)

    A hash mapping column accessor names to the column index



51
52
53
# File 'lib/tabledata/table.rb', line 51

def accessor_columns
  @accessor_columns
end

#accessorsArray<Symbol>

Returns An array of all named accessors.

Returns:

  • (Array<Symbol>)

    An array of all named accessors



48
49
50
# File 'lib/tabledata/table.rb', line 48

def accessors
  @accessors
end

#dataObject (readonly)

The internal data structure. Do not modify.



55
56
57
# File 'lib/tabledata/table.rb', line 55

def data
  @data
end

Class Method Details

.from_file(path, options = nil) ⇒ 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

Parameters:

  • options (Hash) (defaults to: nil)

    a customizable set of options

Options Hash (options):

  • :file_type (Symbol)

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

  • :table_class (Symbol)

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

Returns:



34
35
36
37
38
39
40
41
42
43
44
45
# 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

Instance Method Details

#<<(row) ⇒ Object

Raises:



174
175
176
177
178
179
180
181
182
183
184
# 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) ⇒ Array<TableData::Row>

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

Returns:



107
108
109
# File 'lib/tabledata/table.rb', line 107

def [](*args)
  body[*args]
end

#accessors?Boolean

Returns:

  • (Boolean)


158
159
160
# File 'lib/tabledata/table.rb', line 158

def accessors?
  !@accessors.empty?
end

#bodyObject



170
171
172
# File 'lib/tabledata/table.rb', line 170

def body
  headers? ? @rows[1..-1] : @rows
end

#cell(row, column, default = nil) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
# 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) ⇒ Object



141
142
143
# File 'lib/tabledata/table.rb', line 141

def column(index)
  Column.new(self, index)
end

#column_accessor(index) ⇒ Object



127
128
129
# File 'lib/tabledata/table.rb', line 127

def column_accessor(index)
  @column_accessors[index]
end

#column_countInteger

Returns The number of columns.

Returns:

  • (Integer)

    The number of columns



100
101
102
# File 'lib/tabledata/table.rb', line 100

def column_count
  @data.first ? @data.first.size : 0
end

#column_name(index) ⇒ Object



131
132
133
134
135
# File 'lib/tabledata/table.rb', line 131

def column_name(index)
  h = headers

  h && h.at(index)
end

#columnsObject



137
138
139
# File 'lib/tabledata/table.rb', line 137

def columns
  Array.new(column_count) { |col| column(col) }
end

#each {|row| ... } ⇒ self

Iterate over all rows in the body

Yields:

Yield Parameters:

Returns:

  • (self)

See Also:



194
195
196
197
198
199
200
# File 'lib/tabledata/table.rb', line 194

def each(&block)
  return enum_for(__method__) unless block

  body.each(&block)

  self
end

#each_column {|column| ... } ⇒ self

Iterate over all columns

Yields:

Yield Parameters:

Returns:

  • (self)


224
225
226
227
228
229
230
231
232
# 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 {|row| ... } ⇒ self

Iterate over all rows, header and body

Yields:

Yield Parameters:

Returns:

  • (self)

See Also:



210
211
212
213
214
215
216
# 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) ⇒ Object



242
243
244
# File 'lib/tabledata/table.rb', line 242

def format(format_id, options=nil)
  Presenter.present(self, format_id, options)
end

#headersObject



166
167
168
# File 'lib/tabledata/table.rb', line 166

def headers
  headers? ? @rows.first : nil
end

#headers?Boolean

Returns:

  • (Boolean)


162
163
164
# File 'lib/tabledata/table.rb', line 162

def headers?
  @has_header
end

#index_for_accessor(name) ⇒ Object



145
146
147
# File 'lib/tabledata/table.rb', line 145

def index_for_accessor(name)
  @accessor_columns[name.to_sym]
end

#index_for_header(name) ⇒ Object



149
150
151
152
153
154
155
156
# 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

#inspectObject



246
247
248
# File 'lib/tabledata/table.rb', line 246

def inspect
  sprintf "#<%s headers: %p, cols: %d, rows: %d>", self.class, headers?, column_count, size
end

#row(row) ⇒ Object



123
124
125
# File 'lib/tabledata/table.rb', line 123

def row(row)
  @rows[row]
end

#sizeObject Also known as: length

The number of rows, excluding headers



94
95
96
# File 'lib/tabledata/table.rb', line 94

def size
  @data.size - (@has_header ? 1 : 0)
end

#to_aObject



238
239
240
# File 'lib/tabledata/table.rb', line 238

def to_a
  @data
end

#to_nested_arrayObject



234
235
236
# File 'lib/tabledata/table.rb', line 234

def to_nested_array
  to_a.map(&:to_a)
end