class Cucumber::MultilineArgument::DataTable


This will store [['a', 'b'], ['c', 'd']] in the data variable.
end
data = table.raw
Given /I have:/ do |table|
And a matching StepDefinition:
| c | d |
| a | b |
Given I have:
For example:
in different ways.
table parsed from a feature file and lets you access and manipulate the data
will receive it as an instance of Table. A Table object holds the data of a
Step Definitions that match a plain text Step with a multiline argument table

def self.default_arg_name

def self.default_arg_name
  'table'
end

def append_to(array)

def append_to(array)
  array << self
end

def build_hashes

def build_hashes
  convert_headers!
  convert_columns!
  cells_rows[1..].map(&:to_hash)
end

def cells_rows

def cells_rows
  @rows ||= cell_matrix.map do |cell_row|
    Cells.new(self, cell_row)
  end
end

def cells_to_hash(cells)

def cells_to_hash(cells)
  hash = Hash.new do |hash_inner, key|
    hash_inner[key.to_s] if key.is_a?(Symbol)
  end
  column_names.each_with_index do |column_name, column_index|
    hash[column_name] = cells.value(column_index)
  end
  hash
end

def clear_cache!

def clear_cache!
  @hashes = @rows_hash = @column_names = @rows = @columns = nil
end

def col_width(col)

def col_width(col)
  columns[col].__send__(:width)
end

def column_names

def column_names
  @column_names ||= cell_matrix[0].map(&:value)
end

def columns

def columns
  @columns ||= cell_matrix.transpose.map do |cell_row|
    Cells.new(self, cell_row)
  end
end

def convert_columns!

def convert_columns!
  @conversion_procs.each do |column_name, conversion_proc|
    verify_column(column_name) if conversion_proc[:strict]
  end
  cell_matrix.transpose.each do |col|
    column_name = col[0].value
    conversion_proc = @conversion_procs[column_name][:proc]
    col[1..].each do |cell|
      cell.value = conversion_proc.call(cell.value)
    end
  end
end

def convert_headers!

def convert_headers!
  header_cells = cell_matrix[0]
  if @header_conversion_proc
    header_values = header_cells.map(&:value) - @header_mappings.keys
    @header_mappings = @header_mappings.merge(Hash[*header_values.zip(header_values.map(&@header_conversion_proc)).flatten])
  end
  @header_mappings.each_pair do |pre, post|
    mapped_cells = header_cells.select { |cell| pre.is_a?(Regexp) ? cell.value.match?(pre) : cell.value == pre }
    raise "No headers matched #{pre.inspect}" if mapped_cells.empty?
    raise "#{mapped_cells.length} headers matched #{pre.inspect}: #{mapped_cells.map(&:value).inspect}" if mapped_cells.length > 1
    mapped_cells[0].value = post
    @conversion_procs[post] = @conversion_procs.delete(pre) if @conversion_procs.key?(pre)
  end
end

def create_cell_matrix(ast_table)

def create_cell_matrix(ast_table)
  ast_table.raw.map do |raw_row|
    line = begin
      raw_row.line
    rescue StandardError
      -1
    end
    raw_row.map do |raw_cell|
      Cell.new(raw_cell, self, line)
    end
  end
end

def describe_to(visitor, *args)

def describe_to(visitor, *args)
  visitor.legacy_table(self, *args)
end

def diff!(other_table, options = {})


a Table argument, if you want to compare that table to some actual values.
Calling this method is particularly useful in Then steps that take

