ruleby 0.1 → 0.2
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/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
|