csv_decision 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -1
  3. data/README.md +112 -93
  4. data/benchmarks/rufus_decision.rb +9 -1
  5. data/csv_decision.gemspec +6 -6
  6. data/doc/CSVDecision.html +54 -90
  7. data/doc/CSVDecision/CellValidationError.html +1 -1
  8. data/doc/CSVDecision/Columns.html +104 -45
  9. data/doc/CSVDecision/Columns/Dictionary.html +40 -24
  10. data/doc/CSVDecision/Columns/Entry.html +209 -22
  11. data/doc/CSVDecision/Constant.html +9 -50
  12. data/doc/CSVDecision/Data.html +182 -47
  13. data/doc/CSVDecision/Decide.html +97 -93
  14. data/doc/CSVDecision/Decision.html +105 -294
  15. data/doc/CSVDecision/Error.html +1 -1
  16. data/doc/CSVDecision/FileError.html +1 -1
  17. data/doc/CSVDecision/Function.html +18 -7
  18. data/doc/CSVDecision/Guard.html +245 -0
  19. data/doc/CSVDecision/Header.html +58 -50
  20. data/doc/CSVDecision/Input.html +20 -12
  21. data/doc/CSVDecision/Load.html +80 -14
  22. data/doc/CSVDecision/Matchers.html +237 -279
  23. data/doc/CSVDecision/Matchers/Constant.html +280 -13
  24. data/doc/CSVDecision/Matchers/Function.html +188 -19
  25. data/doc/CSVDecision/Matchers/Guard.html +568 -0
  26. data/doc/CSVDecision/Matchers/Matcher.html +200 -14
  27. data/doc/CSVDecision/Matchers/Numeric.html +165 -13
  28. data/doc/CSVDecision/Matchers/Pattern.html +56 -163
  29. data/doc/CSVDecision/Matchers/Range.html +48 -37
  30. data/doc/CSVDecision/Matchers/Symbol.html +161 -16
  31. data/doc/CSVDecision/Numeric.html +4 -4
  32. data/doc/CSVDecision/Options.html +53 -55
  33. data/doc/CSVDecision/Parse.html +23 -13
  34. data/doc/CSVDecision/ScanRow.html +461 -73
  35. data/doc/CSVDecision/Symbol.html +4 -4
  36. data/doc/CSVDecision/Table.html +185 -79
  37. data/doc/_index.html +15 -28
  38. data/doc/class_list.html +1 -1
  39. data/doc/file.README.html +105 -82
  40. data/doc/index.html +105 -82
  41. data/doc/method_list.html +137 -113
  42. data/doc/top-level-namespace.html +1 -1
  43. data/lib/csv_decision.rb +2 -5
  44. data/lib/csv_decision/columns.rb +14 -5
  45. data/lib/csv_decision/data.rb +24 -6
  46. data/lib/csv_decision/decide.rb +18 -20
  47. data/lib/csv_decision/decision.rb +106 -42
  48. data/lib/csv_decision/header.rb +44 -23
  49. data/lib/csv_decision/input.rb +4 -2
  50. data/lib/csv_decision/load.rb +7 -3
  51. data/lib/csv_decision/matchers.rb +49 -41
  52. data/lib/csv_decision/matchers/constant.rb +62 -4
  53. data/lib/csv_decision/matchers/function.rb +33 -2
  54. data/lib/csv_decision/matchers/guard.rb +143 -0
  55. data/lib/csv_decision/matchers/numeric.rb +34 -3
  56. data/lib/csv_decision/matchers/pattern.rb +11 -4
  57. data/lib/csv_decision/matchers/range.rb +34 -26
  58. data/lib/csv_decision/matchers/symbol.rb +71 -5
  59. data/lib/csv_decision/options.rb +31 -20
  60. data/lib/csv_decision/parse.rb +28 -9
  61. data/lib/csv_decision/scan_row.rb +79 -13
  62. data/lib/csv_decision/table.rb +34 -23
  63. data/spec/csv_decision/columns_spec.rb +32 -7
  64. data/spec/csv_decision/constant_spec.rb +2 -26
  65. data/spec/csv_decision/decision_spec.rb +0 -9
  66. data/spec/csv_decision/examples_spec.rb +33 -16
  67. data/spec/csv_decision/matchers/function_spec.rb +1 -1
  68. data/spec/csv_decision/matchers/guard_spec.rb +153 -0
  69. data/spec/csv_decision/matchers/numeric_spec.rb +1 -1
  70. data/spec/csv_decision/matchers/pattern_spec.rb +2 -2
  71. data/spec/csv_decision/matchers/range_spec.rb +2 -2
  72. data/spec/csv_decision/matchers/symbol_spec.rb +1 -1
  73. data/spec/csv_decision/options_spec.rb +3 -3
  74. data/spec/csv_decision/table_spec.rb +96 -12
  75. data/spec/data/valid/benchmark_regexp.csv +10 -0
  76. data/spec/data/valid/regular_expressions.csv +11 -0
  77. metadata +14 -9
  78. data/lib/csv_decision/constant.rb +0 -54
  79. data/lib/csv_decision/function.rb +0 -32
  80. data/lib/csv_decision/numeric.rb +0 -38
  81. data/lib/csv_decision/symbol.rb +0 -73
