csv_decision 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +48 -32
- data/csv_decision.gemspec +1 -1
- data/lib/csv_decision/columns.rb +11 -0
- data/lib/csv_decision/header.rb +16 -18
- data/lib/csv_decision/matchers/guard.rb +18 -10
- data/lib/csv_decision/matchers/symbol.rb +2 -2
- data/lib/csv_decision/scan_row.rb +7 -1
- data/spec/csv_decision/matchers/symbol_spec.rb +3 -1
- data/spec/csv_decision/table_spec.rb +32 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 63aeefecf31a4b0c640e6b345fa1fdcd8af686ab
|
4
|
+
data.tar.gz: 4c1f45f5272e4c565ccd08482beb72e1f8566835
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0bf8ca1cacd4a5c31198a544c32e4ff0c8138bb9c69771062a91df22ccb4d4e164db7b4134bd21e4be7f857e2909c730a22b6382d21e422c82a030c1641b454c
|
7
|
+
data.tar.gz: 3a54e85c4c8af0859dafcd871a86f143a8cf7f3ae608dda674642c34f5321cee3955fc26910abe496ea7c7912cadf5b26ea378e1bc3b8d884441e42a23139e17
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## v0.0.8, 31 December 2017.
|
2
|
+
*Additions*
|
3
|
+
- Guard conditions can use `=~` and `!~` for regular expressions.
|
4
|
+
|
5
|
+
*Fixes*
|
6
|
+
- Bug with column symbol expression not recognising >= and <=.
|
7
|
+
|
8
|
+
## v0.0.7, 30 December 2017.
|
9
|
+
*Additions*
|
10
|
+
- Guard conditions using column symbols and expressions.
|
11
|
+
- Guard columns.
|
12
|
+
- Symbol functions (0-arity) in output columns.
|
13
|
+
- Update YARD documentation.
|
14
|
+
|
1
15
|
## v0.0.6, 26 December 2017.
|
2
16
|
*Additions*
|
3
17
|
- Update YARD documentation.
|
data/README.md
CHANGED
@@ -20,16 +20,16 @@ producing a decision as an output hash.
|
|
20
20
|
|
21
21
|
Typical "business logic" is notoriously illogical -- full of corner cases and one-off
|
22
22
|
exceptions.
|
23
|
-
A decision table can
|
23
|
+
A decision table can express data-based decisions in a way that comes more naturally
|
24
24
|
to subject matter experts, who typically prefer spreadsheet models.
|
25
25
|
Business logic may then be encapsulated, avoiding the need to write tortuous
|
26
26
|
conditional expressions in Ruby that draw the ire of `rubocop` and its ilk.
|
27
27
|
|
28
28
|
This gem and the examples below take inspiration from
|
29
29
|
[rufus/decision](https://github.com/jmettraux/rufus-decision).
|
30
|
-
(
|
31
|
-
decision-time performance
|
32
|
-
see `benchmarks/rufus_decision.rb
|
30
|
+
(That gem is no longer maintained and CSV Decision has better
|
31
|
+
decision-time performance, at the expense of slower table parse times and more memory --
|
32
|
+
see `benchmarks/rufus_decision.rb`.)
|
33
33
|
|
34
34
|
### Installation
|
35
35
|
|
@@ -68,14 +68,14 @@ When the topic is `finance` and the region is `Europe` the team member `Donald`
|
|
68
68
|
is selected.
|
69
69
|
|
70
70
|
This is a "first match" decision table in that as soon as a match is made execution
|
71
|
-
stops and a single output
|
71
|
+
stops and a single output row (hash) is returned.
|
72
72
|
|
73
73
|
The ordering of rows matters. `Ernest`, who is in charge of `finance` for the rest of
|
74
74
|
the world, except for `America` and `Europe`, *must* come after his colleagues
|
75
75
|
`Charlie` and `Donald`. `Zach` has been placed last, catching all the input combos
|
76
76
|
not matching any other row.
|
77
77
|
|
78
|
-
Here
|
78
|
+
Here is the example as code:
|
79
79
|
|
80
80
|
```ruby
|
81
81
|
# Valid CSV string
|
@@ -94,9 +94,9 @@ Here it is as code:
|
|
94
94
|
|
95
95
|
table = CSVDecision.parse(data)
|
96
96
|
|
97
|
-
table.decide(topic: 'finance', region: 'Europe')
|
98
|
-
table.decide(topic: 'sports', region: nil)
|
99
|
-
table.decide(topic: 'culture', region: 'America')
|
97
|
+
table.decide(topic: 'finance', region: 'Europe') #=> { team_member: 'Donald' }
|
98
|
+
table.decide(topic: 'sports', region: nil) #=> { team_member: 'Bob' }
|
99
|
+
table.decide(topic: 'culture', region: 'America') #=> { team_member: 'Zach' }
|
100
100
|
```
|
101
101
|
|
102
102
|
An empty `in` cell means "matches any value", even nils.
|
@@ -108,12 +108,12 @@ the table from a CSV file:
|
|
108
108
|
table = CSVDecision.parse(Pathname('spec/data/valid/simple_example.csv'))
|
109
109
|
```
|
110
110
|
|
111
|
-
|
112
|
-
|
111
|
+
We can also load this same table using the option: `first_match: false`, which means that
|
112
|
+
*all* matching rows will be accumulated into an array of hashes.
|
113
113
|
|
114
114
|
```ruby
|
115
115
|
table = CSVDecision.parse(data, first_match: false)
|
116
|
-
table.decide(topic: 'finance', region: 'Europe')
|
116
|
+
table.decide(topic: 'finance', region: 'Europe') #=> { team_member: %w[Donald Ernest Zach] }
|
117
117
|
```
|
118
118
|
|
119
119
|
For more examples see `spec/csv_decision/table_spec.rb`.
|
@@ -121,18 +121,24 @@ Complete documentation of all table parameters is in the code - see
|
|
121
121
|
`lib/csv_decision/parse.rb` and `lib/csv_decision/table.rb`.
|
122
122
|
|
123
123
|
### CSV Decision features
|
124
|
+
* Either returns the first matching row as a hash (default), or accumulates all matches as an
|
125
|
+
array of hashes (i.e., `parse` option `first_match: false` or CSV file option `accumulate`).
|
124
126
|
* Fast decision-time performance (see `benchmarks` folder).
|
125
|
-
* In addition to simple
|
126
|
-
regular expressions,
|
127
|
-
|
127
|
+
* In addition to simple strings, `csv_decision` can match basic Ruby constants (e.g., `=nil`),
|
128
|
+
regular expressions (e.g., `=~ on|off`), comparisons (e.g., `> 100.0` ) and
|
129
|
+
Ruby-style ranges (e.g., `1..10`)
|
130
|
+
* Can compare an input column versus another input hash key - e.g., `> :column`.
|
131
|
+
* Any cell starting with `#` is treated as a comment, and comments may appear anywhere in the
|
132
|
+
table. (Comment cells are always interpreted as the empty string.)
|
133
|
+
* Can use column symbol expressions or Ruby methods (0-arity) in input columns for
|
134
|
+
matching - e.g., `:column.zero?` or `:column == 0`.
|
135
|
+
* May also use Ruby methods in output columns - e.g., `:column.length`.
|
128
136
|
* Accepts data as a file, CSV string or an array of arrays. (For safety all input data is
|
129
137
|
force encoded to UTF-8, and non-ascii strings are converted to empty strings.)
|
130
138
|
* All CSV cells are parsed for correctness, and helpful error messages generated for bad
|
131
|
-
|
132
|
-
* Either returns the first matching row as a hash, or accumulates all matches as an
|
133
|
-
array of hashes.
|
139
|
+
input.
|
134
140
|
|
135
|
-
|
141
|
+
#### Constants other than strings
|
136
142
|
Although `csv_decision` is string oriented, it does recognise other types of constant
|
137
143
|
present in the input hash. Specifically, the following classes are recognized:
|
138
144
|
`Integer`, `BigDecimal`, `NilClass`, `TrueClass` and `FalseClass`.
|
@@ -157,7 +163,7 @@ For example:
|
|
157
163
|
table.decide(constant: BigDecimal('100.0')) # returns value: BigDecimal('100.0')
|
158
164
|
```
|
159
165
|
|
160
|
-
|
166
|
+
#### Column header symbols
|
161
167
|
All input and output column names are symbolized, and can be used to form simple
|
162
168
|
expressions that refer to values in the input hash.
|
163
169
|
|
@@ -175,8 +181,9 @@ For example:
|
|
175
181
|
```
|
176
182
|
|
177
183
|
Note that there is no need to include an input column for `:node` in the decision
|
178
|
-
table - it just needs to be present in the input hash.
|
179
|
-
shortened to just `:node`, so the above decision table
|
184
|
+
table - it just needs to be present in the input hash. The expression, `== :node` should be
|
185
|
+
read as `:parent == :node`. It can also be shortened to just `:node`, so the above decision table
|
186
|
+
may be simplified to:
|
180
187
|
|
181
188
|
```ruby
|
182
189
|
data = <<~DATA
|
@@ -188,7 +195,7 @@ shortened to just `:node`, so the above decision table may be simplified to:
|
|
188
195
|
These comparison operators are also supported: `!=`, `>`, `>=`, `<`, `<=`.
|
189
196
|
For more simple examples see `spec/csv_decision/examples_spec.rb`.
|
190
197
|
|
191
|
-
|
198
|
+
#### Column guard conditions
|
192
199
|
Sometimes it's more convenient to write guard conditions in a single column specialized for that purpose.
|
193
200
|
For example:
|
194
201
|
|
@@ -208,7 +215,9 @@ table.decide(country: 'US', CUSIP: '123456789') #=> { ID: '123456789', ID_type:
|
|
208
215
|
table.decide(country: 'EU', CUSIP: '123456789', ISIN:'123456789012')
|
209
216
|
#=> { ID: '123456789012', ID_type: 'ISIN', len: 12 }
|
210
217
|
```
|
211
|
-
Guard columns may be anonymous, and must contain non-constant expressions.
|
218
|
+
Guard columns may be anonymous, and must contain non-constant expressions. In addition to
|
219
|
+
0-arity Ruby methods, the following comparison operators are also supported: `==`, `!=`,
|
220
|
+
`>`, `>=`, `<` and `<=`. Also, regular expressions are supported - i.e., `=~` and `!~`.
|
212
221
|
|
213
222
|
### Testing
|
214
223
|
|
@@ -223,12 +232,19 @@ Guard columns may be anonymous, and must contain non-constant expressions.
|
|
223
232
|
### Planned features
|
224
233
|
`csv_decision` is still a work in progress, and will be enhanced to support
|
225
234
|
the following features:
|
226
|
-
*
|
227
|
-
columns
|
228
|
-
|
229
|
-
*
|
230
|
-
|
231
|
-
*
|
232
|
-
*
|
235
|
+
* Text-only input columns may be indexed for faster lookup performance.
|
236
|
+
* Supply a pre-defined library of functions that can be called within input columns to
|
237
|
+
implement matching logic or from the output columns to formulate the final decision.
|
238
|
+
* Available functions may be extended with a user-supplied library of Ruby methods
|
239
|
+
for tailored logic.
|
240
|
+
* Input hash values may be (conditionally) defaulted with a constant or a function call.
|
241
|
+
* Output columns may construct interpolated strings referencing column symbols.
|
233
242
|
* Can use post-match guard conditions to filter the results of multi-row
|
234
|
-
|
243
|
+
decision output.
|
244
|
+
|
245
|
+
### Reasons for the limitations of column expressions
|
246
|
+
The simple column expressions allowed by `csv_decision` are purposely limited for reasons of
|
247
|
+
understandability and maintainability. The whole point of this gem is to make decision rules
|
248
|
+
easier to express and comprehend as declarative, tabular logic.
|
249
|
+
While Ruby makes it easy to execute arbitrary code embedded within a CSV file,
|
250
|
+
this could easily result in hard to debug logic that also poses safety risks.
|
data/csv_decision.gemspec
CHANGED
@@ -5,7 +5,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = 'csv_decision'
|
8
|
-
spec.version = '0.0.
|
8
|
+
spec.version = '0.0.8'
|
9
9
|
spec.authors = ['Brett Vickers']
|
10
10
|
spec.email = ['brett@phillips-vickers.com']
|
11
11
|
spec.description = 'CSV based Ruby decision tables.'
|
data/lib/csv_decision/columns.rb
CHANGED
@@ -30,6 +30,10 @@ module CSVDecision
|
|
30
30
|
# @return [Hash{Integer=>Entry}] All output column dictionary entries.
|
31
31
|
attr_accessor :outs
|
32
32
|
|
33
|
+
# if: columns.
|
34
|
+
# @return [Hash{Integer=>Entry}] All if: column dictionary entries.
|
35
|
+
attr_accessor :ifs
|
36
|
+
|
33
37
|
# TODO: Input hash path - optional (planned feature)
|
34
38
|
# attr_accessor :path
|
35
39
|
|
@@ -39,6 +43,7 @@ module CSVDecision
|
|
39
43
|
def initialize
|
40
44
|
@ins = {}
|
41
45
|
@outs = {}
|
46
|
+
@ifs = {}
|
42
47
|
# TODO: @path = {}
|
43
48
|
# TODO: @defaults = {}
|
44
49
|
end
|
@@ -60,6 +65,12 @@ module CSVDecision
|
|
60
65
|
@dictionary.outs
|
61
66
|
end
|
62
67
|
|
68
|
+
# if: columns hash keyed by column index.
|
69
|
+
# @return [Hash{Index=>Entry}]
|
70
|
+
def ifs
|
71
|
+
@dictionary.ifs
|
72
|
+
end
|
73
|
+
|
63
74
|
# Input columns with defaults specified (planned feature)
|
64
75
|
# def defaults
|
65
76
|
# @dictionary.defaults
|
data/lib/csv_decision/header.rb
CHANGED
@@ -16,10 +16,20 @@ module CSVDecision
|
|
16
16
|
|
17
17
|
# Column types recognised in the header row.
|
18
18
|
COLUMN_TYPE = %r{
|
19
|
-
\A(?<type>in|out|in/text|out/text|guard)
|
19
|
+
\A(?<type>in|out|in/text|out/text|guard|if)
|
20
20
|
\s*:\s*(?<name>\S?.*)\z
|
21
21
|
}xi
|
22
22
|
|
23
|
+
COLUMN_ENTRY = {
|
24
|
+
in: { type: :in, eval: nil },
|
25
|
+
'in/text': { type: :in, eval: false },
|
26
|
+
out: { type: :out, eval: nil },
|
27
|
+
'out/text': { type: :out, eval: false },
|
28
|
+
guard: { type: :guard, eval: true },
|
29
|
+
if: { type: :if, eval: true }
|
30
|
+
}.freeze
|
31
|
+
private_constant :COLUMN_ENTRY
|
32
|
+
|
23
33
|
# TODO: implement all anonymous column types
|
24
34
|
# COLUMN_TYPE_ANONYMOUS = Set.new(%i[path if guard]).freeze
|
25
35
|
# These column types do not need a name
|
@@ -88,7 +98,7 @@ module CSVDecision
|
|
88
98
|
end
|
89
99
|
private_class_method :input_column?
|
90
100
|
|
91
|
-
def self.
|
101
|
+
def self.validate_column(cell:)
|
92
102
|
match = COLUMN_TYPE.match(cell)
|
93
103
|
raise CellValidationError, 'column name is not well formed' unless match
|
94
104
|
|
@@ -99,7 +109,7 @@ module CSVDecision
|
|
99
109
|
rescue CellValidationError => exp
|
100
110
|
raise CellValidationError, "header column '#{cell}' is not valid as the #{exp.message}"
|
101
111
|
end
|
102
|
-
private_class_method :
|
112
|
+
private_class_method :validate_column
|
103
113
|
|
104
114
|
# Array of all empty column indices.
|
105
115
|
def self.empty_columns?(row:)
|
@@ -131,25 +141,13 @@ module CSVDecision
|
|
131
141
|
# Returns the normalized column type, along with an indication if
|
132
142
|
# the column requires evaluation
|
133
143
|
def self.column_type(column_name, type)
|
134
|
-
|
135
|
-
|
136
|
-
Columns::Entry.new(column_name, false, :in)
|
137
|
-
|
138
|
-
when :guard
|
139
|
-
Columns::Entry.new(column_name, true, :guard)
|
140
|
-
|
141
|
-
when :'out/text'
|
142
|
-
Columns::Entry.new(column_name, false, :out)
|
143
|
-
|
144
|
-
# Column may turn out to be constants only, or not
|
145
|
-
else
|
146
|
-
Columns::Entry.new(column_name, nil, type.to_sym)
|
147
|
-
end
|
144
|
+
entry = COLUMN_ENTRY[type]
|
145
|
+
Columns::Entry.new(column_name, entry[:eval], entry[:type])
|
148
146
|
end
|
149
147
|
private_class_method :column_type
|
150
148
|
|
151
149
|
def self.parse_cell(cell:, index:, dictionary:)
|
152
|
-
column_type, column_name =
|
150
|
+
column_type, column_name = validate_column(cell: cell)
|
153
151
|
|
154
152
|
entry = column_type(column_name, column_type)
|
155
153
|
|
@@ -22,7 +22,7 @@ module CSVDecision
|
|
22
22
|
GUARD =
|
23
23
|
"(?<negate>#{Matchers::NEGATE}?)\\s*" \
|
24
24
|
":(?<name>#{Header::COLUMN_NAME})\\s*" \
|
25
|
-
"(?<method
|
25
|
+
"(?<method>!=|=~|!~|<=|>=|>|<|#{Matchers::EQUALS}|\\.)\\s*" \
|
26
26
|
"(?<param>\\S.*)"
|
27
27
|
private_constant :GUARD
|
28
28
|
|
@@ -30,9 +30,10 @@ module CSVDecision
|
|
30
30
|
private_constant :GUARD_RE
|
31
31
|
|
32
32
|
# Negated methods
|
33
|
-
NEGATION = { '='
|
34
|
-
'>'
|
35
|
-
'.'
|
33
|
+
NEGATION = { '=' => '!=', '==' => '!=', ':=' => '!=', '!=' => '=',
|
34
|
+
'>' => '<=', '>=' => '<', '<' => '>=', '<=' => '>',
|
35
|
+
'.' => '!.',
|
36
|
+
'=~' => '!~', '!~' => '=~' }.freeze
|
36
37
|
private_constant :NEGATION
|
37
38
|
|
38
39
|
# Note: value has already been converted to an Integer or BigDecimal.
|
@@ -46,13 +47,20 @@ module CSVDecision
|
|
46
47
|
}.freeze
|
47
48
|
private_constant :NUMERIC_COMPARE
|
48
49
|
|
49
|
-
def self.symbol_function(symbol,
|
50
|
-
hash[symbol].respond_to?(
|
50
|
+
def self.symbol_function(symbol, method, hash)
|
51
|
+
hash[symbol].respond_to?(method) && hash[symbol].send(method)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.regexp_match(symbol, value, hash)
|
55
|
+
value.is_a?(String) && hash[symbol].is_a?(String) &&
|
56
|
+
Matchers.regexp(value).match(hash[symbol])
|
51
57
|
end
|
52
58
|
|
53
59
|
FUNCTION = {
|
54
|
-
'.' => proc { |symbol,
|
55
|
-
'!.' => proc { |symbol,
|
60
|
+
'.' => proc { |symbol, method, hash| symbol_function(symbol, method, hash) },
|
61
|
+
'!.' => proc { |symbol, method, hash| !symbol_function(symbol, method, hash) },
|
62
|
+
'=~' => proc { |symbol, value, hash| regexp_match(symbol, value, hash) },
|
63
|
+
'!~' => proc { |symbol, value, hash| !regexp_match(symbol, value, hash) }
|
56
64
|
}.freeze
|
57
65
|
private_constant :FUNCTION
|
58
66
|
|
@@ -120,7 +128,7 @@ module CSVDecision
|
|
120
128
|
end
|
121
129
|
private_class_method :symbol_guard
|
122
130
|
|
123
|
-
# (see
|
131
|
+
# (see Matcher#matches?)
|
124
132
|
def self.matches?(cell)
|
125
133
|
proc = symbol_proc(cell)
|
126
134
|
return proc if proc
|
@@ -131,7 +139,7 @@ module CSVDecision
|
|
131
139
|
# @param (see Matcher#matches?)
|
132
140
|
# @return (see Matcher#matches?)
|
133
141
|
def matches?(cell)
|
134
|
-
|
142
|
+
Guard.matches?(cell)
|
135
143
|
end
|
136
144
|
|
137
145
|
# @return (see Matcher#outs?)
|
@@ -10,9 +10,9 @@ module CSVDecision
|
|
10
10
|
class Matchers
|
11
11
|
# Match cell against a symbolic expression - e.g., :column, > :column
|
12
12
|
class Symbol < Matcher
|
13
|
-
#
|
13
|
+
# Column symbol comparison - e.g., > :column or != :column
|
14
14
|
SYMBOL_COMPARE =
|
15
|
-
"(?<comparator
|
15
|
+
"(?<comparator>!=|>=|<=|<|>|#{Matchers::EQUALS})?\\s*:(?<name>#{Header::COLUMN_NAME})"
|
16
16
|
private_constant :SYMBOL_COMPARE
|
17
17
|
|
18
18
|
# Symbol comparision regular expression.
|
@@ -24,7 +24,7 @@ module CSVDecision
|
|
24
24
|
def self.scan_matchers(column:, matchers:, cell:)
|
25
25
|
matchers.each do |matcher|
|
26
26
|
# Guard function only accepts the same matchers as an output column.
|
27
|
-
next if column
|
27
|
+
next if guard_ins_matcher?(column, matcher)
|
28
28
|
|
29
29
|
proc = scan_proc(column: column, cell: cell, matcher: matcher)
|
30
30
|
return proc if proc
|
@@ -35,6 +35,12 @@ module CSVDecision
|
|
35
35
|
end
|
36
36
|
private_class_method :scan_matchers
|
37
37
|
|
38
|
+
# A guard column can only use output matchers
|
39
|
+
def self.guard_ins_matcher?(column, matcher)
|
40
|
+
column.type == :guard && !matcher.outs?
|
41
|
+
end
|
42
|
+
private_class_method :guard_ins_matcher?
|
43
|
+
|
38
44
|
def self.scan_proc(column:, cell:, matcher:)
|
39
45
|
proc = matcher.matches?(cell)
|
40
46
|
guard_constant?(type: proc.type, column: column) if proc
|
@@ -37,12 +37,14 @@ describe CSVDecision::Matchers::Symbol do
|
|
37
37
|
{ cell: ':=:col', value: 0, hash: { col: 0 }, result: true },
|
38
38
|
{ cell: '= :col', value: '0', hash: { col: 0 }, result: false },
|
39
39
|
{ cell: '>=:col', value: 1, hash: { col: 0 }, result: true },
|
40
|
+
{ cell: '>=:col', value: 1, hash: { col: 1 }, result: true },
|
40
41
|
{ cell: '>=:col', value: 0, hash: { col: 1 }, result: false },
|
41
42
|
{ cell: '<=:col', value: 0, hash: { col: 1 }, result: true },
|
42
43
|
{ cell: '<=:col', value: 1, hash: { col: 0 }, result: false },
|
44
|
+
{ cell: '<=:col', value: 1, hash: { col: 1 }, result: true },
|
43
45
|
{ cell: '<=:col', value: '1', hash: { col: 1 }, result: false },
|
46
|
+
{ cell: '<=:col', value: '1', hash: { col: '1' }, result: true },
|
44
47
|
]
|
45
|
-
|
46
48
|
examples.each do |ex|
|
47
49
|
it "cell #{ex[:cell]} matches value: #{ex[:value]} to hash: #{ex[:hash]}" do
|
48
50
|
proc = matcher.matches?(ex[:cell])
|
@@ -131,6 +131,22 @@ describe CSVDecision::Table do
|
|
131
131
|
, maniac, Korolev
|
132
132
|
DATA
|
133
133
|
},
|
134
|
+
{
|
135
|
+
example: 'guard condition',
|
136
|
+
options: { regexp_implicit: false },
|
137
|
+
data: <<~DATA
|
138
|
+
in :age, guard:, out :salesperson
|
139
|
+
18..35, :trait == maniac, Adelsky
|
140
|
+
23..40, :trait =~ bad|maniac, Bronco
|
141
|
+
36..50, :trait =~ bad.*, Espadas
|
142
|
+
==100, , Thorsten
|
143
|
+
44..100, :trait !~ maniac, Ojiisan
|
144
|
+
> 100, :trait =~ maniac.*, Chester
|
145
|
+
23..35, :trait =~ .*rich, Kerfelden
|
146
|
+
, :trait == cheerful, Swanson
|
147
|
+
, :trait == maniac, Korolev
|
148
|
+
DATA
|
149
|
+
},
|
134
150
|
{
|
135
151
|
example: 'multiple in column references',
|
136
152
|
options: { regexp_implicit: false },
|
@@ -239,6 +255,14 @@ describe CSVDecision::Table do
|
|
239
255
|
, no
|
240
256
|
DATA
|
241
257
|
},
|
258
|
+
{ example: 'uses ==:parent & != :parent',
|
259
|
+
options: { first_match: false },
|
260
|
+
data: <<~DATA
|
261
|
+
in :node, out :top?
|
262
|
+
== :parent, yes
|
263
|
+
!= :parent, no
|
264
|
+
DATA
|
265
|
+
},
|
242
266
|
{ example: 'uses != :parent, drops :parent input column',
|
243
267
|
options: {},
|
244
268
|
data: <<~DATA
|
@@ -246,6 +270,14 @@ describe CSVDecision::Table do
|
|
246
270
|
!= :parent, no
|
247
271
|
, yes
|
248
272
|
DATA
|
273
|
+
},
|
274
|
+
{ example: 'uses != :parent and == :parent',
|
275
|
+
options: { first_match: false },
|
276
|
+
data: <<~DATA
|
277
|
+
in :node, out :top?
|
278
|
+
!= :parent, no
|
279
|
+
== :parent, yes
|
280
|
+
DATA
|
249
281
|
}
|
250
282
|
]
|
251
283
|
|
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.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Vickers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-12-
|
11
|
+
date: 2017-12-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|