ruleby 0.2 → 0.3

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.
@@ -0,0 +1,66 @@
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 'model'
15
+
16
+ include Ruleby
17
+
18
+ class TestRulebook < Rulebook
19
+ def rules(n)
20
+ (0..n).each do |index|
21
+ rule "Rule-#{index}".to_sym do |r|
22
+ r.when do |has|
23
+ has.a Account
24
+ has.a.status = 'standard'
25
+ has.a.title = 'mr'
26
+ has.a.account_id = "acc#{index}"
27
+ end
28
+
29
+ r.then do |e,vars|
30
+ # puts "rule #{index} fired"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ def run_benchmark(rules, facts)
38
+ puts "running benchmark for: #{rules} rules and #{facts} facts"
39
+
40
+ t1 = Time.new
41
+ engine :engine do |e|
42
+ TestRulebook.new(e).rules(rules)
43
+
44
+ t2 = Time.new
45
+ diff = t2.to_f - t1.to_f
46
+ puts 'time to create rule set: ' + diff.to_s
47
+ for k in (0..facts)
48
+ e.assert Account.new('standard', 'mr', ('acc'+k.to_s))
49
+ end
50
+
51
+ t3 = Time.new
52
+ diff = t3.to_f - t2.to_f
53
+ puts 'time to assert facts: ' + diff.to_s
54
+ e.match
55
+
56
+ t4 = Time.new
57
+ diff = t4.to_f - t3.to_f
58
+ puts 'time to run agenda: ' + diff.to_s
59
+ end
60
+ end
61
+
62
+ run_benchmark(5, 500)
63
+ puts '-------'
64
+ run_benchmark(50, 500)
65
+ puts '-------'
66
+ run_benchmark(500, 500)
@@ -0,0 +1,73 @@
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 'model'
15
+
16
+ include Ruleby
17
+
18
+ class TestRulebook < Rulebook
19
+ def rules(n)
20
+ (0..n).each do |index|
21
+ rule "Rule-#{index}".to_sym do |r|
22
+ r.when do |has|
23
+ has.a Account
24
+ has.a.status = 'standard'
25
+ has.a.account_id :id
26
+
27
+ has.addr Address
28
+ has.addr.addr_id = :id, :%
29
+ has.addr.city = 'Foobar'
30
+ has.addr.state = 'FB'
31
+ has.addr.zip = '12345'
32
+ end
33
+
34
+ r.then do |e,vars|
35
+ # puts "rule #{index} fired"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ def run_benchmark(rules,facts)
43
+ puts "running benchmark for: #{rules} rules and #{facts} facts"
44
+
45
+ t1 = Time.new
46
+ engine :engine do |e|
47
+ TestRulebook.new(e).rules(rules)
48
+
49
+ t2 = Time.new
50
+ diff = t2.to_f - t1.to_f
51
+ puts 'time to create rule set: ' + diff.to_s
52
+
53
+
54
+ for k in (0..facts)
55
+ e.assert Account.new('standard', nil, "acc#{k}")
56
+ e.assert Address.new("acc#{k}",'Foobar', 'FB', '12345')
57
+ end
58
+
59
+ t3 = Time.new
60
+ diff = t3.to_f - t2.to_f
61
+ puts 'time to assert facts: ' + diff.to_s
62
+
63
+ e.match
64
+
65
+ t4 = Time.new
66
+ diff = t4.to_f - t3.to_f
67
+ puts 'time to run agenda: ' + diff.to_s
68
+ end
69
+ end
70
+
71
+ run_benchmark(5, 50)
72
+ puts '-------'
73
+ run_benchmark(50, 50)
@@ -1,3 +1,14 @@
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
+
1
12
  module MissManners
2
13
  class MannersData
3
14
  def initialize
@@ -1,6 +1,16 @@
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
+
1
12
  $LOAD_PATH << File.join(File.dirname(__FILE__), '../../lib/')
2
13
  require 'ruleby'
3
- require 'rulebook'
4
14
  require 'model'
5
15
  require 'data'
6
16
  require 'rules'
@@ -1,3 +1,14 @@
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
+
1
12
  module MissManners
2
13
 
3
14
  class Chosen
@@ -1,163 +1,102 @@
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
+
1
12
  require 'model'
2
13
 
3
14
  module MissManners
4
15
 
5
- class MannersRulebook < Rulebook
16
+ class MannersRulebook < Ruleby::Rulebook
6
17
  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
-
18
+ rule :assignFirstSeat,
19
+ [Context,:context, m.state == Context::START_UP],
20
+ [Guest,:guest],
21
+ [Count,:count] do |e,vars|
22
+ guestName = vars[:guest].name
17
23
  seating = Seating.new(vars[:count].value,1,true,1,guestName,1,guestName)