@@ -100,7 +100,7 @@
100
100
  </div>
101
101
 
102
102
  <div id="footer">
103
- Generated on Tue Dec 26 21:20:19 2017 by
103
+ Generated on Sat Dec 30 13:04:04 2017 by
104
104
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
105
105
  0.9.12 (ruby-2.3.0).
106
106
  </div>
data/lib/csv_decision.rb CHANGED
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: true\
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'active_support/core_ext/object'
4
4
  require 'csv_decision/parse'
@@ -13,26 +13,23 @@ module CSVDecision
13
13
  File.dirname __dir__
14
14
  end
15
15
 
16
- autoload :Constant, 'csv_decision/constant'
17
16
  autoload :Data, 'csv_decision/data'
18
17
  autoload :Decide, 'csv_decision/decide'
19
18
  autoload :Decision, 'csv_decision/decision'
20
19
  autoload :Columns, 'csv_decision/columns'
21
- autoload :Function, 'csv_decision/function'
22
20
  autoload :Header, 'csv_decision/header'
23
21
  autoload :Input, 'csv_decision/input'
24
22
  autoload :Load, 'csv_decision/load'
25
23
  autoload :Matchers, 'csv_decision/matchers'
26
- autoload :Numeric, 'csv_decision/numeric'
27
24
  autoload :Options, 'csv_decision/options'
28
25
  autoload :Parse, 'csv_decision/parse'
29
26
  autoload :ScanRow, 'csv_decision/scan_row'
30
- autoload :Symbol, 'csv_decision/symbol'
31
27
  autoload :Table, 'csv_decision/table'
32
28
 
33
29
  class Matchers
34
30
  autoload :Constant, 'csv_decision/matchers/constant'
35
31
  autoload :Function, 'csv_decision/matchers/function'
32
+ autoload :Guard, 'csv_decision/matchers/guard'
36
33
  autoload :Numeric, 'csv_decision/matchers/numeric'
37
34
  autoload :Pattern, 'csv_decision/matchers/pattern'
38
35
  autoload :Range, 'csv_decision/matchers/range'
@@ -1,13 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # CSV Decision: CSV based Ruby decision tables.
4
- # Created December 2017 by Brett Vickers
4
+ # Created December 2017.
5
+ # @author Brett Vickers <brett@phillips-vickers.com>
5
6
  # See LICENSE and README.md for details.
6
7
  module CSVDecision
7
8
  # Dictionary of all this table's columns - inputs, outputs etc.
9
+ # @api private
8
10
  class Columns
9
11
  # Value object to hold column dictionary entries.
10
- Entry = Struct.new(:name, :text_only)
12
+ Entry = Struct.new(:name, :eval, :type) do
13
+ def ins?
14
+ %i[in guard].member?(type) ? true : false
15
+ end
16
+ end
11
17
 
12
18
  # TODO: Value object used for any columns with defaults
13
19
  # Default = Struct.new(:name, :function, :default_if)
@@ -38,15 +44,18 @@ module CSVDecision
38
44
  end
39
45
  end
40
46
 
41
- # Dictionary of all data columns
47
+ # Dictionary of all data columns.
48
+ # @return [Columns::Dictionary]
42
49
  attr_reader :dictionary
43
50
 
44
- # Input columns
51
+ # Input columns hash keyed by column index.
52
+ # @return [Hash{Index=>Entry}]
45
53
  def ins
46
54
  @dictionary.ins
47
55
  end
48
56
 
49
- # Output columns
57
+ # Output columns hash keyed by column index.
58
+ # @return [Hash{Index=>Entry}]
50
59
  def outs
