ruleby 0.9.b4 → 0.9.b7
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/GPL.txt +341 -0
- data/LICENSE.txt +52 -0
- data/README.markdown +22 -0
- data/benchmarks/basic_rules.rb +61 -0
- data/benchmarks/joined_rules.rb +66 -0
- data/benchmarks/miss_manners/data.rb +146 -0
- data/benchmarks/miss_manners/miss_manners.rb +33 -0
- data/benchmarks/miss_manners/model.rb +193 -0
- data/benchmarks/miss_manners/rules.rb +105 -0
- data/benchmarks/model.rb +36 -0
- data/examples/diagnosis.rb +129 -0
- data/examples/fibonacci_example1.rb +44 -0
- data/examples/fibonacci_example2.rb +40 -0
- data/examples/fibonacci_example3.rb +78 -0
- data/examples/fibonacci_rulebook.rb +84 -0
- data/examples/hello.rb +45 -0
- data/examples/ticket.rb +113 -0
- data/examples/wordgame.rb +107 -0
- data/lib/core/engine.rb +26 -24
- data/lib/core/nodes.rb +77 -35
- data/lib/ruleby.rb +0 -37
- data/ruleby.gemspec +17 -0
- data/spec/and_or_spec.rb +252 -0
- data/spec/coercion_spec.rb +5 -0
- data/spec/collect_spec.rb +1021 -0
- data/spec/errors_spec.rb +148 -0
- data/spec/ferrari_spec.rb +39 -0
- data/spec/function_spec.rb +199 -0
- data/spec/hello_spec.rb +34 -0
- data/spec/node_sharing_spec.rb +53 -0
- data/spec/property_spec.rb +69 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +9 -0
- data/tasks/documentation.rake +32 -0
- data/tasks/rspec.rake +21 -0
- data/tasks/test.rake +9 -0
- data/tests/assert_facts.rb +130 -0
- data/tests/common.rb +29 -0
- data/tests/duck_type.rb +79 -0
- data/tests/gets.rb +48 -0
- data/tests/join_nodes.rb +63 -0
- data/tests/nil.rb +72 -0
- data/tests/not_patterns.rb +91 -0
- data/tests/or_patterns.rb +154 -0
- data/tests/regex.rb +47 -0
- data/tests/self_reference.rb +54 -0
- metadata +81 -49
data/benchmarks/model.rb
ADDED
@@ -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
|
@@ -0,0 +1,129 @@
|
|
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
|
+
class Patient
|
16
|
+
def initialize(name,fever,spots,rash,sore_throat,innoculated)
|
17
|
+
@name = name
|
18
|
+
@fever = fever
|
19
|
+
@spots= spots
|
20
|
+
@rash = rash
|
21
|
+
@sore_throat = sore_throat
|
22
|
+
@innoculated = innoculated
|
23
|
+
end
|
24
|
+
attr:name, true
|
25
|
+
attr:fever, true
|
26
|
+
attr:spots, true
|
27
|
+
attr:rash, true
|
28
|
+
attr:sore_throat, true
|
29
|
+
attr:innoculated, true
|
30
|
+
end
|
31
|
+
|
32
|
+
class Diagnosis
|
33
|
+
def initialize(name,diagnosis)
|
34
|
+
@name=name
|
35
|
+
@diagnosis=diagnosis
|
36
|
+
end
|
37
|
+
attr:name,true
|
38
|
+
attr:diagnosis,true
|
39
|
+
end
|
40
|
+
|
41
|
+
class Treatment
|
42
|
+
def initialize(name,treatment)
|
43
|
+
@name=name
|
44
|
+
@treatment=treatment
|
45
|
+
end
|
46
|
+
attr:name,true
|
47
|
+
attr:treatment,true
|
48
|
+
end
|
49
|
+
|
50
|
+
class DiagnosisRulebook < Ruleby::Rulebook
|
51
|
+
def rules
|
52
|
+
rule :Measles, {:priority => 100},
|
53
|
+
[Patient,:p,{m.name=>:n},m.fever==:high,m.spots==true,m.innoculated==true] do |v|
|
54
|
+
name = v[:n]
|
55
|
+
assert Diagnosis.new(name, :measles)
|
56
|
+
puts "Measles diagnosed for #{name}"
|
57
|
+
end
|
58
|
+
|
59
|
+
rule :Allergy1,
|
60
|
+
[Patient,:p, {m.name=>:n}, m.spots==true],
|
61
|
+
[:not, Diagnosis, m.name==b(:n), m.diagnosis==:measles] do |v|
|
62
|
+
name = v[:n]
|
63
|
+
assert Diagnosis.new(name, :allergy)
|
64
|
+
puts "Allergy diagnosed for #{name} from spots and lack of measles"
|
65
|
+
end
|
66
|
+
|
67
|
+
rule :Allergy2,
|
68
|
+
[Patient,:p, {m.name=>:n}, m.rash==true] do |v|
|
69
|
+
name = v[:n]
|
70
|
+
assert Diagnosis.new(name, :allergy)
|
71
|
+
puts "Allergy diagnosed from rash for #{name}"
|
72
|
+
end
|
73
|
+
|
74
|
+
rule :Flu,
|
75
|
+
[Patient,:p, {m.name=>:n}, m.sore_throat==true, m.fever(&c{|f| f==:mild || f==:high})] do |v|
|
76
|
+
name = v[:n]
|
77
|
+
assert Diagnosis.new(name, :flu)
|
78
|
+
puts "Flu diagnosed for #{name}"
|
79
|
+
end
|
80
|
+
|
81
|
+
rule :Penicillin,
|
82
|
+
[Diagnosis, :d, {m.name => :n}, m.diagnosis==:measles] do |v|
|
83
|
+
name = v[:n]
|
84
|
+
assert Treatment.new(name, :penicillin)
|
85
|
+
puts "Penicillin prescribed for #{name}"
|
86
|
+
end
|
87
|
+
|
88
|
+
rule :Allergy_pills,
|
89
|
+
[Diagnosis, :d, {m.name => :n}, m.diagnosis==:allergy] do |v|
|
90
|
+
name = v[:n]
|
91
|
+
assert Treatment.new(name, :allergy_shot)
|
92
|
+
puts "Allergy shot prescribed for #{name}"
|
93
|
+
end
|
94
|
+
|
95
|
+
rule :Bed_rest,
|
96
|
+
[Diagnosis, :d, {m.name => :n}, m.diagnosis==:flu] do |v|
|
97
|
+
name = v[:n]
|
98
|
+
assert Treatment.new(name, :bed_rest)
|
99
|
+
puts "Bed rest prescribed for #{name}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
include Ruleby
|
105
|
+
|
106
|
+
engine :engine do |e|
|
107
|
+
|
108
|
+
DiagnosisRulebook.new e do |r|
|
109
|
+
r.rules
|
110
|
+
end
|
111
|
+
|
112
|
+
e.assert Patient.new('Fred',:none,true,false,false,false)
|
113
|
+
e.assert Patient.new('Joe',:high,false,false,true,false)
|
114
|
+
e.assert Patient.new('Bob',:high,true,false,false,true)
|
115
|
+
e.assert Patient.new('Tom',:none,false,true,false,false)
|
116
|
+
|
117
|
+
e.match
|
118
|
+
|
119
|
+
# expect this output:
|
120
|
+
# Measles diagnosed for Bob
|
121
|
+
# Penicillin prescribed for Bob
|
122
|
+
# Allergy diagnosed from rash for Tom
|
123
|
+
# Allergy shot prescribed for Tom
|
124
|
+
# Flu diagnosed for Joe
|
125
|
+
# Bed rest prescribed for Joe
|
126
|
+
# Allergy diagnosed for Fred from spots and lack of measles
|
127
|
+
# Allergy shot prescribed for Fred
|
128
|
+
|
129
|
+
end
|
@@ -0,0 +1,44 @@
|
|
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
|
@@ -0,0 +1,40 @@
|
|
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
|
@@ -0,0 +1,78 @@
|
|
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 'rule_helper'
|
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::RuleHelper
|
30
|
+
#RULES
|
31
|
+
rules = []
|
32
|
+
# Bootstrap1
|
33
|
+
rules += rule :Bootstrap1, {:priority => 4},
|
34
|
+
[Fibonacci, :f, m.value == -1, m.sequence == 1 ] do |vars, engine|
|
35
|
+
vars[:f].value = 1
|
36
|
+
engine.modify vars[:f]
|
37
|
+
puts vars[:f].sequence.to_s + ' == ' + vars[:f].value.to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
# Recurse
|
41
|
+
rules += rule :Recurse, {:priority => 3},
|
42
|
+
[Fibonacci, :f, m.value == -1] do |vars, engine|
|
43
|
+
f2 = Fibonacci.new(vars[:f].sequence - 1)
|
44
|
+
engine.assert f2
|
45
|
+
puts 'recurse for ' + f2.sequence.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
# Bootstrap2
|
49
|
+
rules += rule :Bootstrap2,
|
50
|
+
[Fibonacci, :f, m.value == -1 , m.sequence == 2] do |vars, engine|
|
51
|
+
vars[:f].value = 1
|
52
|
+
engine.modify vars[:f]
|
53
|
+
puts vars[:f].sequence.to_s + ' == ' + vars[:f].value.to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
# Calculate
|
57
|
+
rules += rule :Calculate,
|
58
|
+
[Fibonacci,:f1, m.value.not== -1, {m.sequence => :s1}],
|
59
|
+
[Fibonacci,:f2, m.value.not== -1, {m.sequence( :s1, &c{ |s2,s1| s2 == s1 + 1 } ) => :s2}],
|
60
|
+
[Fibonacci,:f3, m.value == -1, m.sequence(:s2, &c{ |s3,s2| s3 == s2 + 1 }) ] do |vars, engine|
|
61
|
+
vars[:f3].value = vars[:f1].value + vars[:f2].value
|
62
|
+
engine.modify vars[:f3]
|
63
|
+
engine.retract vars[:f1]
|
64
|
+
puts vars[:f3].sequence.to_s + ' == ' + vars[:f3].value.to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
# FACTS
|
68
|
+
fib1 = Fibonacci.new(150)
|
69
|
+
|
70
|
+
include Ruleby
|
71
|
+
|
72
|
+
engine :engine do |e|
|
73
|
+
rules.each do |r|
|
74
|
+
e.assert_rule r
|
75
|
+
end
|
76
|
+
e.assert fib1
|
77
|
+
e.match
|
78
|
+
end
|
@@ -0,0 +1,84 @@
|
|
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 |vars|
|
25
|
+
retract vars[:f1]
|
26
|
+
retract vars[:f3]
|
27
|
+
if(vars[:f2].sequence == MAX_SEQUENCE)
|
28
|
+
retract vars[:f2]
|
29
|
+
else
|
30
|
+
f3 = Fibonacci.new(vars[:f2].sequence + 1, vars[:f1].value + vars[:f2].value)
|
31
|
+
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 |vars|
|
39
|
+
f3 = Fibonacci.new(vars[:f2].sequence + 1, -1)
|
40
|
+
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 |vars|
|
52
|
+
vars[:f].value = 1
|
53
|
+
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 |vars|
|
60
|
+
f2 = Fibonacci.new(vars[:f].sequence - 1)
|
61
|
+
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 |vars|
|
68
|
+
vars[:f].value = 1
|
69
|
+
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 |vars|
|
78
|
+
vars[:f3].value = vars[:f1].value + vars[:f2].value
|
79
|
+
modify vars[:f3]
|
80
|
+
retract vars[:f1]
|
81
|
+
puts vars[:f3].sequence.to_s + ' == ' + vars[:f3].value.to_s
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/examples/hello.rb
ADDED
@@ -0,0 +1,45 @@
|
|
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 |v|
|
29
|
+
puts v[:m].message
|
30
|
+
v[:m].message = "Goodbye world"
|
31
|
+
v[:m].status = :GOODBYE
|
32
|
+
modify v[:m]
|
33
|
+
end
|
34
|
+
|
35
|
+
rule [Message, :m, m.status == :GOODBYE] do |v|
|
36
|
+
puts v[:m].message
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
engine :engine do |e|
|
42
|
+
HelloWorldRulebook.new(e).rules
|
43
|
+
e.assert Message.new(:HELLO, 'Hello World')
|
44
|
+
e.match
|
45
|
+
end
|