class CSVDecision::ScanRow

Data row object indicating which columns are constants versus procs. @api private

Constants

NO_CONSTANTS

These column types cannot have constants in their data cells.

Attributes

constants[RW]

@return [Array<Integer>] Column indices for simple constants.

procs[R]

@return [Array<Integer>] Column indices for Proc objects.

Public Class Methods

new() click to toggle source
# File lib/csv_decision/scan_row.rb, line 72
def initialize
  @constants = []
  @procs = []
end
scan(column:, matchers:, cell:) click to toggle source

Scan the table cell against all matches.

@param column [Dictionary::Entry] Column dictionary entry. @param matchers [Array<Matchers::Matcher>] @param cell [String] @return [false, Matchers::Proc]

# File lib/csv_decision/scan_row.rb, line 21
def self.scan(column:, matchers:, cell:)
  return false if cell == ''

  proc = scan_matchers(column: column, matchers: matchers, cell: cell)
  return proc if proc

  # Must be a simple string constant - this is OK except for a certain column types.
  invalid_constant?(type: :constant, column: column)
end

Private Class Methods

guard_ins_matcher?(column, matcher) click to toggle source

A guard column can only use output matchers

# File lib/csv_decision/scan_row.rb, line 46
def self.guard_ins_matcher?(column, matcher)
  column.type == :guard && !matcher.outs?
end
invalid_constant?(type:, column:) click to toggle source
# File lib/csv_decision/scan_row.rb, line 59
def self.invalid_constant?(type:, column:)
  return false unless type == :constant && NO_CONSTANTS.member?(column.type)

  raise CellValidationError, "#{column.type}: column cannot contain constants"
end
scan_matchers(column:, matchers:, cell:) click to toggle source
# File lib/csv_decision/scan_row.rb, line 31
def self.scan_matchers(column:, matchers:, cell:)
  matchers.each do |matcher|
    # Guard function only accepts the same matchers as an output column.
    next if guard_ins_matcher?(column, matcher)

    proc = scan_proc(column: column, cell: cell, matcher: matcher)
    return proc if proc
  end

  # Must be a string constant
  false
end
scan_proc(column:, cell:, matcher:) click to toggle source
# File lib/csv_decision/scan_row.rb, line 51
def self.scan_proc(column:, cell:, matcher:)
  proc = matcher.matches?(cell)
  invalid_constant?(type: proc.type, column: column) if proc

  proc
end

Public Instance Methods

match?(row:, scan_cols:, hash:) click to toggle source

Match cells in the input hash against a decision table row. @param row (see ScanRow.scan_columns) @param hash (see Decision#row_scan) @return [Boolean] True for a match, false otherwise.

# File lib/csv_decision/scan_row.rb, line 108
def match?(row:, scan_cols:, hash:)
  # Check any table row cell constants first, and maybe fail fast...
  return false if @constants.any? { |col| row[col] != scan_cols[col] }

  # These table row cells are Proc objects which need evaluating and
  # must all return a truthy value.
  @procs.all? { |col| row[col].call(value: scan_cols[col], hash: hash) }
end
scan_columns(row:, columns:, matchers:) click to toggle source

Scan all the specified columns (e.g., inputs) in the given data row using the matchers array supplied.

@param row [Array<String>] Data row - still just all string constants. @param columns [Array<Columns::Entry>] Array of column dictionary entries. @param matchers [Array<Matchers::Matcher>] Array of table cell matchers. @return [Array] Data row with anything not a string constant replaced with a Proc or a

non-string constant.
# File lib/csv_decision/scan_row.rb, line 85
def scan_columns(row:, columns:, matchers:)
  columns.each_pair do |col, column|
    cell = row[col]

    # An empty input cell matches everything, and so never needs to be scanned,
    # but it cannot be indexed either.
    next column.indexed = false if cell == '' && column.ins?

    # If the column is text only then no special matchers need be used.
    next @constants << col if column.eval == false

    # Need to scan the cell against all matchers, and possibly overwrite
    # the cell contents with a Matchers::Proc value.
    row[col] = scan_cell(column: column, col: col, matchers: matchers, cell: cell)
  end

  row
end

Private Instance Methods

scan_cell(column:, col:, matchers:, cell:) click to toggle source
# File lib/csv_decision/scan_row.rb, line 119
def scan_cell(column:, col:, matchers:, cell:)
  # Scan the cell against all the matchers
  proc = ScanRow.scan(column: column, matchers: matchers, cell: cell)

  return set(proc: proc, col: col, column: column) if proc

  # Just a plain constant
  @constants << col
  cell
end
set(proc:, col:, column:) click to toggle source
# File lib/csv_decision/scan_row.rb, line 130
def set(proc:, col:, column:)
  # Unbox a constant
  if proc.type == :constant
    @constants << col
    return proc.function
  end

  @procs << col
  column.indexed = false
  proc
end