Class: Tabledata::Row

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

Overview

Note:

The row data is referenced. If you mutate the table, adding a row to it, this row instance's data will not change. Its index will be off.

Represents a row in a table and provides an easy way to enumerate and access values in a row.

If your table defines accessors, you can access columns by using methods of that name.

Examples:

table = Tabledata.table body: [[1,2,3], [4,5,6]], accessors: %i[foo bar baz]
table[0].foo     # => 1
table[1].baz     # => 6
table[1].bar = 9

Direct Known Subclasses

CoercedRow

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(table, index, data = nil) ⇒ Row


40
41
42
43
44
# File 'lib/tabledata/row.rb', line 40

def initialize(table, index, data=nil)
  @table  = table
  @index  = index
  @data   = data || @table.data[index]
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object

Allow reading and writing cell values by their accessor name.

Raises:

  • (ArgumentError)

219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/tabledata/row.rb', line 219

def method_missing(name, *args, &block)
  return super unless @table.accessors?

  name              =~ /^(\w+)(=)?$/
  name_mod, assign  = $1, $2
  index             = @table.index_for_accessor(name_mod)
  arg_count         = assign ? 1 : 0

  return super unless index

  raise ArgumentError, "Wrong number of arguments (#{args.size} for #{arg_count})" if args.size > arg_count

  if assign then
    @data[index] = args.first
  else
    @data[index]
  end
end

Instance Attribute Details

#dataArray (readonly)


30
31
32
# File 'lib/tabledata/row.rb', line 30

def data
  @data
end

#indexInteger (readonly)


27
28
29
# File 'lib/tabledata/row.rb', line 27

def index
  @index
end

#tableTabledata::Table (readonly)


24
25
26
# File 'lib/tabledata/row.rb', line 24

def table
  @table
end

Instance Method Details

#[](a, b = nil) ⇒ Array, Object

Convenience access of values in the row. Can either be used like Array#[], i.e. it accepts an offset, an offset + length, or an offset-to-offset range. Alternatively you can use a Symbol, if it's a valid accessor in this table. And the last variant is using a String, which will access the value of the corresponding header.


84
85
86
87
88
89
90
# File 'lib/tabledata/row.rb', line 84

def [](a,b=nil)
  if b || a.is_a?(Range) then
    slice(a,b)
  else
    at(a)
  end
end

#at(column) ⇒ Object

Access a single cell by either index, index-range, accessor or header-name.

Examples:

table = Tabledata.table header: %w[x y z], body: [[:a,:b,:c]], accessors: %i[foo bar baz]
row   = table.row(1)
row.at(0)    # => :a
row.at(:foo) # => :a
row.at("x")  # => :a

119
120
121
122
123
124
125
126
127
# File 'lib/tabledata/row.rb', line 119

def at(column)
  case column
    when Symbol  then at_accessor(column)
    when String  then at_header(column)
    when Integer then at_index(column)
    when Range   then @data[column]
    else raise InvalidColumnSpecifier, "Invalid index type, expected Symbol, String or Integer, but got #{column.class}"
  end
end

#at_accessor(name) ⇒ Object

Access a single cell by its corresponding accessor. This method is faster than the generic methods Tabledata::Row#[] and #at.

Examples:

table = Tabledata.table header: %w[x y z], body: [[:a,:b,:c]], accessors: %i[foo bar baz]
row   = table.row(1)
row.at_accessor(:foo) # => :a

Raises:


150
151
152
153
154
155
# File 'lib/tabledata/row.rb', line 150

def at_accessor(name)
  index = @table.index_for_accessor(name)
  raise InvalidColumnAccessor, "No column named #{name}" unless index

  @data[index]
end

#at_header(name) ⇒ Object

Access a single cell by its corresponding header-name. This method is faster than the generic methods Tabledata::Row#[] and #at.

Examples:

table = Tabledata.table header: %w[x y z], body: [[:a,:b,:c]], accessors: %i[foo bar baz]
row   = table.row(1)
row.at_header("x")  # => :a

Raises:


136
137
138
139
140
141
# File 'lib/tabledata/row.rb', line 136

def at_header(name)
  index = @table.index_for_header(name)
  raise InvalidColumnName, "No column named #{name}" unless index

  @data[index]
end

#at_index(index) ⇒ Object

Access a single cell by its index. This method is faster than the generic methods Tabledata::Row#[] and #at.

Examples:

table = Tabledata.table header: %w[x y z], body: [[:a,:b,:c]], accessors: %i[foo bar baz]
row   = table.row(1)
row.at_index(0)    # => :a

164
165
166
# File 'lib/tabledata/row.rb', line 164

def at_index(index)
  @data.at(index)
end

#each(&block) ⇒ Object

Iterate over each cell in this row


47
48
49
# File 'lib/tabledata/row.rb', line 47

def each(&block)
  @data.each(&block)
end

#fetch(column, *default_value, &default_block) ⇒ Object

Tries to return the value of the column identified by index, corresponding accessor or header. It throws an IndexError exception if the referenced index lies outside of the array bounds. This error can be prevented by supplying a second argument, which will act as a default value.

Alternatively, if a block is given it will only be executed when an invalid index is referenced. Negative values of index count from the end of the array.

Raises:

  • (ArgumentError)

58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/tabledata/row.rb', line 58

def fetch(column, *default_value, &default_block)
  raise ArgumentError, "Must only provide at max one default value or one default block" if default_value.size > (block_given? ? 0 : 1)

  index = case column
    when Symbol then @table.index_for_accessor(column)
    when String then @table.index_for_header(column)
    when Integer then column
    else raise InvalidColumnSpecifier, "Invalid index type, expected Symbol, String or Integer, but got #{column.class}"
  end

  @data.fetch(index, *default_value, &default_block)
end

#sizeInteger Also known as: length


190
191
192
# File 'lib/tabledata/row.rb', line 190

def size
  @data.size
end

#slice(*args) ⇒ Object

Array-like access to row-data.

Examples:

table = Tabledata.table body: [[:a,:b,:c]], accessors: %i[foo bar baz]
row   = table.row(0)
row.slice(1)      # => :b
row.slice(0, 2)   # => [:a, :b]
row.slice(-2, 2)  # => [:b, :c]
row.slice(1..2)   # => [:b, :c]
row.slice(0..-2)  # => [:a, :b]
row.slice(-2..-1) # => [:b, :c]
row.slice(:foo)   # !> TypeError
row.slice("foo")  # !> TypeError

See Also:


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

def slice(*args)
  @data[*args]
end

#to_aArray Also known as: to_ary


203
204
205
# File 'lib/tabledata/row.rb', line 203

def to_a
  @data.dup
end

#to_hHash


197
198
199
# File 'lib/tabledata/row.rb', line 197

def to_h
  Hash[@table.accessor_columns.map { |accessor, index| [accessor, @data[index]] }]
end

#values_at(*columns) ⇒ Object

Access multiple values by either index, index-range, accessor or header-name.

Examples:

table = Tabledata.table header: %w[x y z], body: [[:a,:b,:c]], accessors: %i[foo bar baz]
row   = table.row(1)
row.values_at(2,1,0)    # => [:c, :b, :a]
row.values_at(:foo,'z') # => [:a, :c]
row.values_at(0..1, 2..-1) # => [:a, :b, :c]

175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/tabledata/row.rb', line 175

def values_at(*columns)
  result = []
  columns.each do |column|
    data = at(column)
    if column.is_a?(Range)
      result.concat(data) if data
    else
      result << data
    end
  end

  result
end