rudelo 0.2.3 → 0.2.4
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.
- data/Gemfile +1 -0
- data/README.md +4 -3
- data/SET_LOGIC_MATCHER.md +5 -3
- data/lib/rudelo/matchers/set_logic.rb +17 -3
- data/lib/rudelo/parsers/set_value_parser.rb +2 -2
- data/lib/rudelo/version.rb +1 -1
- data/spec/matchers/set_logic_spec.rb +26 -26
- data/spec/parsers/set_logic_parser_spec.rb +11 -11
- data/spec/parsers/set_logic_transform_spec.rb +19 -19
- data/spec/parsers/set_value_parser_spec.rb +4 -8
- metadata +4 -4
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -57,10 +57,11 @@ For a detailed description of the mini-language the set logic matcher uses, see
|
|
57
57
|
|
58
58
|
## Short-Circuiting
|
59
59
|
|
60
|
-
|
60
|
+
By default, Set Logic matchers short-circuit. That is to-say, if a cell does not match, the set logic matcher tells rufus-decision not to try any more matchers if the cell was valid set logic syntax. You can change this behaviour by setting ```matcher.short_circuit = false```
|
61
61
|
|
62
|
-
|
63
|
-
|
62
|
+
## Force Parsing
|
63
|
+
|
64
|
+
By default Set Logic matchers return false for ```matches?``` if they cannot parse a cell. Set ```matcher.force = true``` to force parsing and raise errors if a cell cannot be parsed.
|
64
65
|
|
65
66
|
## Contributing
|
66
67
|
|
data/SET_LOGIC_MATCHER.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
* NOTE: optional space delimiter was removed *
|
2
|
+
|
1
3
|
# Set Logic Matcher
|
2
4
|
|
3
5
|
The SetLogic matcher allows a decision table cell to match based on set logic between the decision table and the corresponding entry in the hash being transformed.
|
@@ -9,13 +11,13 @@ When a decision table cell contains a set expression, the corresponding value in
|
|
9
11
|
|
10
12
|
$(bob, mary) => Set["bob", "mary"]
|
11
13
|
$(bob mary, jeff) => Set["bob mary", "jeff"]
|
12
|
-
$(
|
14
|
+
$(bob rob, jeff) => Set["bob rob", "jeff"]
|
13
15
|
${r: ruby_code} => eval ruby code, ignoring unless it returns a set
|
14
16
|
${other_column} => apply set conversion to other_column
|
15
17
|
$(${c1}, ${c2}) => Set["c1 contents", "c2 contents"]
|
16
18
|
bob, mary => Set["bob", "mary"]
|
17
19
|
bob mary => Set["bob mary"]
|
18
|
-
|
20
|
+
"bob, mary", jeff => Set["bob, mary", "jeff"]
|
19
21
|
|
20
22
|
## Decision Table Cell Syntax
|
21
23
|
|
@@ -87,7 +89,7 @@ Set expressions can use the following operators:
|
|
87
89
|
|
88
90
|
## Examples
|
89
91
|
|
90
|
-
$(bob jeff mary) & $in #= 2
|
92
|
+
$(bob, jeff, mary) & $in #= 2
|
91
93
|
=> does not match (bob, jeff, mary)
|
92
94
|
=> matches (bob, mary) or (jeff, mary) etc.
|
93
95
|
=> does not match (bob) or (jeff) or (mary)
|
@@ -29,17 +29,31 @@ module Rudelo
|
|
29
29
|
module Matchers
|
30
30
|
class SetLogic < Rufus::Decision::Matcher
|
31
31
|
SYNTAX_EXPR = %r{\$\([^)]*\)|\$in}
|
32
|
+
|
33
|
+
# if true, will raise if a cell is not valid set logic syntax
|
32
34
|
attr_accessor :force
|
35
|
+
attr_writer :short_circuit
|
36
|
+
|
37
|
+
def short_circuit
|
38
|
+
defined?(@short_circuit) ? @short_circuit : true
|
39
|
+
end
|
40
|
+
|
41
|
+
def return_on_cant_match
|
42
|
+
short_circuit ? :break : false
|
43
|
+
end
|
33
44
|
|
34
|
-
def
|
45
|
+
def can_match?(cell)
|
35
46
|
! (cell =~ SYNTAX_EXPR).nil?
|
36
47
|
end
|
37
48
|
|
38
49
|
def matches?(cell, value)
|
50
|
+
puts "\n matches? `#{cell}` => `#{value}`"
|
51
|
+
return false unless force || can_match?(cell)
|
39
52
|
evaluator = ast(cell)
|
40
|
-
return
|
53
|
+
return return_on_cant_match if evaluator.nil?
|
41
54
|
in_set = value_transform.apply(value_parser.parse(value)) unless in_set.is_a?(Set)
|
42
|
-
|
55
|
+
result = evaluator.eval(in_set)
|
56
|
+
return result ? true : return_on_cant_match
|
43
57
|
end
|
44
58
|
|
45
59
|
def cell_substitution?
|
@@ -32,7 +32,7 @@ module Rudelo
|
|
32
32
|
rule(:close_set) { str(')') }
|
33
33
|
|
34
34
|
rule(:unquoted_element) {
|
35
|
-
(close_set.absent? >>
|
35
|
+
(close_set.absent? >> comma.absent? >> any).
|
36
36
|
repeat(1).as(:element) }
|
37
37
|
rule(:quoted_element) {
|
38
38
|
(quote >>
|
@@ -40,7 +40,7 @@ module Rudelo
|
|
40
40
|
as(:element) >>
|
41
41
|
quote)}
|
42
42
|
rule(:element) { quoted_element | unquoted_element }
|
43
|
-
rule(:element_delimiter) { (comma
|
43
|
+
rule(:element_delimiter) { (comma) >> space? }
|
44
44
|
|
45
45
|
|
46
46
|
rule(:bare_element_list) {
|
data/lib/rudelo/version.rb
CHANGED
@@ -13,53 +13,53 @@ describe "Rudelo::Matchers::SetLogic" do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
it "matches multiple values against multiple cells correctly" do
|
16
|
-
cell1 = "$in same-as $(a b c)"
|
17
|
-
cell2 = "$in same-as $(k f r)"
|
18
|
-
expect(subject.matches?(cell1, 'a
|
19
|
-
expect(subject.matches?(cell2, 'a
|
20
|
-
expect(subject.matches?(cell1, 'k
|
21
|
-
expect(subject.matches?(cell2, 'k
|
22
|
-
expect(subject.matches?(cell1, 'a
|
23
|
-
expect(subject.matches?(cell2, 'a
|
16
|
+
cell1 = "$in same-as $(a, b, c)"
|
17
|
+
cell2 = "$in same-as $(k, f, r)"
|
18
|
+
expect(subject.matches?(cell1, 'a,b,c')).to be_true
|
19
|
+
expect(subject.matches?(cell2, 'a,b,c')).to eq(:break)
|
20
|
+
expect(subject.matches?(cell1, 'k,f,r')).to eq(:break)
|
21
|
+
expect(subject.matches?(cell2, 'k,f,r')).to be_true
|
22
|
+
expect(subject.matches?(cell1, 'a,b,c')).to be_true
|
23
|
+
expect(subject.matches?(cell2, 'a,b,c')).to eq(:break)
|
24
24
|
end
|
25
25
|
|
26
26
|
context "rufus-decision" do
|
27
27
|
let(:table){
|
28
28
|
table = Rufus::Decision::Table.new(%{
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
29
|
+
in:group, out:situation
|
30
|
+
"$(bob, jeff, mary, alice, ralph) & $in #= 2", company
|
31
|
+
"$(bob, jeff, mary, alice, ralph) & $in same-as $in #= 3", crowd
|
32
|
+
"$(bob, jeff, mary, alice, ralph) >= $in #> 3", exclusive-party
|
33
|
+
"$(bob, jeff, mary, alice, ralph) & $in < $in #> 5", PARTY!
|
34
|
+
"$(bob, jeff, mary, alice, ralph) & $in < $in #> 3", party
|
35
35
|
})
|
36
36
|
table.matchers.unshift Rudelo::Matchers::SetLogic.new
|
37
37
|
table
|
38
38
|
}
|
39
39
|
it "transforms values" do
|
40
40
|
expect(
|
41
|
-
table.transform({'group' => "bob alice"})
|
42
|
-
).to eq({'group' => "bob alice", 'situation' => "company"})
|
41
|
+
table.transform({'group' => "bob, alice"})
|
42
|
+
).to eq({'group' => "bob, alice", 'situation' => "company"})
|
43
43
|
|
44
44
|
expect(
|
45
|
-
table.transform({'group' => "bob alice jeff"})
|
46
|
-
).to eq({'group' => "bob alice jeff", 'situation' => "crowd"})
|
45
|
+
table.transform({'group' => "bob, alice, jeff"})
|
46
|
+
).to eq({'group' => "bob, alice, jeff", 'situation' => "crowd"})
|
47
47
|
|
48
48
|
expect(
|
49
|
-
table.transform({'group' => "bob alice jeff ralph"})
|
50
|
-
).to eq({'group' => "bob alice jeff ralph", 'situation' => "exclusive-party"})
|
49
|
+
table.transform({'group' => "bob, alice, jeff, ralph"})
|
50
|
+
).to eq({'group' => "bob, alice, jeff, ralph", 'situation' => "exclusive-party"})
|
51
51
|
|
52
52
|
expect(
|
53
|
-
table.transform({'group' => "bob alice jeff don"})
|
54
|
-
).to eq({'group' => "bob alice jeff don", 'situation' => "party"})
|
53
|
+
table.transform({'group' => "bob, alice, jeff, don"})
|
54
|
+
).to eq({'group' => "bob, alice, jeff, don", 'situation' => "party"})
|
55
55
|
|
56
56
|
expect(
|
57
|
-
table.transform({'group' => "bob alice jeff mary ralph don"})
|
58
|
-
).to eq({'group' => "bob alice jeff mary ralph don", 'situation' => "PARTY!"})
|
57
|
+
table.transform({'group' => "bob, alice, jeff, mary, ralph, don"})
|
58
|
+
).to eq({'group' => "bob, alice, jeff, mary, ralph, don", 'situation' => "PARTY!"})
|
59
59
|
|
60
60
|
expect(
|
61
|
-
table.transform({'group' => "bob alice jeff mary don bev"})
|
62
|
-
).to eq({'group' => "bob alice jeff mary don bev", 'situation' => "PARTY!"})
|
61
|
+
table.transform({'group' => "bob, alice, jeff, mary, don, bev"})
|
62
|
+
).to eq({'group' => "bob, alice, jeff, mary, don, bev", 'situation' => "PARTY!"})
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
@@ -31,7 +31,7 @@ describe "Rudelo::Parsers::SetLogicParser" do
|
|
31
31
|
|
32
32
|
it "parses set construction to an iterative operation list" do
|
33
33
|
|
34
|
-
expect(expr_parser).to parse('$(bob mary) union $(ralph jeff) & $in', trace: true).as({
|
34
|
+
expect(expr_parser).to parse('$(bob, mary) union $(ralph, jeff) & $in', trace: true).as({
|
35
35
|
:set_construction_expression=>{
|
36
36
|
:left=>{:element_list=>[{:element=>"bob"}, {:element=>"mary"}] },
|
37
37
|
:right=>[
|
@@ -68,7 +68,7 @@ describe "Rudelo::Parsers::SetLogicParser" do
|
|
68
68
|
let(:expr_parser){ parser.set_logic_expression }
|
69
69
|
|
70
70
|
it "parses set logic to a left-right tree" do
|
71
|
-
expect(expr_parser).to parse('$(bob mary) < $(ralph jeff bob mary)', trace: true).as({
|
71
|
+
expect(expr_parser).to parse('$(bob, mary) < $(ralph, jeff, bob, mary)', trace: true).as({
|
72
72
|
:set_logic_expression=>{
|
73
73
|
:left=>{:element_list=>[
|
74
74
|
{:element=>"bob"}, {:element=>"mary"}] },
|
@@ -80,7 +80,7 @@ describe "Rudelo::Parsers::SetLogicParser" do
|
|
80
80
|
end
|
81
81
|
|
82
82
|
it "parses the left hand side as a set construction expression" do
|
83
|
-
expect(expr_parser).to parse('$(bob mary) union $in < $(ralph jeff bob mary)', trace: true).as({
|
83
|
+
expect(expr_parser).to parse('$(bob, mary) union $in < $(ralph, jeff, bob, mary)', trace: true).as({
|
84
84
|
:set_logic_expression=>{
|
85
85
|
:left=> {
|
86
86
|
:set_construction_expression=>{
|
@@ -98,17 +98,17 @@ describe "Rudelo::Parsers::SetLogicParser" do
|
|
98
98
|
end
|
99
99
|
|
100
100
|
it "parses set logic ops in symbol form" do
|
101
|
-
expect(expr_parser).to parse('$(bob mary) < $in', trace: true)
|
102
|
-
expect(expr_parser).to parse('$(bob mary) <= $in', trace: true)
|
103
|
-
expect(expr_parser).to parse('$(bob mary)>$in', trace: true)
|
104
|
-
expect(expr_parser).to parse('$(bob mary) >= $in', trace: true)
|
101
|
+
expect(expr_parser).to parse('$(bob, mary) < $in', trace: true)
|
102
|
+
expect(expr_parser).to parse('$(bob, mary) <= $in', trace: true)
|
103
|
+
expect(expr_parser).to parse('$(bob, mary)>$in', trace: true)
|
104
|
+
expect(expr_parser).to parse('$(bob, mary) >= $in', trace: true)
|
105
105
|
end
|
106
106
|
|
107
107
|
it "parses set logic ops in word form" do
|
108
|
-
expect(expr_parser).to parse('$in superset $(bob mary)', trace: true)
|
109
|
-
expect(expr_parser).to parse('$in subset $(bob mary)', trace: true)
|
110
|
-
expect(expr_parser).to parse('$in proper-superset $(bob mary)', trace: true)
|
111
|
-
expect(expr_parser).to parse('$in proper-subset $(bob mary)', trace: true)
|
108
|
+
expect(expr_parser).to parse('$in superset $(bob, mary)', trace: true)
|
109
|
+
expect(expr_parser).to parse('$in subset $(bob, mary)', trace: true)
|
110
|
+
expect(expr_parser).to parse('$in proper-superset $(bob, mary)', trace: true)
|
111
|
+
expect(expr_parser).to parse('$in proper-subset $(bob, mary)', trace: true)
|
112
112
|
end
|
113
113
|
|
114
114
|
end
|
@@ -152,7 +152,7 @@ describe "Rudelo::Parsers::SetLogicTransform" do
|
|
152
152
|
|
153
153
|
end
|
154
154
|
context "with cardinality" do
|
155
|
-
let(:expr){ "$(k z v) > $in #= 2" }
|
155
|
+
let(:expr){ "$(k, z, v) > $in #= 2" }
|
156
156
|
it "transforms logic expression as match_expression.left" do
|
157
157
|
expect(subject.left).to be_a_kind_of(
|
158
158
|
Rudelo::Parsers::SetLogicTransform::SetLogicExpr)
|
@@ -166,7 +166,7 @@ describe "Rudelo::Parsers::SetLogicTransform" do
|
|
166
166
|
|
167
167
|
context "construction expression" do
|
168
168
|
context "alone" do
|
169
|
-
let(:expr){ "$(k z v) + $in" }
|
169
|
+
let(:expr){ "$(k, z, v) + $in" }
|
170
170
|
it "transforms construction expression as match_expression.left" do
|
171
171
|
expect(subject.left).to be_a_kind_of(
|
172
172
|
Rudelo::Parsers::SetLogicTransform::SetConstructionExpr)
|
@@ -178,7 +178,7 @@ describe "Rudelo::Parsers::SetLogicTransform" do
|
|
178
178
|
|
179
179
|
end
|
180
180
|
context "with cardinality" do
|
181
|
-
let(:expr){ "$(k z v) + $in #= 2" }
|
181
|
+
let(:expr){ "$(k, z, v) + $in #= 2" }
|
182
182
|
it "transforms construction expression as match_expression.left" do
|
183
183
|
expect(subject.left).to be_a_kind_of(
|
184
184
|
Rudelo::Parsers::SetLogicTransform::SetConstructionExpr)
|
@@ -201,11 +201,11 @@ describe "Rudelo::Parsers::SetLogicTransform" do
|
|
201
201
|
|
202
202
|
context "cardinality examples" do
|
203
203
|
specify{ expect(matching(
|
204
|
-
'#= 2', in_set: %w{a b})).to be_true }
|
204
|
+
'#= 2', in_set: %w{a, b})).to be_true }
|
205
205
|
specify{ expect(matching(
|
206
|
-
'#< 3', in_set: %w{a b})).to be_true }
|
206
|
+
'#< 3', in_set: %w{a, b})).to be_true }
|
207
207
|
specify{ expect(matching(
|
208
|
-
'#> 1', in_set: %w{a b})).to be_true }
|
208
|
+
'#> 1', in_set: %w{a, b})).to be_true }
|
209
209
|
specify{ expect(matching(
|
210
210
|
'#= 3', in_set: %w{a b})).to be_false }
|
211
211
|
specify{ expect(matching(
|
@@ -216,42 +216,42 @@ describe "Rudelo::Parsers::SetLogicTransform" do
|
|
216
216
|
|
217
217
|
context "construction examples" do
|
218
218
|
specify{ expect(matching(
|
219
|
-
'$(a b c) & $in #= 2', in_set: %w{a b d})).to be_true }
|
219
|
+
'$(a, b, c) & $in #= 2', in_set: %w{a b d})).to be_true }
|
220
220
|
specify{ expect(matching(
|
221
|
-
'$(a b c) & $in', in_set: %w{a b d})).to be_true }
|
221
|
+
'$(a, b, c) & $in', in_set: %w{a b d})).to be_true }
|
222
222
|
specify{ expect(matching(
|
223
|
-
'$(a b c) & $in', in_set: %w{d e f})).to be_false }
|
223
|
+
'$(a, b, c) & $in', in_set: %w{d e f})).to be_false }
|
224
224
|
specify{ expect(matching(
|
225
|
-
'$(a b c) & $in #= 2', in_set: %w{a k d})).to be_false }
|
225
|
+
'$(a, b, c) & $in #= 2', in_set: %w{a k d})).to be_false }
|
226
226
|
specify{ expect(matching(
|
227
|
-
'$(a b c) + $in + $(e) #= 5', in_set: %w{b c d})).to be_true }
|
227
|
+
'$(a, b, c) + $in + $(e) #= 5', in_set: %w{b c d})).to be_true }
|
228
228
|
end
|
229
229
|
|
230
230
|
context "explicit set examples" do
|
231
231
|
specify{ expect(matching(
|
232
|
-
'$(a b c)', in_set: %w{a b})).to be_true }
|
232
|
+
'$(a, b, c)', in_set: %w{a b})).to be_true }
|
233
233
|
specify{ expect(matching(
|
234
|
-
'$(a b c)', in_set: %w{a k d})).to be_false }
|
234
|
+
'$(a, b, c)', in_set: %w{a k d})).to be_false }
|
235
235
|
end
|
236
236
|
|
237
237
|
context "logic examples" do
|
238
238
|
specify{ expect(matching(
|
239
|
-
'$(a b c) > $in', in_set: %w{a b})).to be_true }
|
239
|
+
'$(a, b, c) > $in', in_set: %w{a b})).to be_true }
|
240
240
|
specify{ expect(matching(
|
241
|
-
'$(a b c) > $in #=2', in_set: %w{a b})).to be_true }
|
241
|
+
'$(a, b, c) > $in #=2', in_set: %w{a b})).to be_true }
|
242
242
|
specify{ expect(matching(
|
243
|
-
'$(a b c) > $in #=1', in_set: %w{a})).to be_true }
|
243
|
+
'$(a, b, c) > $in #=1', in_set: %w{a})).to be_true }
|
244
244
|
specify{ expect(matching(
|
245
|
-
'$(a b c) ^ $in < $(a d k f)', in_set: %w{b c d})).to be_true }
|
245
|
+
'$(a, b, c) ^ $in < $(a, d, k, f)', in_set: %w{b c d})).to be_true }
|
246
246
|
specify{ expect(matching(
|
247
|
-
'$(a b c) ^ $in < $(a e k f)', in_set: %w{b c d})).to be_false }
|
247
|
+
'$(a, b, c) ^ $in < $(a, e, k, f)', in_set: %w{b c d})).to be_false }
|
248
248
|
end
|
249
249
|
|
250
250
|
context "using transform with multiple in-set values" do
|
251
251
|
it "allows re-using a transform" do
|
252
252
|
abc = Set.new(%w{a b c})
|
253
253
|
efg = Set.new(%w{e f g})
|
254
|
-
expr = '$in same-as $(a b c)'
|
254
|
+
expr = '$in same-as $(a, b, c)'
|
255
255
|
|
256
256
|
transform = Rudelo::Parsers::SetLogicTransform.new(abc)
|
257
257
|
ast = transform.apply(parser.parse(expr))
|
@@ -27,16 +27,13 @@ describe "Rudelo::Parsers::SetValueParser" do
|
|
27
27
|
})
|
28
28
|
|
29
29
|
expect(parser).to parse(%Q{bob mary}, trace: true).as({
|
30
|
-
element_list:
|
31
|
-
{element: "bob"}
|
32
|
-
{element: "mary"}
|
33
|
-
]
|
30
|
+
element_list:
|
31
|
+
{element: "bob mary"}
|
34
32
|
})
|
35
33
|
|
36
34
|
expect(parser).to parse(%Q{bob mary, ralph}, trace: true).as({
|
37
35
|
element_list: [
|
38
|
-
{element: "bob"},
|
39
|
-
{element: "mary"},
|
36
|
+
{element: "bob mary"},
|
40
37
|
{element: "ralph"}
|
41
38
|
]
|
42
39
|
})
|
@@ -78,8 +75,7 @@ describe "Rudelo::Parsers::SetValueParser" do
|
|
78
75
|
expect(parser).to parse(%Q{$("bob", mary jeff)}, trace: true).as({
|
79
76
|
element_list: [
|
80
77
|
{element: "bob"},
|
81
|
-
{element: "mary"},
|
82
|
-
{element: "jeff"},
|
78
|
+
{element: "mary jeff"},
|
83
79
|
]
|
84
80
|
})
|
85
81
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rudelo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-05-
|
12
|
+
date: 2013-05-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rufus-decision
|
@@ -136,7 +136,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
136
136
|
version: '0'
|
137
137
|
segments:
|
138
138
|
- 0
|
139
|
-
hash:
|
139
|
+
hash: -283585303558073235
|
140
140
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
141
141
|
none: false
|
142
142
|
requirements:
|
@@ -145,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
145
|
version: '0'
|
146
146
|
segments:
|
147
147
|
- 0
|
148
|
-
hash:
|
148
|
+
hash: -283585303558073235
|
149
149
|
requirements: []
|
150
150
|
rubyforge_project:
|
151
151
|
rubygems_version: 1.8.25
|