ruleby 0.9.b4 → 0.9.b7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +5 -0
  2. data/GPL.txt +341 -0
  3. data/LICENSE.txt +52 -0
  4. data/README.markdown +22 -0
  5. data/benchmarks/basic_rules.rb +61 -0
  6. data/benchmarks/joined_rules.rb +66 -0
  7. data/benchmarks/miss_manners/data.rb +146 -0
  8. data/benchmarks/miss_manners/miss_manners.rb +33 -0
  9. data/benchmarks/miss_manners/model.rb +193 -0
  10. data/benchmarks/miss_manners/rules.rb +105 -0
  11. data/benchmarks/model.rb +36 -0
  12. data/examples/diagnosis.rb +129 -0
  13. data/examples/fibonacci_example1.rb +44 -0
  14. data/examples/fibonacci_example2.rb +40 -0
  15. data/examples/fibonacci_example3.rb +78 -0
  16. data/examples/fibonacci_rulebook.rb +84 -0
  17. data/examples/hello.rb +45 -0
  18. data/examples/ticket.rb +113 -0
  19. data/examples/wordgame.rb +107 -0
  20. data/lib/core/engine.rb +26 -24
  21. data/lib/core/nodes.rb +77 -35
  22. data/lib/ruleby.rb +0 -37
  23. data/ruleby.gemspec +17 -0
  24. data/spec/and_or_spec.rb +252 -0
  25. data/spec/coercion_spec.rb +5 -0
  26. data/spec/collect_spec.rb +1021 -0
  27. data/spec/errors_spec.rb +148 -0
  28. data/spec/ferrari_spec.rb +39 -0
  29. data/spec/function_spec.rb +199 -0
  30. data/spec/hello_spec.rb +34 -0
  31. data/spec/node_sharing_spec.rb +53 -0
  32. data/spec/property_spec.rb +69 -0
  33. data/spec/spec.opts +4 -0
  34. data/spec/spec_helper.rb +9 -0
  35. data/tasks/documentation.rake +32 -0
  36. data/tasks/rspec.rake +21 -0
  37. data/tasks/test.rake +9 -0
  38. data/tests/assert_facts.rb +130 -0
  39. data/tests/common.rb +29 -0
  40. data/tests/duck_type.rb +79 -0
  41. data/tests/gets.rb +48 -0
  42. data/tests/join_nodes.rb +63 -0
  43. data/tests/nil.rb +72 -0
  44. data/tests/not_patterns.rb +91 -0
  45. data/tests/or_patterns.rb +154 -0
  46. data/tests/regex.rb +47 -0
  47. data/tests/self_reference.rb +54 -0
  48. metadata +81 -49
@@ -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
@@ -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