18
- e.assert seating
19
-
24
+ e.assert seating
20
25
  path = Path.new(vars[:count].value, 1, guestName)
21
26
  e.assert path
22
-
23
27
  e.retract vars[:count]
24
28
  vars[:count].value = vars[:count].value + 1
25
29
  e.assert vars[:count]
26
-
27
30
  puts "assign first seat : #{seating.to_s} : #{path.to_s}"
28
-
29
31
  vars[:context].state = Context::ASSIGN_SEATS
30
32
  e.modify vars[:context]
31
- end
32
33
  end
33
34
 
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|
35
+ rule :findSeating,
36
+ [Context,:context, {m.state == Context::ASSIGN_SEATS => :state}],
37
+ [Count,:count, {m.value => :countValue}],
38
+ [Seating,:seating, {m.path == true =>:p, m.id=>:seatingId, m.pid=>:seatingPid, m.rightSeat=>:seatingRightSeat, m.rightGuestName=>:seatingRightGuestName}],
39
+ [Guest,:g, {m.name=>:name, m.sex=>:rightGuestSex, m.hobby=>:rightGuestHobby}, m.name == b(:seatingRightGuestName)],
40
+ [Guest,:lg, {m.name=>:leftGuestName, m.sex=>:sex, m.hobby == b(:rightGuestHobby) => :hobby}, m.sex(:rightGuestSex, &c{|s,rgs| s != rgs} )],
41
+ [:~,Path, m.id == b(:seatingId), m.guestName == b(:leftGuestName)],
42
+ [:~,Chosen, m.id == b(:seatingId), m.guestName == b(:leftGuestName), m.hobby == b(:leftGuestHobby)] do |e,vars|
60
43
  rightSeat = vars[:seatingRightSeat]
61
44
  seatId = vars[:seatingId]
62
- countValue = vars[:count].value
63
-
45
+ countValue = vars[:count].value
64
46
  seating = Seating.new(countValue,seatId,false,rightSeat,vars[:seatingRightGuestName],rightSeat+1,vars[:leftGuestName])
65
47
  path = Path.new(countValue,rightSeat+1,vars[:leftGuestName])
66
- chosen = Chosen.new(seatId, vars[:leftGuestName], vars[:rightGuestHobby] )
67
-
48
+ chosen = Chosen.new(seatId, vars[:leftGuestName], vars[:rightGuestHobby] )
68
49
  puts "find seating : #{seating} : #{path} : #{chosen}"
69
-
70
50
  e.assert seating
71
51
  e.assert path
72
52
  e.assert chosen
73
-
74
53
  vars[:count].value = countValue + 1
75
54
  e.modify vars[:count]
76
-
77
55
  vars[:context].state = Context::MAKE_PATH
78
56
  e.modify vars[:context]
79
- end
80
-
81
57
  end
82
58
 
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|
59
+ rule :makePath,
60
+ [Context,:context, {m.state == Context::MAKE_PATH => :s}],
61
+ [Seating,:seating, {m.id=>:seatingId, m.pid=>:seatingPid, m.path == false =>:p}],
62
+ [Path,:path, {m.guestName=>:pathGuestName, m.seat=>:pathSeat}, m.id == b(:seatingPid)],
63
+ [:~,Path,m.id == b(:seatingId), m.guestName == b(:pathGuestName)] do |e,vars|
99
64
  path = Path.new(vars[:seatingId],vars[:pathSeat],vars[:pathGuestName])
100
65
  e.assert path
101
66
  puts "make Path : #{path}"
102
- end
103
67
  end
104
68
 
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|
69
+ # NOTE We had to add this because Ruleby's conflict resolution strategy
70
+ # is not robust enough. If it worked correctly, this priority would not
71
+ # be nessecary because the 'make path' activations would have more
72
+ # recent facts supporting it.
73
+ rule :pathDone, {:priority => -5},
74
+ [Context,:context, m.state == Context::MAKE_PATH],
75
+ [Seating,:seating, m.path == false] do |e,vars|
112
76
  vars[:seating].path = true
113
- e.modify vars[:seating]
114
-
77
+ e.modify vars[:seating]
115
78
  vars[:context].state = Context::CHECK_DONE
116
79
  e.modify vars[:context]
117
80
  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
81
  end
126
82
 
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|
83
+ rule :areWeDone,
84
+ [Context,:context, m.state == Context::CHECK_DONE],
85
+ [LastSeat,:ls, {m.seat => :lastSeat}],
86
+ [Seating,:seating, m.rightSeat == b(:lastSeat)] do |e,vars|
135
87
  vars[:context].state = Context::PRINT_RESULTS
