rulezilla 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +44 -0
- data/README.md +109 -0
- data/lib/rulezilla.rb +35 -0
- data/lib/rulezilla/basic_support.rb +11 -0
- data/lib/rulezilla/dsl.rb +120 -0
- data/lib/rulezilla/node.rb +44 -0
- data/lib/rulezilla/rule_builder.rb +101 -0
- data/lib/rulezilla/rule_builder/gherkin_to_condition_rule.rb +63 -0
- data/lib/rulezilla/rule_builder/gherkin_to_result_rule.rb +49 -0
- data/lib/rulezilla/tree.rb +76 -0
- data/lib/rulezilla/version.rb +3 -0
- data/rulezilla.gemspec +18 -0
- data/spec/features/default_support_methods.feature +21 -0
- data/spec/features/gherkin_dsl_framework.feature +90 -0
- data/spec/features/gherkin_rules/animal_rule.feature +16 -0
- data/spec/features/gherkin_rules/duration_rule.feature +14 -0
- data/spec/features/rulezilla_dsl_framwork.feature +299 -0
- data/spec/features/step_definitions/rule_steps.rb +64 -0
- data/spec/features/step_definitions/rulezilla_dsl_framework_steps.rb +119 -0
- data/spec/spec_helper.rb +21 -0
- metadata +82 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
module Rulezilla
|
2
|
+
class RuleBuilder
|
3
|
+
class DefaultCondition; end
|
4
|
+
|
5
|
+
class GherkinToConditionRule
|
6
|
+
include Rulezilla::DSL
|
7
|
+
|
8
|
+
define :'this is a "field"' do
|
9
|
+
condition { name =~ /^this is\s?a?n? \"(.*)\"$/i }
|
10
|
+
|
11
|
+
result do
|
12
|
+
field = name.scan(/^this is\s?a?n? \"(.*)\"$/i).flatten.first
|
13
|
+
"#{field}?"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
define :'the "field" is "value"' do
|
18
|
+
condition { name =~ /^the \"(.*)\" is \"(.*)\"$/i }
|
19
|
+
|
20
|
+
result do
|
21
|
+
field, value = name.scan(/^the \"(.*)\" is \"(.*)\"$/i).flatten
|
22
|
+
field = field.gsub(/\s/, '_').downcase
|
23
|
+
|
24
|
+
"#{field} == #{ConditionValueEvaluateRule.apply(value: value)}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
define :'the "field" is in: {table}' do
|
29
|
+
condition { name =~ /^the \"(.*)\" is in:$/i }
|
30
|
+
|
31
|
+
result do
|
32
|
+
field = name.scan(/^the \"(.*)\" is in:$/i).flatten.first
|
33
|
+
field = field.gsub(/\s/, '_').downcase
|
34
|
+
|
35
|
+
values = rows.map{ |row| "#{ConditionValueEvaluateRule.apply(value: row['cells'].first)}" }.join(', ')
|
36
|
+
|
37
|
+
"[#{values}].include?(#{field})"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
define :'none of the above' do
|
42
|
+
condition { name == 'none of the above' }
|
43
|
+
result(DefaultCondition)
|
44
|
+
end
|
45
|
+
|
46
|
+
default { raise "Condition steps is not recognised: #{name}" }
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
class ConditionValueEvaluateRule
|
52
|
+
include Rulezilla::DSL
|
53
|
+
|
54
|
+
define :blank do
|
55
|
+
condition { value == 'blank'}
|
56
|
+
result("''")
|
57
|
+
end
|
58
|
+
|
59
|
+
default { "\"#{value}\"" }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Rulezilla
|
2
|
+
class RuleBuilder
|
3
|
+
class GherkinToResultRule
|
4
|
+
include Rulezilla::DSL
|
5
|
+
|
6
|
+
group :keyword_is do
|
7
|
+
condition { name =~ /^the #{step_keyword} is/i }
|
8
|
+
|
9
|
+
define :value_is do
|
10
|
+
condition { name =~ /^the #{step_keyword} is \"(.*)\"$/i }
|
11
|
+
result { "\"#{name.scan(/^the #{step_keyword} is \"(.*)\"$/i).flatten.first}\"" }
|
12
|
+
end
|
13
|
+
|
14
|
+
define :duration_is do
|
15
|
+
condition { name =~ /^the #{step_keyword} is \"(\d+)\" (days?|hours?|minutes?|seconds?)$/i }
|
16
|
+
result do
|
17
|
+
quantity, unit = name.scan(/^the #{step_keyword} is \"(\d+)\" (days?|hours?|minutes?|seconds?)$/i).flatten
|
18
|
+
|
19
|
+
multiplier = case unit
|
20
|
+
when /day/
|
21
|
+
86400
|
22
|
+
when /hour/
|
23
|
+
3600
|
24
|
+
when /minute/
|
25
|
+
60
|
26
|
+
when /second/
|
27
|
+
1
|
28
|
+
end
|
29
|
+
|
30
|
+
quantity.to_i * multiplier
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
define :start_with_this_is_a do
|
36
|
+
condition { name =~ /^this is an? #{step_keyword}$/i }
|
37
|
+
result("true")
|
38
|
+
end
|
39
|
+
|
40
|
+
define :start_with_this_is_not_a do
|
41
|
+
condition { name =~ /^this is not an? #{step_keyword}$/i }
|
42
|
+
result("false")
|
43
|
+
end
|
44
|
+
|
45
|
+
default { raise "Unrecognisable step: #{name}" }
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Rulezilla
|
2
|
+
class Tree
|
3
|
+
attr_reader :current_node, :root_node
|
4
|
+
|
5
|
+
def initialize(node)
|
6
|
+
@root_node = node
|
7
|
+
@root_node.name = :root
|
8
|
+
@current_node = node
|
9
|
+
end
|
10
|
+
|
11
|
+
def go_up
|
12
|
+
@current_node = is_root? ? @root_node : @current_node.parent
|
13
|
+
end
|
14
|
+
|
15
|
+
def find_all(record, node=@root_node)
|
16
|
+
array = []
|
17
|
+
if node.applies?(record)
|
18
|
+
node.children.each do |child_node|
|
19
|
+
array += find_all(record, child_node)
|
20
|
+
end
|
21
|
+
|
22
|
+
return node.has_result? ? array + [node] : array
|
23
|
+
end
|
24
|
+
return array
|
25
|
+
end
|
26
|
+
|
27
|
+
def trace(record, node=@root_node)
|
28
|
+
if node.applies?(record)
|
29
|
+
node.children.each do |child_node|
|
30
|
+
array = trace(record, child_node)
|
31
|
+
return [node] + array unless array.empty?
|
32
|
+
end
|
33
|
+
return node.has_result? ? [node] : []
|
34
|
+
end
|
35
|
+
return []
|
36
|
+
end
|
37
|
+
|
38
|
+
def all_results(record, node=@root_node, results=[])
|
39
|
+
if node.has_result?
|
40
|
+
results << node.result(record) rescue NoMethodError
|
41
|
+
end
|
42
|
+
|
43
|
+
node.children.each do |child_node|
|
44
|
+
all_results(record, child_node, results)
|
45
|
+
end
|
46
|
+
|
47
|
+
return results
|
48
|
+
end
|
49
|
+
|
50
|
+
def create_and_move_to_child(name=nil)
|
51
|
+
node = Node.new
|
52
|
+
node.name = name
|
53
|
+
@current_node.add_child(node)
|
54
|
+
@current_node = node
|
55
|
+
node
|
56
|
+
end
|
57
|
+
|
58
|
+
def clone_and_append_children(children, node=@current_node)
|
59
|
+
children.each do |child_node|
|
60
|
+
child_node = child_node.dup
|
61
|
+
node.add_child(child_node)
|
62
|
+
|
63
|
+
if child_node.has_children?
|
64
|
+
children_nodes = child_node.children
|
65
|
+
child_node.children = []
|
66
|
+
clone_and_append_children(children_nodes, child_node)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
def is_root?
|
73
|
+
@current_node == @root_node
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/rulezilla.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require 'rulezilla/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ['Peter Wu']
|
6
|
+
gem.email = ['peter.wu@simplybusiness.com']
|
7
|
+
gem.description = %q{Rules DSL}
|
8
|
+
gem.summary = %q{Rules DSL}
|
9
|
+
gem.homepage = %q{https://github.com/simplybusiness/rulezilla}
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.name = 'rulezilla'
|
13
|
+
gem.require_paths = ['lib']
|
14
|
+
gem.version = Rulezilla::VERSION
|
15
|
+
gem.license = 'MIT'
|
16
|
+
|
17
|
+
gem.add_runtime_dependency('gherkin')
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
Feature: Default Support methods
|
2
|
+
|
3
|
+
Scenario Outline: does_not?
|
4
|
+
Given the rule is:
|
5
|
+
"""
|
6
|
+
define :not_sleep do
|
7
|
+
condition { does_not?(go_to_bed) }
|
8
|
+
result('Tired')
|
9
|
+
end
|
10
|
+
|
11
|
+
default('Refreshing')
|
12
|
+
"""
|
13
|
+
When the record has attribute "go_to_bed" and returns "<value>"
|
14
|
+
Then the result is "<result>"
|
15
|
+
|
16
|
+
Examples:
|
17
|
+
| value | result |
|
18
|
+
| true | Refreshing |
|
19
|
+
| false | Tired |
|
20
|
+
| | Refreshing |
|
21
|
+
| nil | Refreshing |
|
@@ -0,0 +1,90 @@
|
|
1
|
+
Feature: Rulezilla Gherkin DSL
|
2
|
+
|
3
|
+
Scenario: Condition: something is something
|
4
|
+
Given the gherkin is:
|
5
|
+
"""
|
6
|
+
Feature: Awesomeness Rule
|
7
|
+
|
8
|
+
Scenario: Robocop
|
9
|
+
When the "target" is "Robocop"
|
10
|
+
Then the awesomeness is "very awesome"
|
11
|
+
"""
|
12
|
+
When the record has attribute "target" and returns "Robocop"
|
13
|
+
Then the result is "very awesome"
|
14
|
+
|
15
|
+
|
16
|
+
Scenario: Multiple Condition: something is something
|
17
|
+
Given the gherkin is:
|
18
|
+
"""
|
19
|
+
Feature: Winner Rule
|
20
|
+
|
21
|
+
Scenario: Robocop vs Ironman
|
22
|
+
When the "target" is "Robocop"
|
23
|
+
And the "opponent" is "Ironman"
|
24
|
+
Then the winner is "Ironman"
|
25
|
+
"""
|
26
|
+
When the record has attribute "target" and returns "Robocop"
|
27
|
+
And the record has attribute "opponent" and returns "Ironman"
|
28
|
+
Then the result is "Ironman"
|
29
|
+
|
30
|
+
|
31
|
+
Scenario: Default
|
32
|
+
Given the gherkin is:
|
33
|
+
"""
|
34
|
+
Feature: Winner Rule
|
35
|
+
|
36
|
+
Scenario: Default
|
37
|
+
When none of the above
|
38
|
+
Then the winner is "Ironman"
|
39
|
+
"""
|
40
|
+
Then the result is "Ironman"
|
41
|
+
|
42
|
+
|
43
|
+
Scenario: 'The :keyword is :value', Keyword mismatch
|
44
|
+
Given the incorrect gherkin is:
|
45
|
+
"""
|
46
|
+
Feature: Winner Rule
|
47
|
+
|
48
|
+
Scenario: Default
|
49
|
+
When none of the above
|
50
|
+
Then the loser is "Hello kitty"
|
51
|
+
"""
|
52
|
+
Then it raises exception "Unrecognisable step: the loser is 'Hello kitty'"
|
53
|
+
|
54
|
+
|
55
|
+
Scenario: True value
|
56
|
+
Given the gherkin is:
|
57
|
+
"""
|
58
|
+
Feature: Dummy Rule
|
59
|
+
|
60
|
+
Scenario: Dummy
|
61
|
+
When the "name" is "007"
|
62
|
+
Then this is a dummy
|
63
|
+
"""
|
64
|
+
When the record has attribute "name" and returns "007"
|
65
|
+
Then the result is "true"
|
66
|
+
|
67
|
+
|
68
|
+
Scenario: False value
|
69
|
+
Given the gherkin is:
|
70
|
+
"""
|
71
|
+
Feature: Dummy Rule
|
72
|
+
|
73
|
+
Scenario: Not Dummy
|
74
|
+
When the "name" is "Tom"
|
75
|
+
Then this is not a dummy
|
76
|
+
"""
|
77
|
+
When the record has attribute "name" and returns "Tom"
|
78
|
+
Then the result is "false"
|
79
|
+
|
80
|
+
|
81
|
+
Scenario: 'This is( not) a :keyword', Keyword mismatch
|
82
|
+
Given the incorrect gherkin is:
|
83
|
+
"""
|
84
|
+
Feature: Winner Rule
|
85
|
+
|
86
|
+
Scenario: Not Dummy
|
87
|
+
When the "name" is "Tom"
|
88
|
+
Then this is not a cat
|
89
|
+
"""
|
90
|
+
Then it raises exception "Unrecognisable step: this is not a 'cat'"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
@rule_steps
|
2
|
+
Feature: Animal Rule
|
3
|
+
|
4
|
+
Scenario: entity is a cat
|
5
|
+
When this is a "cat"
|
6
|
+
Then this is an animal
|
7
|
+
|
8
|
+
Scenario: telephone number is dog or bird
|
9
|
+
When the "entity" is in:
|
10
|
+
| dog |
|
11
|
+
| bird |
|
12
|
+
Then this is an animal
|
13
|
+
|
14
|
+
Scenario: default
|
15
|
+
When none of the above
|
16
|
+
Then this is not an animal
|
@@ -0,0 +1,14 @@
|
|
1
|
+
@rule_steps
|
2
|
+
Feature: Duration Rule
|
3
|
+
|
4
|
+
Scenario: Maths class
|
5
|
+
When the "name of the class" is "Maths"
|
6
|
+
Then the duration is "1" minute
|
7
|
+
|
8
|
+
Scenario: Science class
|
9
|
+
When the "name of the class" is "Science"
|
10
|
+
Then the duration is "10" hours
|
11
|
+
|
12
|
+
Scenario: PE
|
13
|
+
When the "name of the class" is "PE"
|
14
|
+
Then the duration is "2" days
|
@@ -0,0 +1,299 @@
|
|
1
|
+
Feature: Rulezilla DSL
|
2
|
+
|
3
|
+
Scenario: To get all outcome from a rule
|
4
|
+
Given the rule is:
|
5
|
+
"""
|
6
|
+
group :group_1 do
|
7
|
+
condition { false }
|
8
|
+
|
9
|
+
group :group_1_1 do
|
10
|
+
condition { true }
|
11
|
+
|
12
|
+
define :rule_1_1_1 do
|
13
|
+
condition { false }
|
14
|
+
result('A')
|
15
|
+
end
|
16
|
+
|
17
|
+
default('B')
|
18
|
+
end
|
19
|
+
|
20
|
+
define :rule_1_1 do
|
21
|
+
condition { true }
|
22
|
+
result('C')
|
23
|
+
end
|
24
|
+
|
25
|
+
define :rule_1_2 do
|
26
|
+
condition { true }
|
27
|
+
result('D')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
define :rule_2 do
|
32
|
+
condition { false }
|
33
|
+
result('E')
|
34
|
+
end
|
35
|
+
|
36
|
+
default('F')
|
37
|
+
"""
|
38
|
+
Then all the outcomes are "A, B, C, D, E, F"
|
39
|
+
|
40
|
+
Scenario: Rule is evaluated from top to bottom order
|
41
|
+
Given the rule is:
|
42
|
+
"""
|
43
|
+
define :rule_1 do
|
44
|
+
condition { true }
|
45
|
+
result('Yes')
|
46
|
+
end
|
47
|
+
|
48
|
+
define :rule_2 do
|
49
|
+
condition { true }
|
50
|
+
result('May be')
|
51
|
+
end
|
52
|
+
"""
|
53
|
+
Then the result is "Yes"
|
54
|
+
|
55
|
+
|
56
|
+
Scenario: Nesting in Rule DSL
|
57
|
+
Given the rule is:
|
58
|
+
"""
|
59
|
+
group :group_1 do
|
60
|
+
condition { true }
|
61
|
+
|
62
|
+
define :good do
|
63
|
+
condition { true }
|
64
|
+
result('Good')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
"""
|
68
|
+
Then the result is "Good"
|
69
|
+
|
70
|
+
|
71
|
+
Scenario: If nothing is matched in a group, it will fall to default value of the group
|
72
|
+
Given the rule is:
|
73
|
+
"""
|
74
|
+
group :group_1 do
|
75
|
+
condition { true }
|
76
|
+
|
77
|
+
define :good do
|
78
|
+
condition { false }
|
79
|
+
result('Good')
|
80
|
+
end
|
81
|
+
|
82
|
+
default('It is alright')
|
83
|
+
end
|
84
|
+
"""
|
85
|
+
Then the result is "It is alright"
|
86
|
+
|
87
|
+
|
88
|
+
Scenario: If nothing is matched, and no default is define in the group, it will fall to the next default
|
89
|
+
Given the rule is:
|
90
|
+
"""
|
91
|
+
group :group_1 do
|
92
|
+
condition { true }
|
93
|
+
|
94
|
+
define :good do
|
95
|
+
condition { false }
|
96
|
+
result('Good')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
default('Everything is awesome')
|
101
|
+
"""
|
102
|
+
Then the result is "Everything is awesome"
|
103
|
+
|
104
|
+
|
105
|
+
Scenario: If nothing is matched, it will continue to evaluate the next group
|
106
|
+
Given the rule is:
|
107
|
+
"""
|
108
|
+
group :group_1 do
|
109
|
+
condition { true }
|
110
|
+
|
111
|
+
define :good do
|
112
|
+
condition { false }
|
113
|
+
result('Good')
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
group :group_2 do
|
118
|
+
condition { true }
|
119
|
+
|
120
|
+
define :bad do
|
121
|
+
condition { true }
|
122
|
+
result('Bad')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
"""
|
126
|
+
Then the result is "Bad"
|
127
|
+
|
128
|
+
|
129
|
+
Scenario Outline: It evaluate the rule against a record
|
130
|
+
Given the rule is:
|
131
|
+
"""
|
132
|
+
define :fruit do
|
133
|
+
condition { fruit }
|
134
|
+
result('This is good!')
|
135
|
+
end
|
136
|
+
|
137
|
+
define :fruit do
|
138
|
+
condition { !fruit }
|
139
|
+
result('Oh, too bad')
|
140
|
+
end
|
141
|
+
"""
|
142
|
+
When the record has attribute "fruit" and returns "<value>"
|
143
|
+
Then the result is "<result>"
|
144
|
+
|
145
|
+
Examples:
|
146
|
+
| value | result |
|
147
|
+
| true | This is good! |
|
148
|
+
| false | Oh, too bad |
|
149
|
+
|
150
|
+
|
151
|
+
Scenario: To get all matching outcomes from a rule
|
152
|
+
Given the rule is:
|
153
|
+
"""
|
154
|
+
group :group_1 do
|
155
|
+
condition { true }
|
156
|
+
|
157
|
+
group :group_1_1 do
|
158
|
+
condition { true }
|
159
|
+
|
160
|
+
define :rule_1_1_1 do
|
161
|
+
condition { false }
|
162
|
+
result('A')
|
163
|
+
end
|
164
|
+
|
165
|
+
default('B')
|
166
|
+
end
|
167
|
+
|
168
|
+
define :rule_1_1 do
|
169
|
+
condition { true }
|
170
|
+
result('C')
|
171
|
+
end
|
172
|
+
|
173
|
+
define :rule_1_2 do
|
174
|
+
condition { true }
|
175
|
+
result('D')
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
define :rule_2 do
|
180
|
+
condition { false }
|
181
|
+
result('E')
|
182
|
+
end
|
183
|
+
|
184
|
+
default('F')
|
185
|
+
"""
|
186
|
+
Then all the matching outcomes are "B, C, D, F"
|
187
|
+
|
188
|
+
|
189
|
+
Scenario: Support Module
|
190
|
+
Given the rule class name is "FruitRule"
|
191
|
+
And the support module called "FruitRuleSupport" has definition:
|
192
|
+
"""
|
193
|
+
def is_fruit?
|
194
|
+
fruit == true
|
195
|
+
end
|
196
|
+
"""
|
197
|
+
And the rule is:
|
198
|
+
"""
|
199
|
+
define :fruit do
|
200
|
+
condition { is_fruit? }
|
201
|
+
result('This is good!')
|
202
|
+
end
|
203
|
+
|
204
|
+
define :fruit do
|
205
|
+
condition { !is_fruit? }
|
206
|
+
result('Oh, too bad')
|
207
|
+
end
|
208
|
+
"""
|
209
|
+
When the record has attribute "fruit" and returns "true"
|
210
|
+
Then the result is "This is good!"
|
211
|
+
|
212
|
+
|
213
|
+
Scenario Outline: Validate the presence of attributes
|
214
|
+
Given the rule is:
|
215
|
+
"""
|
216
|
+
validate_attributes_presence :apple, :orange
|
217
|
+
|
218
|
+
default(true)
|
219
|
+
"""
|
220
|
+
When the record has attribute "<attributes>"
|
221
|
+
Then "<does or does not>" raise the exception "<exception>"
|
222
|
+
|
223
|
+
Examples:
|
224
|
+
| attributes | does or does not | exception |
|
225
|
+
| apple | does | Missing orange attributes |
|
226
|
+
| orange | does | Missing apple attributes |
|
227
|
+
| apple, orange | does not | |
|
228
|
+
|
229
|
+
|
230
|
+
Scenario: Rule return nil if no rule is defined Given the rule is:
|
231
|
+
Given the rule is:
|
232
|
+
"""
|
233
|
+
"""
|
234
|
+
Then the result is "nil"
|
235
|
+
|
236
|
+
Scenario Outline: Trace the path to the result
|
237
|
+
Given the rule is:
|
238
|
+
"""
|
239
|
+
group :group_1 do
|
240
|
+
condition { group_1_condition }
|
241
|
+
|
242
|
+
group :group_2 do
|
243
|
+
condition { group_2_condition }
|
244
|
+
|
245
|
+
define :rule_1 do
|
246
|
+
condition { false }
|
247
|
+
result('A')
|
248
|
+
end
|
249
|
+
|
250
|
+
define :rule_2 do
|
251
|
+
condition { rule_2_condition }
|
252
|
+
result('B')
|
253
|
+
end
|
254
|
+
|
255
|
+
default('C')
|
256
|
+
end
|
257
|
+
|
258
|
+
default('D')
|
259
|
+
end
|
260
|
+
|
261
|
+
define :rule_3 do
|
262
|
+
condition { rule_3_condition }
|
263
|
+
result('E')
|
264
|
+
end
|
265
|
+
|
266
|
+
default('F')
|
267
|
+
"""
|
268
|
+
When the record has attribute "group_1_condition" and returns "<group_1_condition>"
|
269
|
+
And the record has attribute "group_2_condition" and returns "<group_2_condition>"
|
270
|
+
And the record has attribute "rule_2_condition" and returns "<rule_2_condition>"
|
271
|
+
And the record has attribute "rule_3_condition" and returns "<rule_3_condition>"
|
272
|
+
Then the trace is "<trace>"
|
273
|
+
|
274
|
+
Examples:
|
275
|
+
| group_1_condition | group_2_condition | rule_2_condition | rule_3_condition | trace |
|
276
|
+
| true | true | true | true | root -> group_1 -> group_2 -> rule_2 |
|
277
|
+
| true | false | true | true | root -> group_1 |
|
278
|
+
| false | true | true | true | root -> rule_3 |
|
279
|
+
| false | true | true | false | root |
|
280
|
+
|
281
|
+
Scenario: Include rule
|
282
|
+
Given there is a rule called "CommonRule":
|
283
|
+
"""
|
284
|
+
define :a do
|
285
|
+
condition { true }
|
286
|
+
result { 'A' }
|
287
|
+
end
|
288
|
+
|
289
|
+
define :b do
|
290
|
+
condition { false }
|
291
|
+
result { 'B' }
|
292
|
+
end
|
293
|
+
"""
|
294
|
+
And our rule is:
|
295
|
+
"""
|
296
|
+
include_rule CommonRule
|
297
|
+
"""
|
298
|
+
Then all the outcomes are "A, B"
|
299
|
+
And the result is "A"
|