csv_decision 0.0.9 → 0.1.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +1 -1
- data/benchmarks/rufus_decision.rb +3 -5
- data/csv_decision.gemspec +1 -3
- data/doc/CSVDecision/CellValidationError.html +1 -1
- data/doc/CSVDecision/Columns/Dictionary.html +112 -18
- data/doc/CSVDecision/Columns.html +112 -81
- data/doc/CSVDecision/Data.html +1 -1
- data/doc/CSVDecision/Decide.html +1 -1
- data/doc/CSVDecision/Decision.html +1 -1
- data/doc/CSVDecision/Dictionary/Entry.html +1 -1
- data/doc/CSVDecision/Dictionary.html +152 -7
- data/doc/CSVDecision/Error.html +1 -1
- data/doc/CSVDecision/FileError.html +1 -1
- data/doc/CSVDecision/Header.html +1 -1
- data/doc/CSVDecision/Input.html +5 -11
- data/doc/CSVDecision/Load.html +1 -1
- data/doc/CSVDecision/Matchers/Constant.html +1 -1
- data/doc/CSVDecision/Matchers/Function.html +1 -1
- data/doc/CSVDecision/Matchers/Guard.html +23 -23
- data/doc/CSVDecision/Matchers/Matcher.html +13 -13
- data/doc/CSVDecision/Matchers/Numeric.html +3 -3
- data/doc/CSVDecision/Matchers/Pattern.html +9 -11
- data/doc/CSVDecision/Matchers/Proc.html +571 -0
- data/doc/CSVDecision/Matchers/Range.html +1 -1
- data/doc/CSVDecision/Matchers/Symbol.html +1 -1
- data/doc/CSVDecision/Matchers.html +63 -83
- data/doc/CSVDecision/Options.html +1 -1
- data/doc/CSVDecision/Parse.html +115 -8
- data/doc/CSVDecision/Result.html +55 -37
- data/doc/CSVDecision/ScanRow.html +3 -3
- data/doc/CSVDecision/Table.html +5 -6
- data/doc/CSVDecision.html +5 -5
- data/doc/_index.html +12 -5
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +2 -2
- data/doc/index.html +2 -2
- data/doc/method_list.html +110 -46
- data/doc/top-level-namespace.html +1 -1
- data/lib/csv_decision/columns.rb +14 -9
- data/lib/csv_decision/dictionary.rb +46 -21
- data/lib/csv_decision/input.rb +13 -6
- data/lib/csv_decision/matchers/constant.rb +1 -1
- data/lib/csv_decision/matchers/guard.rb +7 -10
- data/lib/csv_decision/matchers/numeric.rb +2 -2
- data/lib/csv_decision/matchers/pattern.rb +1 -2
- data/lib/csv_decision/matchers/range.rb +1 -1
- data/lib/csv_decision/matchers/symbol.rb +1 -1
- data/lib/csv_decision/matchers.rb +38 -4
- data/lib/csv_decision/parse.rb +100 -5
- data/lib/csv_decision/result.rb +23 -7
- data/lib/csv_decision/table.rb +2 -2
- data/spec/csv_decision/columns_spec.rb +89 -0
- data/spec/csv_decision/examples_spec.rb +23 -1
- data/spec/csv_decision/input_spec.rb +0 -2
- data/spec/csv_decision/table_spec.rb +1 -1
- metadata +3 -30
@@ -74,8 +74,7 @@ module CSVDecision
|
|
74
74
|
# No need for a regular expression if we have simple string inequality
|
75
75
|
pattern = comparator == '!=' ? value : Matchers.regexp(value)
|
76
76
|
|
77
|
-
Proc.
|
78
|
-
function: PATTERN_LAMBDAS[comparator].curry[pattern].freeze)
|
77
|
+
Proc.new(type: :proc, function: PATTERN_LAMBDAS[comparator].curry[pattern].freeze)
|
79
78
|
end
|
80
79
|
|
81
80
|
# @param options [Hash{Symbol=>Object}] Used to determine the value of regexp_implicit:.
|
@@ -81,7 +81,7 @@ module CSVDecision
|
|
81
81
|
negate, range = range(match, coerce: coerce)
|
82
82
|
method = coerce ? :numeric_range : :alnum_range
|
83
83
|
function = Range.send(method, negate, range).freeze
|
84
|
-
Proc.
|
84
|
+
Proc.new(type: :proc, function: function)
|
85
85
|
end
|
86
86
|
private_class_method :range_proc
|
87
87
|
|
@@ -60,7 +60,7 @@ module CSVDecision
|
|
60
60
|
# E.g., > :col, we get comparator: >, args: col
|
61
61
|
def self.comparison(comparator:, name:)
|
62
62
|
function = COMPARE[comparator]
|
63
|
-
Matchers::Proc.
|
63
|
+
Matchers::Proc.new(type: :symbol, function: function[name], symbols: name)
|
64
64
|
end
|
65
65
|
private_class_method :comparison
|
66
66
|
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'values'
|
4
|
-
|
5
3
|
# CSV Decision: CSV based Ruby decision tables.
|
6
4
|
# Created December 2017.
|
7
5
|
# @author Brett Vickers.
|
@@ -10,9 +8,45 @@ module CSVDecision
|
|
10
8
|
# Match table data cells against a valid decision table expression or a simple constant.
|
11
9
|
# @api private
|
12
10
|
class Matchers
|
13
|
-
#
|
11
|
+
# Composite object for a data cell proc. Note that we do not need it to be comparable.
|
12
|
+
# Implemented as an immutable array of 2 or 3 entries for memory compactness and speed.
|
14
13
|
# @api private
|
15
|
-
Proc
|
14
|
+
class Proc < Array
|
15
|
+
# @param type [Symbol] Type of the function value - e.g., :constant or :guard.
|
16
|
+
# @param function [Object] Either a lambda function,
|
17
|
+
# or some kind of constant such as an Integer.
|
18
|
+
# @param symbols [nil, Symbol, Array<Symbol>] The symbol or list of symbols
|
19
|
+
# that the function uses to reference input hash keys (which are always symbolized).
|
20
|
+
def initialize(type:, function:, symbols: nil)
|
21
|
+
super()
|
22
|
+
|
23
|
+
self << type
|
24
|
+
|
25
|
+
# Function values should always be frozen
|
26
|
+
self << function.freeze
|
27
|
+
|
28
|
+
# Some function values, such as constants or 0-arity functions, do not reference symbols.
|
29
|
+
self << symbols if symbols
|
30
|
+
|
31
|
+
freeze
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Symbol] Type of the function value - e.g., :constant or :guard.
|
35
|
+
def type
|
36
|
+
fetch(0)
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Object] Either a lambda function, or some kind of constant such as an Integer.
|
40
|
+
def function
|
41
|
+
fetch(1)
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [nil, Symbol, Array<Symbol>] The symbol or list of symbols
|
45
|
+
# that the function uses to reference input hash keys (which are always symbolized).
|
46
|
+
def symbols
|
47
|
+
fetch(2, nil)
|
48
|
+
end
|
49
|
+
end
|
16
50
|
|
17
51
|
# Negation sign prefixed to ranges and functions.
|
18
52
|
NEGATE = '!'
|
data/lib/csv_decision/parse.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'ice_nine'
|
4
|
-
require 'ice_nine/core_ext/object'
|
5
|
-
|
6
3
|
# CSV Decision: CSV based Ruby decision tables.
|
7
4
|
# Created December 2017.
|
8
5
|
# @author Brett Vickers.
|
@@ -63,7 +60,7 @@ module CSVDecision
|
|
63
60
|
parse_table(table: table, input: data, options: options)
|
64
61
|
|
65
62
|
# The table object is now immutable.
|
66
|
-
table.columns.
|
63
|
+
table.columns.freeze
|
67
64
|
table.freeze
|
68
65
|
rescue CSVDecision::Error => exp
|
69
66
|
raise_error(file: table.file, exception: exp)
|
@@ -108,12 +105,110 @@ module CSVDecision
|
|
108
105
|
private_class_method :parse_data
|
109
106
|
|
110
107
|
def self.parse_row(table:, matchers:, row:, index:)
|
108
|
+
# Parse the input cells for this row
|
109
|
+
row = parse_row_ins(table: table, matchers: matchers, row: row, index: index)
|
110
|
+
|
111
|
+
# Parse the output cells for this row
|
112
|
+
parse_row_outs(table: table, matchers: matchers, row: row, index: index)
|
113
|
+
end
|
114
|
+
private_class_method :parse_row
|
115
|
+
|
116
|
+
def self.parse_row_ins(table:, matchers:, row:, index:)
|
117
|
+
# Parse the input cells for this row
|
111
118
|
row, table.scan_rows[index] = matchers.parse_ins(columns: table.columns.ins, row: row)
|
119
|
+
|
120
|
+
# Add any symbol references made by input cell procs to the column dictionary
|
121
|
+
ins_column_dictionary(columns: table.columns.dictionary, row: row)
|
122
|
+
|
123
|
+
row
|
124
|
+
end
|
125
|
+
private_class_method :parse_row_ins
|
126
|
+
|
127
|
+
def self.parse_row_outs(table:, matchers:, row:, index:)
|
128
|
+
# Parse the output cells for this row
|
112
129
|
row, table.outs_rows[index] = matchers.parse_outs(columns: table.columns.outs, row: row)
|
113
130
|
|
131
|
+
outs_column_dictionary(columns: table.columns, row: row)
|
132
|
+
|
114
133
|
row
|
115
134
|
end
|
116
|
-
private_class_method :
|
135
|
+
private_class_method :parse_row_outs
|
136
|
+
|
137
|
+
def self.outs_column_dictionary(columns:, row:)
|
138
|
+
row.each_with_index do |cell, index|
|
139
|
+
outs_check_cell(columns: columns, cell: cell, index: index)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
private_class_method :outs_column_dictionary
|
143
|
+
|
144
|
+
def self.outs_check_cell(columns:, cell:, index:)
|
145
|
+
return unless cell.is_a?(Matchers::Proc)
|
146
|
+
return if cell.symbols.nil?
|
147
|
+
|
148
|
+
check_outs_symbols(columns: columns, cell: cell, index: index)
|
149
|
+
end
|
150
|
+
private_class_method :outs_check_cell
|
151
|
+
|
152
|
+
def self.check_outs_symbols(columns:, cell:, index:)
|
153
|
+
Array(cell.symbols).each do |symbol|
|
154
|
+
check_outs_symbol(columns: columns, symbol: symbol, index: index)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
private_class_method :check_outs_symbols
|
158
|
+
|
159
|
+
def self.check_outs_symbol(columns:, symbol:, index:)
|
160
|
+
in_out = columns.dictionary[symbol]
|
161
|
+
|
162
|
+
# If its an input column symbol then we're good.
|
163
|
+
return if ins_symbol?(columns: columns, symbol: symbol, in_out: in_out)
|
164
|
+
|
165
|
+
# Check if this output symbol reference is on or after this cell's column
|
166
|
+
invalid_out_ref?(columns, index, in_out)
|
167
|
+
end
|
168
|
+
private_class_method :check_outs_symbol
|
169
|
+
|
170
|
+
# If the symbol exists either as an input or does not exist then we're good.
|
171
|
+
def self.ins_symbol?(columns:, symbol:, in_out:)
|
172
|
+
return true if in_out == :in
|
173
|
+
|
174
|
+
# It must an input symbol, as all the output symbols have been parsed.
|
175
|
+
return columns.dictionary[symbol] = :in if in_out.nil?
|
176
|
+
|
177
|
+
false
|
178
|
+
end
|
179
|
+
private_class_method :ins_symbol?
|
180
|
+
|
181
|
+
def self.invalid_out_ref?(columns, index, in_out)
|
182
|
+
return false if in_out < index
|
183
|
+
|
184
|
+
that_column = if in_out == index
|
185
|
+
'reference to itself'
|
186
|
+
else
|
187
|
+
"an out of order reference to output column '#{columns.outs[in_out].name}'"
|
188
|
+
end
|
189
|
+
raise CellValidationError,
|
190
|
+
"output column '#{columns.outs[index].name}' makes #{that_column}"
|
191
|
+
end
|
192
|
+
|
193
|
+
def self.ins_column_dictionary(columns:, row:)
|
194
|
+
row.each { |cell| ins_cell_dictionary(columns: columns, cell: cell) }
|
195
|
+
end
|
196
|
+
private_class_method :ins_column_dictionary
|
197
|
+
|
198
|
+
def self.ins_cell_dictionary(columns:, cell:)
|
199
|
+
return unless cell.is_a?(Matchers::Proc)
|
200
|
+
return if cell.symbols.nil?
|
201
|
+
|
202
|
+
add_ins_symbols(columns: columns, cell: cell)
|
203
|
+
end
|
204
|
+
private_class_method :ins_cell_dictionary
|
205
|
+
|
206
|
+
def self.add_ins_symbols(columns:, cell:)
|
207
|
+
Array(cell.symbols).each do |symbol|
|
208
|
+
Dictionary.add_name(columns: columns, name: symbol)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
private_class_method :add_ins_symbols
|
117
212
|
|
118
213
|
def self.outs_functions(table:, index:)
|
119
214
|
return if table.outs_rows[index].procs.empty?
|
data/lib/csv_decision/result.rb
CHANGED
@@ -20,10 +20,18 @@ module CSVDecision
|
|
20
20
|
@outs = table.columns.outs
|
21
21
|
@if_columns = table.columns.ifs
|
22
22
|
|
23
|
-
# Partial result always
|
24
|
-
|
25
|
-
|
23
|
+
# Partial result always copies in the input hash for calculating output functions.
|
24
|
+
# Note that these input key values will not be mutated, as output columns can never
|
25
|
+
# have the same symbol as an input hash key.
|
26
|
+
# However, the rest of this hash is mutated as output column evaluation results
|
27
|
+
# are accumulated.
|
28
|
+
@partial_result = input[:hash]&.slice(*table.columns.input_keys) if table.outs_functions
|
29
|
+
|
30
|
+
# Attributes hash contains the output decision key value pairs
|
26
31
|
@attributes = {}
|
32
|
+
|
33
|
+
# Set to true if the result has more than one row.
|
34
|
+
# Only possible for the first_match: false option.
|
27
35
|
@multi_result = false
|
28
36
|
end
|
29
37
|
|
@@ -35,7 +43,7 @@ module CSVDecision
|
|
35
43
|
@outs.each_pair { |col, column| @attributes[column.name] = row[col] }
|
36
44
|
end
|
37
45
|
|
38
|
-
# Accumulate the outs into arrays.
|
46
|
+
# Accumulate the outs into arrays of values.
|
39
47
|
# @param row [Array]
|
40
48
|
# @return [void]
|
41
49
|
def accumulate_outs(row)
|
@@ -45,6 +53,7 @@ module CSVDecision
|
|
45
53
|
# Derive the final result.
|
46
54
|
# @return [{Symbol=>Object}]
|
47
55
|
def final
|
56
|
+
# If there are no if: columns, then nothing needs to be filtered out of this result hash.
|
48
57
|
return @attributes if @if_columns.empty?
|
49
58
|
|
50
59
|
@multi_result ? multi_row_result : single_row_result
|
@@ -76,12 +85,13 @@ module CSVDecision
|
|
76
85
|
|
77
86
|
private
|
78
87
|
|
79
|
-
# Case where we have a single row result
|
88
|
+
# Case where we have a single row result, which either gets returned
|
89
|
+
# or filtered by the if: column conditions.
|
80
90
|
def single_row_result
|
81
91
|
@if_columns.each_key do |col|
|
82
92
|
return nil unless @attributes[col]
|
83
93
|
|
84
|
-
# Remove the if: column from the final result
|
94
|
+
# Remove the if: column from the final result hash.
|
85
95
|
@attributes.delete(col)
|
86
96
|
end
|
87
97
|
|
@@ -121,9 +131,11 @@ module CSVDecision
|
|
121
131
|
case count
|
122
132
|
when 0
|
123
133
|
{}
|
124
|
-
|
134
|
+
|
135
|
+
# Single row array values do not require arrays.
|
125
136
|
when 1
|
126
137
|
@attributes.transform_values!(&:first)
|
138
|
+
|
127
139
|
else
|
128
140
|
@attributes
|
129
141
|
end
|
@@ -168,6 +180,10 @@ module CSVDecision
|
|
168
180
|
when nil
|
169
181
|
@attributes[column_name] = cell
|
170
182
|
|
183
|
+
when Matchers::Proc
|
184
|
+
@attributes[column_name] = [current, cell]
|
185
|
+
@multi_result = true
|
186
|
+
|
171
187
|
when Array
|
172
188
|
@attributes[column_name] << cell
|
173
189
|
|
data/lib/csv_decision/table.rb
CHANGED
@@ -16,11 +16,11 @@ module CSVDecision
|
|
16
16
|
Decide.decide(table: self, input: input, symbolize_keys: true)
|
17
17
|
end
|
18
18
|
|
19
|
-
# Unsafe version of decide -
|
20
|
-
# is used (planned feature).
|
19
|
+
# Unsafe version of decide - assumes the input hash is symbolized.
|
21
20
|
#
|
22
21
|
# @param input (see #decide)
|
23
22
|
# @note Input hash must have its keys symbolized.
|
23
|
+
# Input hash will be mutated by any functions that have side effects.
|
24
24
|
# @return (see #decide)
|
25
25
|
def decide!(input)
|
26
26
|
Decide.decide(table: self, input: input, symbolize_keys: false)
|
@@ -37,6 +37,80 @@ describe CSVDecision::Columns do
|
|
37
37
|
expect(table.columns.ins[2].to_h).to eq(name: :input, eval: false, type: :in)
|
38
38
|
expect(table.columns.outs[1].to_h).to eq(name: :output, eval: nil, type: :out)
|
39
39
|
expect(table.columns.outs[3].to_h).to eq(name: :output2, eval: false, type: :out)
|
40
|
+
|
41
|
+
expect(table.columns.dictionary).to eq(input: :in, output: 1, output2: 3)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'recognises all input and output column symbols' do
|
45
|
+
data = <<~DATA
|
46
|
+
IN :input, OUT :output, IN/text :input, OUT/text:output2, out: len, guard:
|
47
|
+
input0, output0, input1, output1, :input2.length,
|
48
|
+
input1, output1, input1, output2, :input3.length, :input4.present?
|
49
|
+
DATA
|
50
|
+
table = CSVDecision.parse(data)
|
51
|
+
|
52
|
+
expect(table.columns).to be_a(CSVDecision::Columns)
|
53
|
+
expect(table.columns.ins[0].to_h).to eq(name: :input, eval: nil, type: :in)
|
54
|
+
expect(table.columns.ins[2].to_h).to eq(name: :input, eval: false, type: :in)
|
55
|
+
expect(table.columns.ins[5].to_h).to eq(name: nil, eval: true, type: :guard)
|
56
|
+
|
57
|
+
expect(table.columns.outs[1].to_h).to eq(name: :output, eval: nil, type: :out)
|
58
|
+
expect(table.columns.outs[3].to_h).to eq(name: :output2, eval: false, type: :out)
|
59
|
+
expect(table.columns.outs[4].to_h).to eq(name: :len, eval: true, type: :out)
|
60
|
+
|
61
|
+
expect(table.columns.dictionary)
|
62
|
+
.to eq(input: :in, output: 1, output2: 3, len: 4, input2: :in, input3: :in, input4: :in)
|
63
|
+
|
64
|
+
expect(table.columns.input_keys).to eq %i[input input2 input4 input3]
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'recognises the output symbol referenced by an output function' do
|
68
|
+
data = <<~DATA
|
69
|
+
IN :input, OUT :output, IN/text :input, OUT/text:output2, out: input3, out: len
|
70
|
+
input0, output0, input1, output1, , :input2.length
|
71
|
+
input1, output1, input1, output2, :input4.present?, :input3.length
|
72
|
+
DATA
|
73
|
+
|
74
|
+
table = CSVDecision.parse(data)
|
75
|
+
|
76
|
+
expect(table.columns).to be_a(CSVDecision::Columns)
|
77
|
+
expect(table.columns.ins[0].to_h).to eq(name: :input, eval: nil, type: :in)
|
78
|
+
expect(table.columns.ins[2].to_h).to eq(name: :input, eval: false, type: :in)
|
79
|
+
expect(table.columns.outs[1].to_h).to eq(name: :output, eval: nil, type: :out)
|
80
|
+
expect(table.columns.outs[3].to_h).to eq(name: :output2, eval: false, type: :out)
|
81
|
+
expect(table.columns.outs[4].to_h).to eq(name: :input3, eval: true, type: :out)
|
82
|
+
expect(table.columns.outs[5].to_h).to eq(name: :len, eval: true, type: :out)
|
83
|
+
|
84
|
+
expect(table.columns.dictionary)
|
85
|
+
.to eq(input: :in, output: 1, output2: 3, len: 5, input2: :in, input3: 4, input4: :in)
|
86
|
+
|
87
|
+
expect(table.columns.input_keys).to eq %i[input input2 input4]
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'raises an error for an output column referring to a later output column' do
|
91
|
+
data = <<~DATA
|
92
|
+
IN :input, OUT :output, IN/text :input, OUT/text:output2, out: len, out: input3,
|
93
|
+
input0, output0, input1, output1, :input2.length,
|
94
|
+
input1, output1, input1, output2, :input3.length :input4.upcase
|
95
|
+
DATA
|
96
|
+
|
97
|
+
expect { CSVDecision.parse(data) }
|
98
|
+
.to raise_error(
|
99
|
+
CSVDecision::CellValidationError,
|
100
|
+
"output column 'len' makes an out of order reference to output column 'input3'"
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'raises an error for an output column referring to itself' do
|
105
|
+
data = <<~DATA
|
106
|
+
IN :input, OUT :output, IN/text :input, OUT/text:output2, out: len, out: input3,
|
107
|
+
input0, output0, input1, output1, :len.length,
|
108
|
+
input1, output1, input1, output2, :len.length :input4.upcase
|
109
|
+
DATA
|
110
|
+
|
111
|
+
expect { CSVDecision.parse(data) }
|
112
|
+
.to raise_error(CSVDecision::CellValidationError,
|
113
|
+
"output column 'len' makes reference to itself")
|
40
114
|
end
|
41
115
|
|
42
116
|
it 'parses a decision table columns from a CSV file' do
|
@@ -106,6 +180,8 @@ describe CSVDecision::Columns do
|
|
106
180
|
|
107
181
|
expect(table.columns.ins[1].to_h)
|
108
182
|
.to eq(name: nil, eval: true, type: :guard)
|
183
|
+
|
184
|
+
expect(table.columns.input_keys).to eq %i[country CUSIP SEDOL]
|
109
185
|
end
|
110
186
|
|
111
187
|
it 'rejects output column being same as input column' do
|
@@ -114,11 +190,23 @@ describe CSVDecision::Columns do
|
|
114
190
|
US, :CUSIP.present?, :CUSIP, CUSUP
|
115
191
|
GB, :SEDOL.present?, :SEDOL, SEDOL
|
116
192
|
DATA
|
193
|
+
|
117
194
|
expect { CSVDecision.parse(data) }
|
118
195
|
.to raise_error(CSVDecision::CellValidationError,
|
119
196
|
"output column name 'country' is also an input column")
|
120
197
|
end
|
121
198
|
|
199
|
+
it 'rejects output column being same as an input symbol not in the header' do
|
200
|
+
data = <<~DATA
|
201
|
+
in :parent, out :node
|
202
|
+
==:node, top
|
203
|
+
, child
|
204
|
+
DATA
|
205
|
+
expect { CSVDecision.parse(data) }
|
206
|
+
.to raise_error(CSVDecision::CellValidationError,
|
207
|
+
"output column name 'node' is also an input column")
|
208
|
+
end
|
209
|
+
|
122
210
|
it 'recognises the if: column' do
|
123
211
|
data = <<~DATA
|
124
212
|
in :country, out :PAID, out :PAID_type, if:
|
@@ -128,5 +216,6 @@ describe CSVDecision::Columns do
|
|
128
216
|
table = CSVDecision.parse(data)
|
129
217
|
|
130
218
|
expect(table.columns.ifs[3].to_h).to eq(name: 3, eval: true, type: :if)
|
219
|
+
expect(table.columns.input_keys).to eq %i[country CUSIP SEDOL]
|
131
220
|
end
|
132
221
|
end
|
@@ -95,6 +95,27 @@ context 'simple examples' do
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
|
+
context 'simple example - column symbols not in header' do
|
99
|
+
data = <<~DATA
|
100
|
+
in :parent, out :top?
|
101
|
+
==:node, yes
|
102
|
+
, no
|
103
|
+
DATA
|
104
|
+
|
105
|
+
it 'makes correct decisions' do
|
106
|
+
table = CSVDecision.parse(data)
|
107
|
+
|
108
|
+
result = table.decide(node: 0, parent: 0)
|
109
|
+
expect(result).to eq(top?: 'yes')
|
110
|
+
|
111
|
+
result = table.decide(node: 1, parent: 0)
|
112
|
+
expect(result).to eq(top?: 'no')
|
113
|
+
|
114
|
+
result = table.decide(node: '0', parent: 0)
|
115
|
+
expect(result).to eq(top?: 'no')
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
98
119
|
it 'makes correct decision for table with symbol ordered compares' do
|
99
120
|
data = <<~DATA
|
100
121
|
in :traded, in :settled, out :status
|
@@ -103,9 +124,10 @@ context 'simple examples' do
|
|
103
124
|
, <:traded, invalid trade
|
104
125
|
, , invalid data
|
105
126
|
DATA
|
106
|
-
|
107
127
|
table = CSVDecision.parse(data)
|
108
128
|
|
129
|
+
expect(table.columns.input_keys).to eq %i[traded settled]
|
130
|
+
|
109
131
|
expect(table.decide(traded: '20171227', settled: '20171227')).to eq(status: 'same day')
|
110
132
|
expect(table.decide(traded: 20171227, settled: 20171227 )).to eq(status: 'same day')
|
111
133
|
expect(table.decide(traded: '20171227', settled: '20171228')).to eq(status: 'pending')
|
@@ -30,7 +30,6 @@ describe CSVDecision::Input do
|
|
30
30
|
expect(result).to eql expected
|
31
31
|
expect(result[:hash]).not_to equal expected[:hash]
|
32
32
|
expect(result[:hash].frozen?).to eq true
|
33
|
-
expect(result[:defaults].frozen?).to eq true
|
34
33
|
end
|
35
34
|
|
36
35
|
it 'processes input hash with symbolize_keys: false' do
|
@@ -49,6 +48,5 @@ describe CSVDecision::Input do
|
|
49
48
|
expect(result).to eql expected
|
50
49
|
expect(result[:hash]).to equal expected[:hash]
|
51
50
|
expect(result[:hash].frozen?).to eq false
|
52
|
-
expect(result[:defaults].frozen?).to eq true
|
53
51
|
end
|
54
52
|
end
|
@@ -439,7 +439,7 @@ describe CSVDecision::Table do
|
|
439
439
|
it "#{method} correctly #{test[:example]}" do
|
440
440
|
table = CSVDecision.parse(test[:data], test[:options])
|
441
441
|
|
442
|
-
expect(table.send(method, country: 'US', CUSIP: '123456789'))
|
442
|
+
expect(table.send(method, country: 'US', CUSIP: '123456789', Ticker: 'USTY'))
|
443
443
|
.to eq(ID: '123456789', ID_type: 'CUSIP', len: 9)
|
444
444
|
|
445
445
|
expect(table.send(method, country: 'US', CUSIP: '123456789', ISIN: '123456789012'))
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: csv_decision
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Vickers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-01-
|
11
|
+
date: 2018-01-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -24,34 +24,6 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '5.1'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: ice_nine
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0.11'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0.11'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: values
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '1.8'
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '1.8'
|
55
27
|
- !ruby/object:Gem::Dependency
|
56
28
|
name: benchmark-ips
|
57
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -223,6 +195,7 @@ files:
|
|
223
195
|
- doc/CSVDecision/Matchers/Matcher.html
|
224
196
|
- doc/CSVDecision/Matchers/Numeric.html
|
225
197
|
- doc/CSVDecision/Matchers/Pattern.html
|
198
|
+
- doc/CSVDecision/Matchers/Proc.html
|
226
199
|
- doc/CSVDecision/Matchers/Range.html
|
227
200
|
- doc/CSVDecision/Matchers/Symbol.html
|
228
201
|
- doc/CSVDecision/Numeric.html
|