136
88
  e.modify vars[:context]
137
- end
138
89
  end
139
90
 
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|
91
+ rule :continue, {:priority => -5},
92
+ [Context,:context,m.state == Context::CHECK_DONE] do |e,vars|
146
93
  vars[:context].state = Context::ASSIGN_SEATS
147
94
  e.modify vars[:context]
148
- end
149
- r.priority = -5
150
95
  end
151
96
 
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|
97
+ rule :allDone,
98
+ [Context,:context,m.state == Context::PRINT_RESULTS] do |e,vars|
159
99
  puts 'All done'
160
- end
161
100
  end
162
101
  end
163
102
  end
@@ -0,0 +1,36 @@
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
+ class Account
13
+ def initialize(status, title, account_id)
14
+ @status = status
15
+ @title = title
16
+ @account_id = account_id
17
+ end
18
+
19
+ attr :status, true
20
+ attr :title, true
21
+ attr :account_id, true
22
+ end
23
+
24
+ class Address
25
+ def initialize(addr_id, city, state, zip)
26
+ @addr_id = addr_id
27
+ @city = city
28
+ @state = state
29
+ @zip = zip
30
+ end
31
+
32
+ attr :addr_id, true
33
+ attr :city, true
34
+ attr :state, true
35
+ attr :zip, true
36
+ end
@@ -1,6 +1,16 @@
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
+
1
12
  $LOAD_PATH << File.join(File.dirname(__FILE__), '../lib/')
2
13
  require 'ruleby'
3
- require 'rulebook'
4
14
 
5
15
  class Patient
6
16
  def initialize(name,fever,spots,rash,sore_throat,innoculated)
@@ -37,104 +47,56 @@ class Treatment
37
47
  attr:treatment,true
38
48
  end
39
49
 
40
- class DiagnosisRulebook < Rulebook
50
+ class DiagnosisRulebook < Ruleby::Rulebook
41
51
  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]
52
+ rule :Measles, {:priority => 100},
53
+ [Patient,:p,{m.name=>:n},m.fever==:high,m.spots==true,m.innoculated==true] do |e,v|
54
+ name = v[:n]
51
55
  e.assert Diagnosis.new(name, :measles)
52
- puts "Measles diagnosed for #{name}"
53
- end
54
- r.priority = 100
56
+ puts "Measles diagnosed for #{name}"
55
57
  end
56
58
 
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]
59
+ rule :Allergy1,
60
+ [Patient,:p, {m.name=>:n}, m.spots==true],
61
+ [:not, Diagnosis, m.name==b(:n), m.diagnosis==:measles] do |e,v|
62
+ name = v[:n]
69
63
  e.assert Diagnosis.new(name, :allergy)
70
64
  puts "Allergy diagnosed for #{name} from spots and lack of measles"
71
- end
72
65
  end
73
66
 
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]
67
+ rule :Allergy2,
68
+ [Patient,:p, {m.name=>:n}, m.rash==true] do |e,v|
69
+ name = v[:n]
82
70
  e.assert Diagnosis.new(name, :allergy)
83
71
  puts "Allergy diagnosed from rash for #{name}"
84
- end
85
72
  end
86
73
 
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]
74
+ rule :Flu,
75
+ [Patient,:p, {m.name=>:n}, m.sore_throat==true, m.fever(&c{|f| f==:mild || f==:high})] do |e,v|
76
+ name = v[:n]
96
77
  e.assert Diagnosis.new(name, :flu)
97
78
  puts "Flu diagnosed for #{name}"
98
- end
99
79
  end
100
80
 
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]
81
+ rule :Penicillin,
82
+ [Diagnosis, :d, {m.name => :n}, m.diagnosis==:measles] do |e,v|
83
+ name = v[:n]
109
84
  e.assert Treatment.new(name, :penicillin)
110
85
  puts "Penicillin prescribed for #{name}"
111
- end
112
86
  end
113
87
 
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]
88
+ rule :Allergy_pills,
89
+ [Diagnosis, :d, {m.name => :n}, m.diagnosis==:allergy] do |e,v|
90
+ name = v[:n]
122
91
  e.assert Treatment.new(name, :allergy_shot)
123
92
  puts "Allergy shot prescribed for #{name}"
124
- end
125
93
  end
126
94
 
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]
95
+ rule :Bed_rest,
96
+ [Diagnosis, :d, {m.name => :n}, m.diagnosis==:flu] do |e,v|
97
+ name = v[:n]
135
98
  e.assert Treatment.new(name, :bed_rest)
136
99
  puts "Bed rest prescribed for #{name}"
137
- end
138
100
  end
139
101
  end
140
102
  end