ruleby 0.3 → 0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/core/atoms.rb +76 -27
- data/lib/core/engine.rb +11 -5
- data/lib/core/nodes.rb +175 -71
- data/lib/core/patterns.rb +21 -18
- data/lib/dsl/ferrari.rb +50 -33
- data/lib/dsl/letigre.rb +43 -17
- data/lib/dsl/steel.rb +1 -0
- data/lib/dsl/yaml_dsl.rb +23 -0
- data/lib/rulebook.rb +18 -3
- data/lib/ruleby.rb +37 -0
- data/tests/test.rb +15 -0
- metadata +53 -57
- data/benchmarks/basic_rules.rb +0 -66
- data/benchmarks/joined_rules.rb +0 -73
- data/benchmarks/miss_manners/data.rb +0 -146
- data/benchmarks/miss_manners/miss_manners.rb +0 -33
- data/benchmarks/miss_manners/model.rb +0 -193
- data/benchmarks/miss_manners/rules.rb +0 -104
- data/benchmarks/model.rb +0 -36
- data/examples/example_diagnosis.rb +0 -117
- data/examples/example_hello.rb +0 -46
- data/examples/example_politician.rb +0 -97
- data/examples/example_ticket.rb +0 -113
- data/examples/fibonacci_example1.rb +0 -44
- data/examples/fibonacci_example2.rb +0 -40
- data/examples/fibonacci_rulebook.rb +0 -84
- data/examples/test_self_reference.rb +0 -77
data/examples/example_hello.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
# This file is part of the Ruleby project (http://ruleby.org)
|
2
|
-
#
|
3
|
-
# This application is free software; you can redistribute it and/or
|
4
|
-
# modify it under the terms of the Ruby license defined in the
|
5
|
-
# LICENSE.txt file.
|
6
|
-
#
|
7
|
-
# Copyright (c) 2007 Joe Kutner and Matt Smith. All rights reserved.
|
8
|
-
#
|
9
|
-
# * Authors: Joe Kutner, Matt Smith
|
10
|
-
#
|
11
|
-
|
12
|
-
$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib/')
|
13
|
-
require 'ruleby'
|
14
|
-
|
15
|
-
include Ruleby
|
16
|
-
|
17
|
-
class Message
|
18
|
-
def initialize(status,message)
|
19
|
-
@status = status
|
20
|
-
@message = message
|
21
|
-
end
|
22
|
-
attr :status, true
|
23
|
-
attr :message, true
|
24
|
-
end
|
25
|
-
|
26
|
-
class HelloWorldRulebook < Rulebook
|
27
|
-
def rules
|
28
|
-
rule [Message, :m, m.status == :HELLO] do |e,v|
|
29
|
-
puts v[:m].message
|
30
|
-
v[:m].message = "Goodbye world"
|
31
|
-
v[:m].status = :GOODBYE
|
32
|
-
e.modify v[:m]
|
33
|
-
end
|
34
|
-
|
35
|
-
rule [Message, :m, m.status == :GOODBYE] do |e,v|
|
36
|
-
puts v[:m].message
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
|
42
|
-
engine :engine do |e|
|
43
|
-
HelloWorldRulebook.new(e).rules
|
44
|
-
e.assert Message.new(:HELLO, 'Hello World')
|
45
|
-
e.match
|
46
|
-
end
|
@@ -1,97 +0,0 @@
|
|
1
|
-
# This file is part of the Ruleby project (http://ruleby.org)
|
2
|
-
#
|
3
|
-
# This application is free software; you can redistribute it and/or
|
4
|
-
# modify it under the terms of the Ruby license defined in the
|
5
|
-
# LICENSE.txt file.
|
6
|
-
#
|
7
|
-
# Copyright (c) 2007 Joe Kutner and Matt Smith. All rights reserved.
|
8
|
-
#
|
9
|
-
# * Authors: Joe Kutner, Matt Smith
|
10
|
-
#
|
11
|
-
|
12
|
-
$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib/')
|
13
|
-
require 'ruleby'
|
14
|
-
|
15
|
-
include Ruleby
|
16
|
-
|
17
|
-
class Politician
|
18
|
-
def initialize(name,honest)
|
19
|
-
@name = name
|
20
|
-
@honest = honest
|
21
|
-
end
|
22
|
-
attr_accessor :name, :honest
|
23
|
-
def to_s
|
24
|
-
return "[#{@name.to_s},#{@honest.to_s}]"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
class Hope
|
29
|
-
end
|
30
|
-
|
31
|
-
# NOTE this example demonstrates the BlueSteel (block based) DSL syntax
|
32
|
-
class PoliticianRulebook < Rulebook
|
33
|
-
def rules
|
34
|
-
rule :We_have_an_honest_Politician do |r|
|
35
|
-
r.when do |has|
|
36
|
-
has.p Politician
|
37
|
-
has.p.honest = true
|
38
|
-
end
|
39
|
-
r.then do |e,vars|
|
40
|
-
e.assert Hope.new
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
rule :Hope_Lives do |r|
|
45
|
-
r.when do |has|
|
46
|
-
has.h Hope
|
47
|
-
end
|
48
|
-
r.then do |e,vars|
|
49
|
-
puts 'Hurrah!!! Democracy Lives'
|
50
|
-
end
|
51
|
-
r.priority = 10
|
52
|
-
end
|
53
|
-
|
54
|
-
# This example was taken from the JBoss-Rules project. That example
|
55
|
-
# demonstrates a feature that Ruleby does not support: logical assertions.
|
56
|
-
# The JBoss-Rules example, the following rule is fired as the last
|
57
|
-
# activation (because Hope is retracted during fact maintenence).
|
58
|
-
# rule do |r|
|
59
|
-
# r.when = pattern do |p|
|
60
|
-
# p.not :h, Hope
|
61
|
-
# end
|
62
|
-
# r.then 'Hope is Dead' do |r,vars|
|
63
|
-
# puts 'We are all Doomed!!! Democracy is Dead'
|
64
|
-
# end
|
65
|
-
# r.priority = -1
|
66
|
-
# end
|
67
|
-
|
68
|
-
rule :Corrupt_the_Honest do |r|
|
69
|
-
r.when do |has|
|
70
|
-
has.p Politician
|
71
|
-
has.p.honest = true
|
72
|
-
|
73
|
-
has.h Hope
|
74
|
-
end
|
75
|
-
r.then do |e,vars|
|
76
|
-
puts 'I am an evil corporation and I have corrupted ' + vars[:p].name
|
77
|
-
vars[:p].honest = false
|
78
|
-
e.modify vars[:p]
|
79
|
-
end
|
80
|
-
r.priority = 5
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
p1 = Politician.new('blair', true)
|
86
|
-
p2 = Politician.new('bush', true)
|
87
|
-
p3 = Politician.new('chirac', true)
|
88
|
-
p4 = Politician.new('schroder', true)
|
89
|
-
|
90
|
-
engine :engine do |e|
|
91
|
-
PoliticianRulebook.new(e).rules
|
92
|
-
e.assert p1
|
93
|
-
e.assert p2
|
94
|
-
e.assert p3
|
95
|
-
e.assert p4
|
96
|
-
e.match
|
97
|
-
end
|
data/examples/example_ticket.rb
DELETED
@@ -1,113 +0,0 @@
|
|
1
|
-
# This file is part of the Ruleby project (http://ruleby.org)
|
2
|
-
#
|
3
|
-
# This application is free software; you can redistribute it and/or
|
4
|
-
# modify it under the terms of the Ruby license defined in the
|
5
|
-
# LICENSE.txt file.
|
6
|
-
#
|
7
|
-
# Copyright (c) 2007 Joe Kutner and Matt Smith. All rights reserved.
|
8
|
-
#
|
9
|
-
# * Authors: Joe Kutner, Matt Smith
|
10
|
-
#
|
11
|
-
|
12
|
-
$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib/')
|
13
|
-
require 'ruleby'
|
14
|
-
|
15
|
-
include Ruleby
|
16
|
-
|
17
|
-
class Customer
|
18
|
-
def initialize(name,subscription)
|
19
|
-
@name = name
|
20
|
-
@subscription = subscription
|
21
|
-
end
|
22
|
-
attr_reader :name,:subscription
|
23
|
-
def to_s
|
24
|
-
return '[Customer ' + @name.to_s + ' : ' + @subscription.to_s + ']';
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
class Ticket
|
29
|
-
def initialize(customer)
|
30
|
-
@customer = customer
|
31
|
-
@status = :New
|
32
|
-
end
|
33
|
-
attr :status, true
|
34
|
-
attr_reader :customer
|
35
|
-
def to_s
|
36
|
-
return '[Ticket ' + @customer.to_s + ' : ' + @status.to_s + ']';
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
# This example is used in JBoss-Rules to demonstrate durations and the use of
|
41
|
-
# custom DSL. We are simply using it here to demonstrate another example.
|
42
|
-
class TroubleTicketRulebook < Rulebook
|
43
|
-
def rules
|
44
|
-
|
45
|
-
# This is uses the letigre syntax... but we can mix and match syntaxes in
|
46
|
-
# the same rule set.
|
47
|
-
rule :New_Ticket, {:priority => 10}, # :duration => 10},
|
48
|
-
'Customer as :c',
|
49
|
-
'Ticket as :ticket where #customer == #:c #&& #status == :New' do |e,vars|
|
50
|
-
puts 'New : ' + vars[:ticket].to_s
|
51
|
-
end
|
52
|
-
|
53
|
-
# Now we are using the ferrari syntax. The rule method can detect which
|
54
|
-
# syntax we are using, and compile accordingly.
|
55
|
-
rule :Silver_Priority, #{:duration => 3000},
|
56
|
-
[Customer, :customer, m.subscription == 'Silver'],
|
57
|
-
[Ticket,:ticket, m.customer == b(:customer), m.status == :New] do |e,vars|
|
58
|
-
vars[:ticket].status = :Escalate
|
59
|
-
e.modify vars[:ticket]
|
60
|
-
end
|
61
|
-
|
62
|
-
rule :Gold_Priority, #{:duration => 1000},
|
63
|
-
[Customer, :customer, m.subscription == 'Gold'],
|
64
|
-
[Ticket,:ticket, m.customer == b(:customer), m.status == :New] do |e,vars|
|
65
|
-
vars[:ticket].status = :Escalate
|
66
|
-
e.modify vars[:ticket]
|
67
|
-
end
|
68
|
-
|
69
|
-
rule :Platinum_Priority,
|
70
|
-
[Customer, :customer, m.subscription == 'Platinum'],
|
71
|
-
[Ticket,:ticket, m.customer == b(:customer), m.status == :New] do |e,vars|
|
72
|
-
vars[:ticket].status = :Escalate
|
73
|
-
e.modify vars[:ticket]
|
74
|
-
end
|
75
|
-
|
76
|
-
rule :Escalate,
|
77
|
-
'Customer as :c',
|
78
|
-
'Ticket as :ticket where #customer == #:c #&& #status == :Escalate' do |e,vars|
|
79
|
-
puts 'Email : ' + vars[:ticket].to_s
|
80
|
-
end
|
81
|
-
|
82
|
-
rule :Done,
|
83
|
-
'Customer as :c',
|
84
|
-
'Ticket as :ticket where #customer == #:c #&& #status == :Done' do |e,vars|
|
85
|
-
puts 'Done : ' + vars[:ticket].to_s
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
# FACTS
|
91
|
-
|
92
|
-
a = Customer.new('A', 'Gold')
|
93
|
-
b = Customer.new('B', 'Platinum')
|
94
|
-
c = Customer.new('C', 'Silver')
|
95
|
-
d = Customer.new('D', 'Silver')
|
96
|
-
|
97
|
-
t1 = Ticket.new(a)
|
98
|
-
t2 = Ticket.new(b)
|
99
|
-
t3 = Ticket.new(c)
|
100
|
-
t4 = Ticket.new(d)
|
101
|
-
|
102
|
-
engine :engine do |e|
|
103
|
-
TroubleTicketRulebook.new(e).rules
|
104
|
-
e.assert a
|
105
|
-
e.assert b
|
106
|
-
e.assert c
|
107
|
-
e.assert d
|
108
|
-
e.assert t1
|
109
|
-
e.assert t2
|
110
|
-
e.assert t3
|
111
|
-
e.assert t4
|
112
|
-
e.match
|
113
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
# This file is part of the Ruleby project (http://ruleby.org)
|
2
|
-
#
|
3
|
-
# This application is free software; you can redistribute it and/or
|
4
|
-
# modify it under the terms of the Ruby license defined in the
|
5
|
-
# LICENSE.txt file.
|
6
|
-
#
|
7
|
-
# Copyright (c) 2007 Joe Kutner and Matt Smith. All rights reserved.
|
8
|
-
#
|
9
|
-
# * Authors: Joe Kutner, Matt Smith
|
10
|
-
#
|
11
|
-
|
12
|
-
$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib/')
|
13
|
-
require 'ruleby'
|
14
|
-
require 'fibonacci_rulebook'
|
15
|
-
class Fibonacci
|
16
|
-
def initialize(sequence,value=-1)
|
17
|
-
@sequence = sequence
|
18
|
-
@value = value
|
19
|
-
end
|
20
|
-
|
21
|
-
attr_reader :sequence
|
22
|
-
attr :value, true
|
23
|
-
|
24
|
-
def to_s
|
25
|
-
return '['+super + " sequence=" + @sequence.to_s + ",value=" + @value.to_s + ']'
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
include Ruleby
|
30
|
-
|
31
|
-
# This example is borrowed from the JBoss-Rule project.
|
32
|
-
|
33
|
-
# FACTS
|
34
|
-
fib1 = Fibonacci.new(150)
|
35
|
-
|
36
|
-
t1 = Time.new
|
37
|
-
engine :engine do |e|
|
38
|
-
FibonacciRulebookFerrari.new(e).rules
|
39
|
-
e.assert fib1
|
40
|
-
e.match
|
41
|
-
end
|
42
|
-
t2 = Time.new
|
43
|
-
diff = t2.to_f - t1.to_f
|
44
|
-
puts diff.to_s
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# This file is part of the Ruleby project (http://ruleby.org)
|
2
|
-
#
|
3
|
-
# This application is free software; you can redistribute it and/or
|
4
|
-
# modify it under the terms of the Ruby license defined in the
|
5
|
-
# LICENSE.txt file.
|
6
|
-
#
|
7
|
-
# Copyright (c) 2007 Joe Kutner and Matt Smith. All rights reserved.
|
8
|
-
#
|
9
|
-
# * Authors: Joe Kutner, Matt Smith
|
10
|
-
#
|
11
|
-
|
12
|
-
$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib/')
|
13
|
-
require 'ruleby'
|
14
|
-
require 'fibonacci_rulebook'
|
15
|
-
class Fibonacci
|
16
|
-
def initialize(sequence,value=-1)
|
17
|
-
@sequence = sequence
|
18
|
-
@value = value
|
19
|
-
end
|
20
|
-
|
21
|
-
attr_reader :sequence
|
22
|
-
attr :value, true
|
23
|
-
|
24
|
-
def to_s
|
25
|
-
return super + "::sequence=" + @sequence.to_s + ",value=" + @value.to_s
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
include Ruleby
|
30
|
-
|
31
|
-
# FACTS
|
32
|
-
fib1 = Fibonacci.new(1,1)
|
33
|
-
fib2 = Fibonacci.new(2,1)
|
34
|
-
|
35
|
-
engine :engine do |e|
|
36
|
-
FibonacciRulebook2.new(e).rules
|
37
|
-
e.assert fib1
|
38
|
-
e.assert fib2
|
39
|
-
e.match
|
40
|
-
end
|
@@ -1,84 +0,0 @@
|
|
1
|
-
# This file is part of the Ruleby project (http://ruleby.org)
|
2
|
-
#
|
3
|
-
# This application is free software; you can redistribute it and/or
|
4
|
-
# modify it under the terms of the Ruby license defined in the
|
5
|
-
# LICENSE.txt file.
|
6
|
-
#
|
7
|
-
# Copyright (c) 2007 Joe Kutner and Matt Smith. All rights reserved.
|
8
|
-
#
|
9
|
-
# * Authors: Joe Kutner, Matt Smith
|
10
|
-
#
|
11
|
-
|
12
|
-
require 'ruleby'
|
13
|
-
|
14
|
-
include Ruleby
|
15
|
-
|
16
|
-
# NOTE this example uses the LeTigre DSL syntax. In addition, its semantics are
|
17
|
-
# different from the other classes.
|
18
|
-
class FibonacciRulebook2 < Rulebook
|
19
|
-
MAX_SEQUENCE = 100
|
20
|
-
def rules
|
21
|
-
rule :Calculate, {:priority => 2 },
|
22
|
-
'Fibonacci as :f1 where #value != -1 #&& #sequence as :s1',
|
23
|
-
'Fibonacci as :f2 where #value != -1 #&& #sequence == #:s1 + 1 as :s2',
|
24
|
-
'Fibonacci as :f3 where #value == -1 #&& #sequence == #:s2 + 1' do |e, vars|
|
25
|
-
e.retract vars[:f1]
|
26
|
-
e.retract vars[:f3]
|
27
|
-
if(vars[:f2].sequence == MAX_SEQUENCE)
|
28
|
-
e.retract vars[:f2]
|
29
|
-
else
|
30
|
-
f3 = Fibonacci.new(vars[:f2].sequence + 1, vars[:f1].value + vars[:f2].value)
|
31
|
-
e.assert f3
|
32
|
-
puts "#{f3.sequence} == #{f3.value}"
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
rule :Build, {:priority => 1},
|
37
|
-
'Fibonacci as :f1 where #value != -1 #&& #sequence as :s1',
|
38
|
-
'Fibonacci as :f2 where #value != -1 #&& #sequence == #:s1 + 1' do |e, vars|
|
39
|
-
f3 = Fibonacci.new(vars[:f2].sequence + 1, -1)
|
40
|
-
e.assert f3
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
# NOTE
|
46
|
-
# In this class we demonstrate the Ferrari DSL syntax.
|
47
|
-
class FibonacciRulebookFerrari < Rulebook
|
48
|
-
def rules
|
49
|
-
# Bootstrap1
|
50
|
-
rule :Bootstrap1, {:priority => 4},
|
51
|
-
[Fibonacci, :f, m.value == -1, m.sequence == 1 ] do |e,vars|
|
52
|
-
vars[:f].value = 1
|
53
|
-
e.modify vars[:f]
|
54
|
-
puts vars[:f].sequence.to_s + ' == ' + vars[:f].value.to_s
|
55
|
-
end
|
56
|
-
|
57
|
-
# Recurse
|
58
|
-
rule :Recurse, {:priority => 3},
|
59
|
-
[Fibonacci, :f, m.value == -1] do |e,vars|
|
60
|
-
f2 = Fibonacci.new(vars[:f].sequence - 1)
|
61
|
-
e.assert f2
|
62
|
-
puts 'recurse for ' + f2.sequence.to_s
|
63
|
-
end
|
64
|
-
|
65
|
-
# Bootstrap2
|
66
|
-
rule :Bootstrap2,
|
67
|
-
[Fibonacci, :f, m.value == -1 , m.sequence == 2] do |e,vars|
|
68
|
-
vars[:f].value = 1
|
69
|
-
e.modify vars[:f]
|
70
|
-
puts vars[:f].sequence.to_s + ' == ' + vars[:f].value.to_s
|
71
|
-
end
|
72
|
-
|
73
|
-
# Calculate
|
74
|
-
rule :Calculate,
|
75
|
-
[Fibonacci,:f1, m.value.not== -1, {m.sequence => :s1}],
|
76
|
-
[Fibonacci,:f2, m.value.not== -1, {m.sequence( :s1, &c{ |s2,s1| s2 == s1 + 1 } ) => :s2}],
|
77
|
-
[Fibonacci,:f3, m.value == -1, m.sequence(:s2, &c{ |s3,s2| s3 == s2 + 1 }) ] do |e,vars|
|
78
|
-
vars[:f3].value = vars[:f1].value + vars[:f2].value
|
79
|
-
e.modify vars[:f3]
|
80
|
-
e.retract vars[:f1]
|
81
|
-
puts vars[:f3].sequence.to_s + ' == ' + vars[:f3].value.to_s
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
@@ -1,77 +0,0 @@
|
|
1
|
-
# This file is part of the Ruleby project (http://ruleby.org)
|
2
|
-
#
|
3
|
-
# This application is free software; you can redistribute it and/or
|
4
|
-
# modify it under the terms of the Ruby license defined in the
|
5
|
-
# LICENSE.txt file.
|
6
|
-
#
|
7
|
-
# Copyright (c) 2007 Joe Kutner and Matt Smith. All rights reserved.
|
8
|
-
#
|
9
|
-
# * Authors: Joe Kutner, Matt Smith
|
10
|
-
#
|
11
|
-
|
12
|
-
$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib/')
|
13
|
-
require 'ruleby'
|
14
|
-
|
15
|
-
include Ruleby
|
16
|
-
|
17
|
-
class Message
|
18
|
-
def initialize(status,message)
|
19
|
-
@status = status
|
20
|
-
@message = message
|
21
|
-
end
|
22
|
-
attr :status, true
|
23
|
-
attr :message, true
|
24
|
-
end
|
25
|
-
|
26
|
-
class SelfRefRulebook < Rulebook
|
27
|
-
def rules
|
28
|
-
rule :LeTigreTest,
|
29
|
-
'For each Message as :m where #message as :x #&& #status == #:x' do |r,v|
|
30
|
-
puts 'Success'
|
31
|
-
end
|
32
|
-
|
33
|
-
rule :LeTigreTest,
|
34
|
-
[Message, :m, {m.message => :x}, m.status == b(:x)] do |r,v|
|
35
|
-
puts 'Success'
|
36
|
-
end
|
37
|
-
|
38
|
-
rule :BlueSteelTest do |r|
|
39
|
-
r.when do |has|
|
40
|
-
has.m Message
|
41
|
-
has.m.message :x
|
42
|
-
has.m.status = :x, :%
|
43
|
-
end
|
44
|
-
r.then do |e,vars|
|
45
|
-
puts 'Success'
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# NOTE references the self class binding is not allowed yet
|
50
|
-
|
51
|
-
# rule 'LeTigreTest',
|
52
|
-
# 'exists? Message as :m where #status == #:m.message' do |r,v|
|
53
|
-
# puts 'Success'
|
54
|
-
# end
|
55
|
-
|
56
|
-
# rule 'LeTigreTest',
|
57
|
-
# [Message, :m, m.status(:m, &c{|s,m| s == m.message})] do |r,v|
|
58
|
-
# puts 'Success'
|
59
|
-
# end
|
60
|
-
|
61
|
-
# rule :BlueSteelTest do |r|
|
62
|
-
# r.when do |has|
|
63
|
-
# has.m Message
|
64
|
-
# has.m.status :m do |s,m| s == m.message end
|
65
|
-
# end
|
66
|
-
# r.then do |e,vars|
|
67
|
-
# puts 'Success'
|
68
|
-
# end
|
69
|
-
# end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
engine :engine do |e|
|
74
|
-
SelfRefRulebook.new(e).rules
|
75
|
-
e.assert Message.new(:HELLO, :HELLO)
|
76
|
-
e.match
|
77
|
-
end
|