an Array of Hash (similar to the structure returned by #hashes).
The +other_table+ argument can be another Table, an Array of Array or

* misplaced_col : Raise on misplaced columns (defaults to false)
* surplus_col : Raise on surplus columns (defaults to false)
* missing_col : Raise on missing columns (defaults to true)
* surplus_row : Raise on surplus rows (defaults to true)
* missing_row : Raise on missing rows (defaults to true)

+options+ to true or false:
Whether to raise or not raise can be changed by setting values in
error is not raised for misplaced (out of sequence) columns.
surplus rows. An error is not raised for surplus columns. An
A Different error is raised if there are missing rows or columns, or

#diff!. You can use #map_column on either of the tables.
objects in their cells, you may want to use #map_column before calling
Since all tables that are passed to StepDefinitions always have String

where the difference actually is.
representation and preceded with (i) - to make it easier to identify
boolean true and the string "true") are converted to their Object#inspect
Cells that are different, but look identical (for example the

and displayed so that it's easy to read the differences.
surplus and missing cells are recognised by formatters

rows that are present in self, these are marked as missing.
surplus. Likewise, if +other_table+ lacks columns and/or
relevant positions, marking the cells in those rows/columns as
and/or rows that are not in self, new columns/rows are added at the
Compares +other_table+ to self. If +other_table+ contains columns
def diff!(other_table, options = {})
  other_table = ensure_table(other_table)
  other_table.convert_headers!
  other_table.convert_columns!
  convert_headers!
  convert_columns!
  DiffMatrices.new(cell_matrix, other_table.cell_matrix, options).call
end

def each_cells_row(&proc)

def each_cells_row(&proc)
  cells_rows.each(&proc)
end

def ensure_table(table_or_array)

def ensure_table(table_or_array)
  return table_or_array if table_or_array.instance_of?(DataTable)
  DataTable.from(table_or_array)
end

def from(data)

def from(data)
  case data
  when Array
    from_array(data)
  when String
    parse(data)
  else
    raise ArgumentError, 'expected data to be a String or an Array.'
  end
end

def from_array(data)

def from_array(data)
  new Core::Test::DataTable.new(data)
end

def hashes


Use #map_column to specify how values in a column are converted.

[{'a' => '2', 'b' => '3', 'sum' => '5'}, {'a' => '7', 'b' => '9', 'sum' => '16'}]

Gets converted into the following:

| 7 | 9 | 16 |
| 2 | 3 | 5 |
| a | b | sum |

the following plain text:
Hash are the headers in the table. For example, a Table built from
Converts this table into an Array of Hash where the keys of each
def hashes
  @hashes ||= build_hashes
end

def header_cell(col)

def header_cell(col)
  cells_rows[0][col]
end

def headers

def headers
  raw.first
end

def index(cells)

def index(cells)
  cells_rows.index(cells)
end

def initialize(data, conversion_procs = NULL_CONVERSIONS.dup, header_mappings = {}, header_conversion_proc = nil)

Parameters:
  • header_conversion_proc (Proc) -- see map_headers
  • header_mappings (Hash) -- see map_headers
  • conversion_procs (Hash) -- see map_column
  • data (Core::Test::DataTable) -- the data for the table
def initialize(data, conversion_procs = NULL_CONVERSIONS.dup, header_mappings = {}, header_conversion_proc = nil)
  raise ArgumentError, 'data must be a Core::Test::DataTable' unless data.is_a? Core::Test::DataTable
  ast_table = data
  # Verify that it's square
  ast_table.transpose
  @cell_matrix = create_cell_matrix(ast_table)
  @conversion_procs = conversion_procs
  @header_mappings = header_mappings
  @header_conversion_proc = header_conversion_proc
  @ast_table = ast_table
end

def location

def location
  @ast_table.location
end

def map_column(column_name, strict: true, &conversion_proc)


end
end
# post['amount'] is a Fixnum, rather than a String
posts_table.hashes.each do |post|
posts_table = posts_table.map_column('amount') { |a| a.to_i }
Given /^an expense report for (.*) with the following posts:$/ do |table|

is false, no error will be raised. Example:
true, an error will be raised if the column named +column_name+ is not found. If +strict+
and +conversion_proc+ performs the conversion for each cell in that column. If +strict+ is
Change how #hashes converts column values. The +column_name+ argument identifies the column

Returns a new Table with an additional column mapping.
def map_column(column_name, strict: true, &conversion_proc)
  conversion_procs = @conversion_procs.dup
  conversion_procs[column_name.to_s] = { strict: strict, proc: conversion_proc }
  self.class.new(Core::Test::DataTable.new(raw), conversion_procs, @header_mappings.dup, @header_conversion_proc)
end

def map_headers(mappings = {}, &block)


# => ['phone number', 'ADDRESS']
table.hashes.keys
table.map_headers('Address' => 'ADDRESS') { |header| header.downcase }

When a block is passed in along with a hash then the mappings in the hash take precendence:

# => ['phone number', 'address']
table.hashes.keys
table.map_headers { |header| header.downcase }

You may also pass in a block if you wish to convert all of the headers:

# => [{:phone => '123456', :address => 'xyz'}, {:phone => '345678', :address => 'abc'}]
table.hashes
table.map_headers(/phone( number)?/i => :phone, 'Address' => :address)

with both Regexp and String:
A StepDefinition receiving this table can then map the columns

| 345678 | abc |
| 123456 | xyz |
| Phone Number | Address |

Example:

desired names for the columns.
column headings in the table. The values of +mappings+ are
(anything that responds to #=== will work) that may match
keys of +mappings+ are Strings or regular expressions
prettier and more flexible header names in the features. The
This makes it possible to use
Returns a new Table where the headers are redefined.
def map_headers(mappings = {}, &block)
  self.class.new(Core::Test::DataTable.new(raw), @conversion_procs.dup, mappings, block)
end

def match(pattern)

Note: must use 'table:' prefix on match

table.match(/table:column_1_name,column_2_name/) #=> non-nil

| x | y |
| column_1_name | column_2_name |
Example:

This is used especially for argument transforms.
Matches +pattern+ against the header row of the table.
def match(pattern)
  header_to_match = "table:#{headers.join(',')}"
  pattern.match(header_to_match)
end

def parse(text)

def parse(text)
  builder = Builder.new
  parser = Cucumber::Gherkin::DataTableParser.new(builder)
  parser.parse(text)
  from_array(builder.rows)
end

def raw


[['a', 'b'], ['c', 'd']]

gets converted into the following:

| c | d |
| a | b |

the following plain text:
Gets the raw data of this table. For example, a Table built from
def raw
  cell_matrix.map do |row|
    row.map(&:value)
  end
end

def rows

def rows
  hashes.map do |hash|
    hash.values_at(*headers)
  end
end

def rows_hash


The table must be exactly two columns wide

{'a' => '2', 'b' => '3'}

Gets converted into the following:

| b | 3 |
| a | 2 |

used as keys and the second column is used as values
Converts this table into a Hash where the first column is
def rows_hash
  return @rows_hash if @rows_hash
  verify_table_width(2)
  @rows_hash = transpose.hashes[0]
end

def symbolic_hashes


[{:foo => '2', :bar => '3', :foo_bar => '5'}, {:foo => '7', :bar => '9', :foo_bar => '16'}]

Gets converted into the following:

| 7 | 9 | 16 |
| 2 | 3 | 5 |
| foo | Bar | Foo Bar |

For example, a Table built from the following plain text:
Converts this table into an Array of Hashes where the keys are symbols.
def symbolic_hashes
  @symbolic_hashes ||=
    hashes.map do |string_hash|
      string_hash.transform_keys { |a| symbolize_key(a) }
    end
end

def symbolize_key(key)

def symbolize_key(key)
  key.downcase.tr(' ', '_').to_sym
end

def text?(text)

Nothing else in this repo calls it.
TODO: remove the below function if it's not actually being used.
def text?(text)
  Cucumber.deprecate(
    'This method is no longer supported for checking text',
    '#text?',
    '11.0.0'
  )
  raw.flatten.compact.detect { |cell_value| cell_value.index(text) }
end

def to_hash

def to_hash
  cells_rows.map { |cells| cells.map(&:value) }
end

def to_json(*args)

def to_json(*args)
  raw.to_json(*args)
end

def to_s(options = {})

def to_s(options = {})
  indentation = options.key?(:indent) ? options[:indent] : 2
  prefixes = options.key?(:prefixes) ? options[:prefixes] : TO_S_PREFIXES
  DataTablePrinter.new(self, indentation, prefixes).to_s
end

def to_step_definition_arg

def to_step_definition_arg
  dup
end

def transpose


| 4 | 2 |
| 7 | 9 |
| a | b |

Gets converted into the following:

| b | 9 | 2 |
| a | 7 | 4 |

Returns a new, transposed table. Example:
def transpose
  self.class.new(Core::Test::DataTable.new(raw.transpose), @conversion_procs.dup, @header_mappings.dup, @header_conversion_proc)
end

def verify_column(column_name)

def verify_column(column_name)
  raise %(The column named "#{column_name}" does not exist) unless raw[0].include?(column_name)
end

def verify_table_width(width)

def verify_table_width(width)
  raise %(The table must have exactly #{width} columns) unless raw[0].size == width
end