51
60
  @dictionary.outs
52
61
  end
@@ -3,25 +3,43 @@
3
3
  require 'csv'
4
4
 
5
5
  # CSV Decision: CSV based Ruby decision tables.
6
- # Created December 2017 by Brett Vickers
7
- # See LICENSE and README.md for details.
6
+ # Created December 2017.
7
+ # @author Brett Vickers <brett@phillips-vickers.com>
8
+ # See LICENSE and README.md for details..
8
9
  module CSVDecision
10
+ # All cells starting with this character are comments, and treated as a blank cell.
9
11
  COMMENT_CHARACTER = '#'
12
+ private_constant :COMMENT_CHARACTER
10
13
 
11
- # Methods to load data from a file, CSV string or array of arrays
14
+ # Methods to load data from a file, CSV string or an array of arrays.
15
+ # @api private
12
16
  module Data
17
+ # Options passed to CSV.parse and CSV.read.
13
18
  CSV_OPTIONS = { encoding: 'UTF-8', skip_blanks: true }.freeze
19
+ private_constant :CSV_OPTIONS
14
20
 
15
21
  # Parse the input data which may either be a file path name, CSV string or
16
- # array of arrays. Strips out empty columns/rows and comment cells
22
+ # array of arrays. Strips out empty columns/rows and comment cells.
23
+ #
24
+ # @param data (see Parse.parse)
25
+ # @return [Array<Array<String>>] Data array stripped of empty rows.
17
26
  def self.to_array(data:)
18
27
  strip_rows(data: data_array(data))
19
28
  end
20
29
 
21
- def self.input_file?(input)
22
- input.is_a?(Pathname) || input.is_a?(File)
30
+ # If the input is a file name return true, otherwise false.
31
+ #
32
+ # @param data (see Parse.parse)
33
+ # @return [Boolean] Set to true if the input data is passed as a File or Pathname.
34
+ def self.input_file?(data)
35
+ data.is_a?(Pathname) || data.is_a?(File)
23
36
  end
24
37
 
38
+ # Strip the empty columns from the input data rows.
39
+ #
40
+ # @param data (see Parse.parse)
41
+ # @param empty_columns [Array<Index>]
42
+ # @return [Array<Array<String>>] Data array stripped of empty columns.
25
43
  def self.strip_columns(data:, empty_columns:)
26
44
  # Adjust column indices as we delete columns the rest shift to the left by 1
27
45
  empty_columns.map!.with_index { |col, index| col - index }
@@ -1,11 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # CSV Decision: CSV based Ruby decision tables.
4
- # Created December 2017 by Brett Vickers
4
+ # Created December 2017.
5
+ # @author Brett Vickers <brett@phillips-vickers.com>
5
6
  # See LICENSE and README.md for details.
6
7
  module CSVDecision
7
8
  # Main module for searching the decision table looking for one or more matches
9
+ # @api private
8
10
  module Decide
11
+ # Match the table row against the input hash.
12
+ #
13
+ # @param row [Array] Table row.
14
+ # @param input [Hash{Symbol=>Object}] Input hash data structure.
15
+ # @param scan_row [ScanRow]
16
+ # @return [Boolean] Returns true if a match, false otherwise.
17
+ def self.matches?(row:, input:, scan_row:)
18
+ match = scan_row.match_constants?(row: row, scan_cols: input[:scan_cols])
19
+ return false unless match
20
+
21
+ return true if scan_row.procs.empty?
22
+
23
+ scan_row.match_procs?(row: row, input: input)
24
+ end
25
+
9
26
  # Main method for making decisions.
10
27
  #
11
28
  # @param table [CSVDecision::Table] Decision table.
@@ -24,24 +41,5 @@ module CSVDecision
24
41
  # table_scan(table: table, input: parsed_input, decision: decision)
25
42
  decision.scan(table: table, input: parsed_input)
26
43
  end
27
-
28
- def self.matches?(row:, input:, scan_row:)
29
- match = scan_row.match_constants?(row: row, scan_cols: input[:scan_cols])
30
- return false unless match
31
-
32
- return true if scan_row.procs.empty?
33
-
34
- scan_row.match_procs?(row: row, input: input)
35
- end
36
-
37
- def self.eval_matcher(proc:, value:, hash:)
38
- function = proc.function
39
-
40
- # A symbol guard expression just needs to be passed the input hash
41
- return function[hash] if proc.type == :expression
42
-
43
- # All other procs can take one or two args
44
- function.arity == 1 ? function[value] : function[value, hash]
45
- end
46
44
  end
