csv_decision 0.0.7 → 0.0.8
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 +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
|