ruleby 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/benchmarks/50_joined_rules.rb +78 -0
- data/benchmarks/50_rules.rb +57 -0
- data/benchmarks/5_joined_rules.rb +78 -0
- data/benchmarks/5_rules.rb +57 -0
- data/benchmarks/miss_manners/data.rb +135 -0
- data/benchmarks/miss_manners/miss_manners.rb +23 -0
- data/benchmarks/miss_manners/model.rb +182 -0
- data/benchmarks/miss_manners/rules.rb +165 -0
- data/examples/example_diagnosis.rb +155 -0
- data/examples/example_hello.rb +48 -0
- data/examples/example_politician.rb +86 -0
- data/examples/example_ticket.rb +158 -0
- data/examples/fibonacci_example1.rb +33 -0
- data/examples/fibonacci_example2.rb +29 -0
- data/examples/fibonacci_rulebook.rb +137 -0
- data/examples/test_self_reference.rb +35 -0
- data/lib/core/atoms.rb +13 -71
- data/lib/core/engine.rb +47 -9
- data/lib/core/nodes.rb +487 -548
- data/lib/core/patterns.rb +0 -110
- data/lib/core/utils.rb +120 -184
- data/lib/rulebook.rb +237 -157
- data/lib/ruleby.rb +0 -20
- metadata +28 -11
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'model'
|
2
|
+
|
3
|
+
module MissManners
|
4
|
+
|
5
|
+
class MannersRulebook < Rulebook
|
6
|
+
def rules
|
7
|
+
rule 'assignFirstSeat' do |r|
|
8
|
+
r.when do |has|
|
9
|
+
has.context(Context).&.state = Context::START_UP
|
10
|
+
has.guest Guest
|
11
|
+
has.count Count
|
12
|
+
end
|
13
|
+
|
14
|
+
r.then = action do |e,vars|
|
15
|
+
guestName = vars[:guest].name
|
16
|
+
|
17
|
+
seating = Seating.new(vars[:count].value,1,true,1,guestName,1,guestName)
|
18
|
+
e.assert seating
|
19
|
+
|
20
|
+
path = Path.new(vars[:count].value, 1, guestName)
|
21
|
+
e.assert path
|
22
|
+
|
23
|
+
e.retract vars[:count]
|
24
|
+
vars[:count].value = vars[:count].value + 1
|
25
|
+
e.assert vars[:count]
|
26
|
+
|
27
|
+
puts "assign first seat : #{seating.to_s} : #{path.to_s}"
|
28
|
+
|
29
|
+
vars[:context].state = Context::ASSIGN_SEATS
|
30
|
+
e.modify vars[:context]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
rule 'findSeating' do |r|
|
35
|
+
r.when do |has|
|
36
|
+
has.context(Context).&.state(:state).&.state = Context::ASSIGN_SEATS
|
37
|
+
|
38
|
+
has.count(Count).&.value:countValue
|
39
|
+
|
40
|
+
has.s(Seating).&.id(:seatingId).&.pid(:seatingPid).&.rightSeat(:seatingRightSeat).&.rightGuestName:seatingRightGuestName
|
41
|
+
has.s.path(:p).&.path = true
|
42
|
+
|
43
|
+
has.g(Guest).&.name(:name).&.sex(:rightGuestSex).&.hobby(:rightGuestHobby)
|
44
|
+
has.g.name = :seatingRightGuestName, :%
|
45
|
+
|
46
|
+
has.lg(Guest).&.name(:leftGuestName).&.sex:sex
|
47
|
+
has.lg.hobby(:hobby).&.hobby = :rightGuestHobby, :%
|
48
|
+
has.lg.sex :rightGuestSex do |s,rgs| s != rgs end
|
49
|
+
|
50
|
+
has.not.path(Path).&.id = :seatingId, :%
|
51
|
+
has.path.guestName = :leftGuestName, :%
|
52
|
+
|
53
|
+
has.not.chosen Chosen
|
54
|
+
has.chosen.id = :seatingId, :%
|
55
|
+
has.chosen.guestName = :leftGuestName, :%
|
56
|
+
has.chosen.hobby = :leftGuestHobby, :%
|
57
|
+
end
|
58
|
+
|
59
|
+
r.then = action do |e,vars|
|
60
|
+
rightSeat = vars[:seatingRightSeat]
|
61
|
+
seatId = vars[:seatingId]
|
62
|
+
countValue = vars[:count].value
|
63
|
+
|
64
|
+
seating = Seating.new(countValue,seatId,false,rightSeat,vars[:seatingRightGuestName],rightSeat+1,vars[:leftGuestName])
|
65
|
+
path = Path.new(countValue,rightSeat+1,vars[:leftGuestName])
|
66
|
+
chosen = Chosen.new(seatId, vars[:leftGuestName], vars[:rightGuestHobby] )
|
67
|
+
|
68
|
+
puts "find seating : #{seating} : #{path} : #{chosen}"
|
69
|
+
|
70
|
+
e.assert seating
|
71
|
+
e.assert path
|
72
|
+
e.assert chosen
|
73
|
+
|
74
|
+
vars[:count].value = countValue + 1
|
75
|
+
e.modify vars[:count]
|
76
|
+
|
77
|
+
vars[:context].state = Context::MAKE_PATH
|
78
|
+
e.modify vars[:context]
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
rule 'makePath' do |r|
|
84
|
+
r.when do |has|
|
85
|
+
has.context(Context).&.state(:s).&.state = Context::MAKE_PATH
|
86
|
+
|
87
|
+
has.seating(Seating).&.id(:seatingId).&.pid:seatingPid
|
88
|
+
has.seating.path(:p).&.path = false
|
89
|
+
|
90
|
+
has.path(Path).&.guestName(:pathGuestName).&.seat(:pathSeat)
|
91
|
+
has.path.id = :seatingPid, :%
|
92
|
+
|
93
|
+
has.not.path2 Path
|
94
|
+
has.path2.id = :seatingId, :%
|
95
|
+
has.path2.guestName = :pathGuestName, :%
|
96
|
+
end
|
97
|
+
|
98
|
+
r.then = action do |e,vars|
|
99
|
+
path = Path.new(vars[:seatingId],vars[:pathSeat],vars[:pathGuestName])
|
100
|
+
e.assert path
|
101
|
+
puts "make Path : #{path}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
rule 'pathDone' do |r|
|
106
|
+
r.when do |has|
|
107
|
+
has.context(Context).&.state = Context::MAKE_PATH
|
108
|
+
has.seating(Seating).&.path = false
|
109
|
+
end
|
110
|
+
|
111
|
+
r.then = action do |e,vars|
|
112
|
+
vars[:seating].path = true
|
113
|
+
e.modify vars[:seating]
|
114
|
+
|
115
|
+
vars[:context].state = Context::CHECK_DONE
|
116
|
+
e.modify vars[:context]
|
117
|
+
puts "path Done : #{vars[:seating]}"
|
118
|
+
end
|
119
|
+
|
120
|
+
# HACK We had to add this because Ruleby's conflict resolution strategy
|
121
|
+
# is not robust enough. If it worked correctly, this priority would not
|
122
|
+
# be nessecary because the 'make path' activations would have more
|
123
|
+
# recent facts supporting it.
|
124
|
+
r.priority = -5
|
125
|
+
end
|
126
|
+
|
127
|
+
rule 'areWeDone' do |r|
|
128
|
+
r.when do |has|
|
129
|
+
has.context(Context).&.state = Context::CHECK_DONE
|
130
|
+
has.ls(LastSeat).&.seat:lastSeat
|
131
|
+
has.seating(Seating).&.rightSeat = :lastSeat, :%
|
132
|
+
end
|
133
|
+
|
134
|
+
r.then = action do |e,vars|
|
135
|
+
vars[:context].state = Context::PRINT_RESULTS
|
136
|
+
e.modify vars[:context]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
rule 'continue' do |r|
|
141
|
+
r.when do |has|
|
142
|
+
has.context Context
|
143
|
+
has.context.state = Context::CHECK_DONE
|
144
|
+
end
|
145
|
+
r.then = action do |e,vars|
|
146
|
+
vars[:context].state = Context::ASSIGN_SEATS
|
147
|
+
e.modify vars[:context]
|
148
|
+
end
|
149
|
+
r.priority = -5
|
150
|
+
end
|
151
|
+
|
152
|
+
rule 'allDone' do |r|
|
153
|
+
r.when do |has|
|
154
|
+
has.c Context
|
155
|
+
has.c.state = Context::PRINT_RESULTS
|
156
|
+
end
|
157
|
+
|
158
|
+
r.then = action do |e,vars|
|
159
|
+
puts 'All done'
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib/')
|
2
|
+
require 'ruleby'
|
3
|
+
require 'rulebook'
|
4
|
+
|
5
|
+
class Patient
|
6
|
+
def initialize(name,fever,spots,rash,sore_throat,innoculated)
|
7
|
+
@name = name
|
8
|
+
@fever = fever
|
9
|
+
@spots= spots
|
10
|
+
@rash = rash
|
11
|
+
@sore_throat = sore_throat
|
12
|
+
@innoculated = innoculated
|
13
|
+
end
|
14
|
+
attr:name, true
|
15
|
+
attr:fever, true
|
16
|
+
attr:spots, true
|
17
|
+
attr:rash, true
|
18
|
+
attr:sore_throat, true
|
19
|
+
attr:innoculated, true
|
20
|
+
end
|
21
|
+
|
22
|
+
class Diagnosis
|
23
|
+
def initialize(name,diagnosis)
|
24
|
+
@name=name
|
25
|
+
@diagnosis=diagnosis
|
26
|
+
end
|
27
|
+
attr:name,true
|
28
|
+
attr:diagnosis,true
|
29
|
+
end
|
30
|
+
|
31
|
+
class Treatment
|
32
|
+
def initialize(name,treatment)
|
33
|
+
@name=name
|
34
|
+
@treatment=treatment
|
35
|
+
end
|
36
|
+
attr:name,true
|
37
|
+
attr:treatment,true
|
38
|
+
end
|
39
|
+
|
40
|
+
class DiagnosisRulebook < Rulebook
|
41
|
+
def rules
|
42
|
+
rule 'Measles' do |r|
|
43
|
+
r.when do |exists|
|
44
|
+
exists.p(Patient).&.name:n
|
45
|
+
exists.p.fever=:high
|
46
|
+
exists.p.spots=true
|
47
|
+
exists.p.innoculated=true
|
48
|
+
end
|
49
|
+
r.then = action do |e,vars|
|
50
|
+
name = vars[:n]
|
51
|
+
e.assert Diagnosis.new(name, :measles)
|
52
|
+
puts "Measles diagnosed for #{name}"
|
53
|
+
end
|
54
|
+
r.priority = 100
|
55
|
+
end
|
56
|
+
|
57
|
+
rule 'Allergy1' do |r|
|
58
|
+
r.when do |exists|
|
59
|
+
exists.p Patient
|
60
|
+
exists.p.name:n
|
61
|
+
exists.p.spots=true
|
62
|
+
|
63
|
+
exists.not.d Diagnosis
|
64
|
+
exists.d.name = :n, :%
|
65
|
+
exists.d.diagnosis = :measles
|
66
|
+
end
|
67
|
+
r.then = action do |e,vars|
|
68
|
+
name = vars[:n]
|
69
|
+
e.assert Diagnosis.new(name, :allergy)
|
70
|
+
puts "Allergy diagnosed for #{name} from spots and lack of measles"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
rule 'Allergy2' do |r|
|
75
|
+
r.when do |exists|
|
76
|
+
exists.p Patient
|
77
|
+
exists.p.name :n2
|
78
|
+
exists.p.rash = true
|
79
|
+
end
|
80
|
+
r.then = action do |e,vars|
|
81
|
+
name = vars[:n2]
|
82
|
+
e.assert Diagnosis.new(name, :allergy)
|
83
|
+
puts "Allergy diagnosed from rash for #{name}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
rule 'Flu' do |r|
|
88
|
+
r.when do |exists|
|
89
|
+
exists.p Patient
|
90
|
+
exists.p.name :n3
|
91
|
+
exists.p.sore_throat=true
|
92
|
+
exists.p.fever do |f| f == :mild || f == :high end
|
93
|
+
end
|
94
|
+
r.then = action do |e,vars|
|
95
|
+
name = vars[:n3]
|
96
|
+
e.assert Diagnosis.new(name, :flu)
|
97
|
+
puts "Flu diagnosed for #{name}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
rule 'Penicillin' do |r|
|
102
|
+
r.when do |exists|
|
103
|
+
exists.d Diagnosis
|
104
|
+
exists.d.name :n4
|
105
|
+
exists.d.diagnosis = :measles
|
106
|
+
end
|
107
|
+
r.then = action do |e,vars|
|
108
|
+
name = vars[:n4]
|
109
|
+
e.assert Treatment.new(name, :penicillin)
|
110
|
+
puts "Penicillin prescribed for #{name}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
rule 'Allergy_pills' do |r|
|
115
|
+
r.when do |exists|
|
116
|
+
exists.d Diagnosis
|
117
|
+
exists.d.name :n5
|
118
|
+
exists.d.diagnosis = :allergy
|
119
|
+
end
|
120
|
+
r.then = action do |e,vars|
|
121
|
+
name = vars[:n5]
|
122
|
+
e.assert Treatment.new(name, :allergy_shot)
|
123
|
+
puts "Allergy shot prescribed for #{name}"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
rule 'Bed_rest' do |r|
|
128
|
+
r.when do |exists|
|
129
|
+
exists.d Diagnosis
|
130
|
+
exists.d.name :n6
|
131
|
+
exists.d.diagnosis = :flu
|
132
|
+
end
|
133
|
+
r.then = action do |e,vars|
|
134
|
+
name = vars[:n6]
|
135
|
+
e.assert Treatment.new(name, :bed_rest)
|
136
|
+
puts "Bed rest prescribed for #{name}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
include Ruleby
|
143
|
+
|
144
|
+
engine :engine do |e|
|
145
|
+
|
146
|
+
DiagnosisRulebook.new(e).rules
|
147
|
+
|
148
|
+
e.assert Patient.new('Fred',:none,true,false,false,false)
|
149
|
+
e.assert Patient.new('Joe',:high,false,false,true,false)
|
150
|
+
e.assert Patient.new('Bob',:high,true,false,false,true)
|
151
|
+
e.assert Patient.new('Tom',:none,false,true,false,false)
|
152
|
+
|
153
|
+
e.match
|
154
|
+
|
155
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib/')
|
2
|
+
require 'ruleby'
|
3
|
+
require 'rulebook'
|
4
|
+
|
5
|
+
class Message
|
6
|
+
def initialize(status,message)
|
7
|
+
@status = status
|
8
|
+
@message = message
|
9
|
+
end
|
10
|
+
attr :status, true
|
11
|
+
attr :message, true
|
12
|
+
end
|
13
|
+
|
14
|
+
class HelloWorldRulebook < Rulebook
|
15
|
+
def rules
|
16
|
+
rule 'HelloWorld' do |r|
|
17
|
+
r.when do |has|
|
18
|
+
has.m Message
|
19
|
+
has.m.status = :HELLO
|
20
|
+
end
|
21
|
+
r.then = action do |e,vars|
|
22
|
+
puts vars[:m].message
|
23
|
+
e.retract vars[:m]
|
24
|
+
vars[:m].message = "Goodbye world"
|
25
|
+
vars[:m].status = :GOODBYE
|
26
|
+
e.assert vars[:m]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
rule 'Goodbye' do |r|
|
31
|
+
r.when do |has|
|
32
|
+
has.m Message
|
33
|
+
has.m.status = :GOODBYE
|
34
|
+
end
|
35
|
+
r.then = action do |e,vars|
|
36
|
+
puts vars[:m].message
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
include Ruleby
|
43
|
+
|
44
|
+
engine :engine do |e|
|
45
|
+
HelloWorldRulebook.new(e).rules
|
46
|
+
e.assert Message.new(:HELLO, 'Hello World')
|
47
|
+
e.match
|
48
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib/')
|
2
|
+
require 'ruleby'
|
3
|
+
require 'rulebook'
|
4
|
+
|
5
|
+
class Politician
|
6
|
+
def initialize(name,honest)
|
7
|
+
@name = name
|
8
|
+
@honest = honest
|
9
|
+
end
|
10
|
+
attr_accessor :name, :honest
|
11
|
+
def to_s
|
12
|
+
return "[#{@name.to_s},#{@honest.to_s}]"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Hope
|
17
|
+
end
|
18
|
+
|
19
|
+
class PoliticianRulebook < Rulebook
|
20
|
+
def rules
|
21
|
+
rule 'We have an honest Politician' do |r|
|
22
|
+
r.when do |has|
|
23
|
+
has.p Politician
|
24
|
+
has.p.honest = true
|
25
|
+
end
|
26
|
+
r.then = action do |e,vars|
|
27
|
+
e.assert Hope.new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
rule 'Hope Lives' do |r|
|
32
|
+
r.when do |has|
|
33
|
+
has.h Hope
|
34
|
+
end
|
35
|
+
r.then = action do |e,vars|
|
36
|
+
puts 'Hurrah!!! Democracy Lives'
|
37
|
+
end
|
38
|
+
r.priority = 10
|
39
|
+
end
|
40
|
+
|
41
|
+
# This example was taken from the JBoss-Rules project. That example
|
42
|
+
# demonstrates a feature that Ruleby does not support: logical assertions.
|
43
|
+
# The JBoss-Rules example, the following rule is fired as the last
|
44
|
+
# activation (because Hope is retracted during fact maintenence).
|
45
|
+
# rule do |r|
|
46
|
+
# r.when = pattern do |p|
|
47
|
+
# p.not :h, Hope
|
48
|
+
# end
|
49
|
+
# r.then = action 'Hope is Dead' do |r,vars|
|
50
|
+
# puts 'We are all Doomed!!! Democracy is Dead'
|
51
|
+
# end
|
52
|
+
# r.priority = -1
|
53
|
+
# end
|
54
|
+
|
55
|
+
rule 'Corrupt the Honest' do |r|
|
56
|
+
r.when do |has|
|
57
|
+
has.p Politician
|
58
|
+
has.p.honest = true
|
59
|
+
|
60
|
+
has.h Hope
|
61
|
+
end
|
62
|
+
r.then = action do |e,vars|
|
63
|
+
puts 'I am an evil corporation and I have corrupted ' + vars[:p].name
|
64
|
+
vars[:p].honest = false
|
65
|
+
e.modify vars[:p]
|
66
|
+
end
|
67
|
+
r.priority = 5
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
include Ruleby
|
73
|
+
|
74
|
+
p1 = Politician.new('blair', true)
|
75
|
+
p2 = Politician.new('bush', true)
|
76
|
+
p3 = Politician.new('chirac', true)
|
77
|
+
p4 = Politician.new('schroder', true)
|
78
|
+
|
79
|
+
engine :engine do |e|
|
80
|
+
PoliticianRulebook.new(e).rules
|
81
|
+
e.assert p1
|
82
|
+
e.assert p2
|
83
|
+
e.assert p3
|
84
|
+
e.assert p4
|
85
|
+
e.match
|
86
|
+
end
|