47
45
  end
@@ -1,80 +1,90 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # CSV Decision: CSV based Ruby decision tables.
4
- # Created December 2017 by Brett Vickers
4
+ # Created December 2017.
5
+ # @author Brett Vickers <brett@phillips-vickers.com>
5
6
  # See LICENSE and README.md for details.
6
7
  module CSVDecision
7
- # Accumulate the matching row(s) and calculate the final result
8
+ # Accumulate the matching row(s) and calculate the final result.
9
+ # @api private
8
10
  class Decision
11
+ # @param table [CSVDecision::Table] Decision table being processed.
12
+ # @param input [Hash{Symbol=>Object}] Input hash data structure.
9
13
  def initialize(table:, input:)
10
14
  @result = {}
11
15
 
12
16
  # Relevant table attributes
13
17
  @first_match = table.options[:first_match]
14
18
  @outs = table.columns.outs
15
-
16
- # TODO: Planned feature
17
- # @outs_functions = table.outs_functions
19
+ @outs_functions = table.outs_functions
18
20
 
19
21
  # Partial result always includes the input hash for calculating output functions
20
22
  @partial_result = input[:hash].dup if @outs_functions
21
23
 
22
- @row_picked = nil
23
- return if @first_match
24
-
25
- # Extra attributes for the accumulate option
26
24
  @rows_picked = []
27
- @multi_result = nil
28
25
  end
29
26
 
30
- # Is the result set empty? That is, nothing matched?
31
- def empty?
32
- return @row_picked.nil? if @first_match
33
- @rows_picked.empty?
34
- end
27
+ # Scan the decision table up against the input hash.
28
+ #
29
+ # @param table [CSVDecision::Table] Decision table being processed.
30
+ # @param input (see #initialize)
31
+ # @return [self] Decision object built so far.
32
+ def scan(table:, input:)
33
+ table.each do |row, index|
34
+ return result if row_scan(input: input, row: row, scan_row: table.scan_rows[index])
35
+ end
35
36
 
36
- def exist?
37
- !empty?
37
+ result
38
38
  end
39
39
 
40
- def result
41
- return {} if empty?
42
- return final_result unless @outs_functions
40
+ private
43
41
 
44
- nil
42
+ # Calculate the final result.
43
+ # @return [nil, Hash{Symbol=>Object}] Final result hash if found, otherwise nil for no result.
44
+ def result
45
+ return {} if @rows_picked.blank?
46
+ @first_match ? final_result : accumulated_result
45
47
  end
46
48
 
47
- def scan(table:, input:)
48
- scan_rows = table.scan_rows
49
-
50
- table.each do |row, index|
51
- done = row_scan(input: input, row: row, scan_row: scan_rows[index])
52
-
53
- return self if done
54
- end
55
-
56
- self
49
+ def row_scan(input:, row:, scan_row:)
50
+ add(row) if Decide.matches?(row: row, input: input, scan_row: scan_row)
57
51
  end
58
52
 
53
+ # Add a matched row to the decision object being built.
54
+ #
55
+ # @param row [Array]
59
56
  def add(row)
60
57
  return add_first_match(row) if @first_match
61
58
 
62
59
  # Accumulate output rows
63
60
  @rows_picked << row
64
- @outs.each_pair do |col, column|
65
- accumulate_outs(column_name: column.name, cell: row[col])
66
- end
61
+ @outs.each_pair { |col, column| accumulate_outs(column_name: column.name, cell: row[col]) }
67
62
 
68
63
  # Not done
69
64
  false
70
65
  end
71
66
 
72
- private
67
+ def accumulated_result
68
+ return final_result unless @outs_functions
69
+ return eval_outs(@rows_picked.first) unless @multi_result
73
70
 
74
- def accumulate_outs(column_name:, cell:)
75
- current = @result[column_name]
71
+ multi_row_result
72
+ end
73
+
74
+ def multi_row_result
75
+ # Scan each output column that contains functions
76
+ @outs.each_pair do |col, column|
77
+ # Does this column have any functions defined?
78
+ next unless column.eval
79
+
80
+ eval_column_procs(col, column)
81
+ end
76
82
 
77
- case current
83
+ final_result
84
+ end
85
+
86
+ def accumulate_outs(column_name:, cell:)
87
+ case (current = @result[column_name])
78
88
  when nil
