lib/csv_decision/columns.rb in csv_decision-0.2.0 vs lib/csv_decision/columns.rb in csv_decision-0.3.0

- old
+ new

@@ -6,10 +6,93 @@ # See LICENSE and README.md for details. module CSVDecision # Dictionary of all this table's columns - inputs, outputs etc. # @api private class Columns + # @param columns [CSVDecision::Columns] Table's columns dictionary. + # @param row [Array] Data row. + # @return [void] + def self.outs_dictionary(columns:, row:) + row.each_with_index do |cell, index| + outs_check_cell(columns: columns, cell: cell, index: index) + end + end + + # @param columns [CSVDecision::Columns] Table's columns dictionary. + # @param row [Array] Data row. + # @return [void] + def self.ins_dictionary(columns:, row:) + row.each { |cell| ins_cell_dictionary(columns: columns, cell: cell) } + end + + # @param columns [CSVDecision::Columns] Table's columns dictionary. + # @param cell [Object] Data row cell. + # @return [void] + def self.ins_cell_dictionary(columns:, cell:) + return unless cell.is_a?(Matchers::Proc) + return if cell.symbols.nil? + + add_ins_symbols(columns: columns, cell: cell) + end + + def self.outs_check_cell(columns:, cell:, index:) + return unless cell.is_a?(Matchers::Proc) + return if cell.symbols.nil? + + check_outs_symbols(columns: columns, cell: cell, index: index) + end + private_class_method :outs_check_cell + + def self.check_outs_symbols(columns:, cell:, index:) + Array(cell.symbols).each do |symbol| + check_outs_symbol(columns: columns, symbol: symbol, index: index) + end + end + private_class_method :check_outs_symbols + + def self.check_outs_symbol(columns:, symbol:, index:) + in_out = columns.dictionary[symbol] + + # If its an input column symbol then we're good. + return if ins_symbol?(columns: columns, symbol: symbol, in_out: in_out) + + # Check if this output symbol reference is on or after this cell's column + invalid_out_ref?(columns, index, in_out) + end + private_class_method :check_outs_symbol + + # If the symbol exists either as an input or does not exist then we're good. + def self.ins_symbol?(columns:, symbol:, in_out:) + return true if in_out == :in + + # It must an input symbol, as all the output symbols have been parsed. + return columns.dictionary[symbol] = :in if in_out.nil? + + false + end + private_class_method :ins_symbol? + + def self.invalid_out_ref?(columns, index, in_out) + return false if in_out < index + + that_column = if in_out == index + 'reference to itself' + else + "an out of order reference to output column '#{columns.outs[in_out].name}'" + end + raise CellValidationError, + "output column '#{columns.outs[index].name}' makes #{that_column}" + end + private_class_method :invalid_out_ref? + + def self.add_ins_symbols(columns:, cell:) + Array(cell.symbols).each do |symbol| + CSVDecision::Dictionary.add_name(columns: columns, name: symbol) + end + end + private_class_method :add_ins_symbols + # Dictionary of all table data columns. # The key of each hash is the header cell's array column index. # Note that input and output columns may be interspersed, and multiple input columns # may refer to the same input hash key symbol. # However, output columns must have unique symbols, which cannot overlap with input @@ -84,11 +167,14 @@ def initialize(table) # If a column does not have a valid header cell, then it's empty of data. # Return the stripped header row, and remove it from the data array. row = Header.strip_empty_columns(rows: table.rows) + # No header row found? + raise TableValidationError, 'table has no header row' unless row + # Build a dictionary of all valid data columns from the header row. - @dictionary = CSVDecision::Dictionary.build(header: row, dictionary: Dictionary.new) if row + @dictionary = CSVDecision::Dictionary.build(header: row, dictionary: Dictionary.new) freeze end end end \ No newline at end of file