79
89
  @result[column_name] = cell
80
90
 
@@ -87,10 +97,30 @@ module CSVDecision
87
97
  end
88
98
  end
89
99
 
90
- def row_scan(input:, row:, scan_row:)
91
- return unless Decide.matches?(row: row, input: input, scan_row: scan_row)
100
+ def eval_column_procs(col, column)
101
+ @rows_picked.each_with_index do |row, index|
102
+ proc = row[col]
103
+ next unless proc.is_a?(Matchers::Proc)
104
+
105
+ # Evaluate the proc and update the result
106
+ eval_cell_proc(proc: proc, column_name: column.name, index: index)
107
+ end
108
+ end
92
109
 
93
- add(row)
110
+ # Update the partial result calculated so far and call the function
111
+ def eval_cell_proc(proc:, column_name:, index:)
112
+ value = proc.function[partial_result(index)]
113
+ @multi_result ? @result[column_name][index] = value : @result[column_name] = value
114
+ end
115
+
116
+ def partial_result(index)
117
+ @result.each_pair do |column_name, value|
118
+ # Delete this column from the partial result in case there is data from a prior result row
119
+ next @partial_result.delete(column_name) if value[index].is_a?(Matchers::Proc)
120
+ @partial_result[column_name] = value[index]
121
+ end
122
+
123
+ @partial_result
94
124
  end
95
125
 
96
126
  def final_result
@@ -98,10 +128,44 @@ module CSVDecision
98
128
  end
99
129
 
100
130
  def add_first_match(row)
101
- @row_picked = row
131
+ @rows_picked = row
132
+
133
+ return eval_outs(row) if @outs_functions
102
134
 
103
135
  # Common case is just copying output column values to the final result
104
136
  @outs.each_pair { |col, column| @result[column.name] = row[col] }
105
137
  end
138
+
139
+ def eval_outs(row)
140
+ # Set the constants first, in case the functions refer to them
141
+ eval_outs_constants(row)
142
+
143
+ # Then evaluate the functions, left to right
144
+ eval_outs_procs(row)
145
+
146
+ final_result
147
+ end
148
+
149
+ def eval_outs_constants(row)
150
+ @outs.each_pair do |col, column|
151
+ value = row[col]
152
+ next if value.is_a?(Matchers::Proc)
153
+
154
+ @partial_result[column.name] = value
155
+ @result[column.name] = value
156
+ end
157
+ end
158
+
159
+ def eval_outs_procs(row)
160
+ @outs.each_pair do |col, column|
161
+ proc = row[col]
162
+ next unless proc.is_a?(Matchers::Proc)
163
+
164
+ value = proc.function[@partial_result]
165
+
166
+ @partial_result[column.name] = value
167
+ @result[column.name] = value
168
+ end
169
+ end
106
170
  end
107
171
  end
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # CSV Decision: CSV based Ruby decision tables.
4
- # Created December 2017 by Brett Vickers
4
+ # Created December 2017.
5
+ # @author Brett Vickers <brett@phillips-vickers.com>
5
6
  # See LICENSE and README.md for details.
6
7
  module CSVDecision
7
8
  # Parse the CSV file's header row. These methods are only required at table load time.
9
+ # @api private
8
10
  module Header
9
11
  # TODO: implement all column types
10
12
  # COLUMN_TYPE = %r{
@@ -12,22 +14,26 @@ module CSVDecision
12
14
  # \s*:\s*(?<name>\S?.*)\z
13
15
  # }xi
14
16
 
15
- # Column types recognise din the header row.
17
+ # Column types recognised in the header row.
16
18
  COLUMN_TYPE = %r{
17
- \A(?<type>in|out|in/text|out/text)
19
+ \A(?<type>in|out|in/text|out/text|guard)
18
20
  \s*:\s*(?<name>\S?.*)\z
19
21
  }xi
20
22
 
21
- # These column types do not need a name
22
- # TODO: implement anonymous column types
23
+ # TODO: implement all anonymous column types
23
24
  # COLUMN_TYPE_ANONYMOUS = Set.new(%i[path if guard]).freeze
25
+ # These column types do not need a name
26
+ COLUMN_TYPE_ANONYMOUS = Set.new(%i[guard]).freeze
27
+ private_constant :COLUMN_TYPE_ANONYMOUS
24
28
 
25
29
  # Regular expression string for a column name.
26
- # More lenient than a Ruby method name - note any spaces will have been replaced with underscores.
30
+ # More lenient than a Ruby method name - note any spaces will have been replaced with
31
+ # underscores.
27
32
  COLUMN_NAME = "\\w[\\w:/!?]*"
28
33
 
29
34
  # Column name regular expression.
30
35
  COLUMN_NAME_RE = Matchers.regexp(COLUMN_NAME)
36
+ private_constant :COLUMN_NAME_RE
31
37
 
32
38
  # Check if the given row contains a recognisable header cell.
33
39
  #
@@ -61,8 +67,26 @@ module CSVDecision
61
67
  dictionary = parse_cell(cell: cell, index: index, dictionary: dictionary)
62
68
  end
63
69
 
70
+ validate(dictionary: dictionary)
71
+ end
72
+
73
+ def self.validate(dictionary:)
74
+ dictionary.outs.each_value do |column|
75
+ next unless input_column?(dictionary: dictionary, column_name: column.name)
76
+
77
+ raise CellValidationError, "output column name '#{column.name}' is also an input column"
78
+ end
79
+
64
80
  dictionary
65
81
  end
82
+ private_class_method :validate
83
+
84
+ def self.input_column?(dictionary:, column_name:)
85
+ dictionary.ins.each_value { |column| return true if column_name == column.name }
86
+
87
+ false
88
+ end
89
+ private_class_method :input_column?
66
90
 
67
91
  def self.validate_header_column(cell:)
68
92
  match = COLUMN_TYPE.match(cell)
@@ -73,8 +97,7 @@ module CSVDecision
73
97
 
74
98
  [column_type, column_name]
75
99
  rescue CellValidationError => exp
76
- raise CellValidationError,
77
- "header column '#{cell}' is not valid as the #{exp.message}"
100
+ raise CellValidationError, "header column '#{cell}' is not valid as the #{exp.message}"
78
101
  end
79
102
  private_class_method :validate_header_column
80
103
 
@@ -90,8 +113,7 @@ module CSVDecision
90
113
  def self.column_name(type:, name:)
91
114
  return format_column_name(name) if name.present?
92
115
 
93
- # TODO: implement anonymous column types
94
- # return if COLUMN_TYPE_ANONYMOUS.member?(type)
116
+ return if COLUMN_TYPE_ANONYMOUS.member?(type)
95
117
 
96
118
  raise CellValidationError, 'column name is missing'
97
119
  end
@@ -107,22 +129,21 @@ module CSVDecision
107
129
  private_class_method :format_column_name
108
130
 
109
131
  # Returns the normalized column type, along with an indication if
110
- # the column is text only
111
- def self.column_type(type)
132
+ # the column requires evaluation
133
+ def self.column_type(column_name, type)
112
134
  case type
113
135
  when :'in/text'
114
- [:in, true]
136
+ Columns::Entry.new(column_name, false, :in)
115
137
 
116
- # TODO: planned feature
117
- # when :guard
118
- # [:in, false]
138
+ when :guard
139
+ Columns::Entry.new(column_name, true, :guard)
119
140
 
120
141
  when :'out/text'
121
- [:out, true]
142
+ Columns::Entry.new(column_name, false, :out)
122
143
 
123
- # Column may turn out to be text-only, or not
144
+ # Column may turn out to be constants only, or not
124
145
  else
125
- [type, nil]
146
+ Columns::Entry.new(column_name, nil, type.to_sym)
126
147
  end
127
148
  end
128
149
  private_class_method :column_type
@@ -130,11 +151,11 @@ module CSVDecision
130
151
  def self.parse_cell(cell:, index:, dictionary:)
131
152
  column_type, column_name = validate_header_column(cell: cell)
132
153
 
133
- type, text_only = column_type(column_type)
154
+ entry = column_type(column_name, column_type)
134
155
 
135
156
  dictionary_entry(dictionary: dictionary,
136
- type: type,
137
- entry: Columns::Entry.new(column_name, text_only),
157
+ type: entry.type,
158
+ entry: entry,
138
159
  index: index)
139
160
  end
140
161
  private_class_method :parse_cell
@@ -150,7 +171,7 @@ module CSVDecision
150
171
  # # Treat set: as an in: column
151
172
  # dictionary.ins[index] = entry
152
173
 
153
- when :in
174
+ when :in, :guard
154
175
  dictionary.ins[index] = entry
155
176
 
156
177
  when :out