ruleby 0.9.b4 → 0.9.b7
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/.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
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class PropFact
|
|
4
|
+
attr :value, true
|
|
5
|
+
def initialize(v=nil); @value = v; end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
class PropCtx
|
|
9
|
+
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
include Ruleby
|
|
14
|
+
|
|
15
|
+
class PropRulebook < Rulebook
|
|
16
|
+
def gt_rules
|
|
17
|
+
rule [PropFact, :p, m.value > 0] do
|
|
18
|
+
assert Success.new
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def lte_rules
|
|
23
|
+
rule [PropFact, :p, m.value > 42], [PropCtx, :pc] do
|
|
24
|
+
# do nothing, just being here helps reproduce a bug
|
|
25
|
+
end
|
|
26
|
+
rule [PropFact, :p, m.value <= 42], [PropCtx, :pc] do
|
|
27
|
+
assert Success.new
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe Ruleby::Core::Engine do
|
|
33
|
+
describe "property gt_rules" do
|
|
34
|
+
subject do
|
|
35
|
+
engine :engine do |e|
|
|
36
|
+
PropRulebook.new(e).gt_rules
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
before do
|
|
41
|
+
subject.assert PropFact.new(1)
|
|
42
|
+
subject.match
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "should have matched" do
|
|
46
|
+
subject.errors.should == []
|
|
47
|
+
subject.retrieve(Success).size.should == 1
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
describe "property lte_rules" do
|
|
51
|
+
subject do
|
|
52
|
+
engine :engine do |e|
|
|
53
|
+
PropRulebook.new(e).lte_rules
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
before do
|
|
58
|
+
subject.assert PropCtx.new
|
|
59
|
+
subject.assert PropFact.new(42)
|
|
60
|
+
subject.assert PropFact.new(41)
|
|
61
|
+
subject.match
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "should have matched" do
|
|
65
|
+
subject.errors.should == []
|
|
66
|
+
subject.retrieve(Success).size.should == 2
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#
|
|
2
|
+
# DOCUMENTATION
|
|
3
|
+
|
|
4
|
+
#ALLISON=`allison --path`
|
|
5
|
+
#ALLISON="/Library/Ruby/Gems/1.8/gems/allison-2.0.3/lib/allison.rb"
|
|
6
|
+
|
|
7
|
+
Rake::RDocTask.new do |rd|
|
|
8
|
+
|
|
9
|
+
#rd.main = "README.txt"
|
|
10
|
+
#rd.rdoc_dir = "html/rufus-verbs"
|
|
11
|
+
|
|
12
|
+
rd.rdoc_files.include(
|
|
13
|
+
"LICENSE.txt",
|
|
14
|
+
"lib/**/*.rb")
|
|
15
|
+
|
|
16
|
+
rd.title = "ruleby rdoc"
|
|
17
|
+
|
|
18
|
+
rd.options << '-N' # line numbers
|
|
19
|
+
rd.options << '-S' # inline source
|
|
20
|
+
|
|
21
|
+
#rd.template = ALLISON if File.exist?(ALLISON)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
#
|
|
26
|
+
# WEBSITE
|
|
27
|
+
|
|
28
|
+
#task :upload_website => [ :clean, :rdoc ] do
|
|
29
|
+
# account = "whoever@rubyforge.org"
|
|
30
|
+
# webdir = "/var/www/gforge-projects/ruleby"
|
|
31
|
+
# sh "rsync -azv -e ssh html/source #{account}:#{webdir}/"
|
|
32
|
+
#end
|
data/tasks/rspec.rake
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'spec'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
require 'rubygems'
|
|
5
|
+
require 'spec'
|
|
6
|
+
end
|
|
7
|
+
begin
|
|
8
|
+
require 'spec/rake/spectask'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
puts <<-EOS
|
|
11
|
+
To use rspec for testing you must install rspec gem:
|
|
12
|
+
gem install rspec
|
|
13
|
+
EOS
|
|
14
|
+
exit(0)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc "Run the specs under spec/models"
|
|
18
|
+
Spec::Rake::SpecTask.new do |t|
|
|
19
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
|
20
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
|
21
|
+
end
|
data/tasks/test.rake
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
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) 2008 Joe Kutner and Matt Smith. All rights reserved.
|
|
8
|
+
#
|
|
9
|
+
# * Authors: Joe Kutner
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
require 'test/unit'
|
|
13
|
+
|
|
14
|
+
require 'ruleby'
|
|
15
|
+
|
|
16
|
+
include Ruleby
|
|
17
|
+
|
|
18
|
+
module AssertFacts
|
|
19
|
+
|
|
20
|
+
class Fibonacci
|
|
21
|
+
def initialize(sequence,value=-1)
|
|
22
|
+
@sequence = sequence
|
|
23
|
+
@value = value
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
attr_reader :sequence
|
|
27
|
+
attr :value, true
|
|
28
|
+
|
|
29
|
+
def to_s
|
|
30
|
+
return super + "::sequence=" + @sequence.to_s + ",value=" + @value.to_s
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class SimpleRulebook < Rulebook
|
|
35
|
+
def rules
|
|
36
|
+
rule [Message, :m, {m.message => :x}, m.status == b(:x)],
|
|
37
|
+
[Context, :c] do |v|
|
|
38
|
+
v[:c].inc :rule1
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class FibonacciRulebook < Rulebook
|
|
44
|
+
def rules
|
|
45
|
+
# Bootstrap1
|
|
46
|
+
rule :Bootstrap1, {:priority => 4},
|
|
47
|
+
[Fibonacci, :f, m.value == -1, m.sequence == 1 ] do |vars|
|
|
48
|
+
vars[:f].value = 1
|
|
49
|
+
modify vars[:f]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Recurse
|
|
53
|
+
rule :Recurse, {:priority => 3},
|
|
54
|
+
[Fibonacci, :f, m.value == -1] do |vars|
|
|
55
|
+
f2 = Fibonacci.new(vars[:f].sequence - 1)
|
|
56
|
+
assert f2
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Bootstrap2
|
|
60
|
+
rule :Bootstrap2,
|
|
61
|
+
[Fibonacci, :f, m.value == -1 , m.sequence == 2] do |vars|
|
|
62
|
+
vars[:f].value = 1
|
|
63
|
+
modify vars[:f]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Calculate
|
|
67
|
+
rule :Calculate,
|
|
68
|
+
[Context, :c],
|
|
69
|
+
[Fibonacci,:f1, m.value.not== -1, {m.sequence => :s1}],
|
|
70
|
+
[Fibonacci,:f2, m.value.not== -1, {m.sequence( :s1, &c{ |s2,s1| s2 == s1 + 1 } ) => :s2}],
|
|
71
|
+
[Fibonacci,:f3, m.value == -1, m.sequence(:s2, &c{ |s3,s2| s3 == s2 + 1 }) ] do |vars|
|
|
72
|
+
vars[:f3].value = vars[:f1].value + vars[:f2].value
|
|
73
|
+
modify vars[:f3]
|
|
74
|
+
retract vars[:f1]
|
|
75
|
+
vars[:c].set vars[:f3].sequence, vars[:f3].value
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
class Test < Test::Unit::TestCase
|
|
81
|
+
|
|
82
|
+
def test_0
|
|
83
|
+
engine :engine do |e|
|
|
84
|
+
ctx = Context.new
|
|
85
|
+
e.assert ctx
|
|
86
|
+
e.assert Message.new(:HELLO, :HELLO)
|
|
87
|
+
SimpleRulebook.new(e).rules
|
|
88
|
+
e.assert Message.new(:HELLO, :GOODBYE)
|
|
89
|
+
e.match
|
|
90
|
+
assert_equal 1, ctx.get(:rule1)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def test_1
|
|
95
|
+
engine :engine do |e|
|
|
96
|
+
ctx = Context.new
|
|
97
|
+
e.assert ctx
|
|
98
|
+
e.assert Message.new(:HELLO, :HELLO)
|
|
99
|
+
e.assert Message.new(:HELLO, :GOODBYE)
|
|
100
|
+
SimpleRulebook.new(e).rules
|
|
101
|
+
e.match
|
|
102
|
+
assert_equal 1, ctx.get(:rule1)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def test_2
|
|
107
|
+
fib1 = Fibonacci.new(150)
|
|
108
|
+
engine :engine do |e|
|
|
109
|
+
FibonacciRulebook.new(e).rules
|
|
110
|
+
ctx = Context.new
|
|
111
|
+
e.assert ctx
|
|
112
|
+
e.assert fib1
|
|
113
|
+
e.match
|
|
114
|
+
assert_equal 9969216677189303386214405760200, ctx.get(150)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def test_3
|
|
119
|
+
fib1 = Fibonacci.new(150)
|
|
120
|
+
engine :engine do |e|
|
|
121
|
+
ctx = Context.new
|
|
122
|
+
e.assert ctx
|
|
123
|
+
e.assert fib1
|
|
124
|
+
FibonacciRulebook.new(e).rules
|
|
125
|
+
e.match
|
|
126
|
+
assert_equal 9969216677189303386214405760200, ctx.get(150)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
data/tests/common.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
|
|
2
|
+
class Context
|
|
3
|
+
|
|
4
|
+
def initialize
|
|
5
|
+
@counts = {}
|
|
6
|
+
@counts.default = 0
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def inc(key)
|
|
10
|
+
@counts[key] += 1
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def set(key,value)
|
|
14
|
+
@counts[key] = value
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def get(key)
|
|
18
|
+
@counts[key]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class Message
|
|
23
|
+
def initialize(status,message)
|
|
24
|
+
@status = status
|
|
25
|
+
@message = message
|
|
26
|
+
end
|
|
27
|
+
attr :status, true
|
|
28
|
+
attr :message, true
|
|
29
|
+
end
|
data/tests/duck_type.rb
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
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) 2008 Joe Kutner and Matt Smith. All rights reserved.
|
|
8
|
+
#
|
|
9
|
+
# * Authors: Joe Kutner, Matt Smith, John Mettraux
|
|
10
|
+
#
|
|
11
|
+
require 'test/unit'
|
|
12
|
+
require 'ruleby'
|
|
13
|
+
|
|
14
|
+
include Ruleby
|
|
15
|
+
|
|
16
|
+
module Duck
|
|
17
|
+
class Foobar
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class Email < Message
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class Loan
|
|
24
|
+
def initialize(name,age)
|
|
25
|
+
@name = name
|
|
26
|
+
@age = age
|
|
27
|
+
@status = :HELLO
|
|
28
|
+
end
|
|
29
|
+
attr :name, true
|
|
30
|
+
attr :age, true
|
|
31
|
+
attr :status, true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class DuckRulebook < Rulebook
|
|
36
|
+
|
|
37
|
+
def rules
|
|
38
|
+
|
|
39
|
+
rule [m.status == :HELLO], [Context, :c] do |v|
|
|
40
|
+
v[:c].inc :rule1
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
rule [:is_a?, Message, m.status == :HELLO], [Context, :c] do |v|
|
|
44
|
+
v[:c].inc :rule2
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
rule [Message, m.status == :HELLO], [Context, :c] do |v|
|
|
48
|
+
v[:c].inc :rule3
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class DuckTypeTest < Test::Unit::TestCase
|
|
54
|
+
|
|
55
|
+
def test_0
|
|
56
|
+
|
|
57
|
+
engine :engine do |e|
|
|
58
|
+
DuckRulebook.new(e).rules
|
|
59
|
+
ctx = Context.new
|
|
60
|
+
a = Loan.new('A','B')
|
|
61
|
+
b = Message.new(:HELLO, 'test')
|
|
62
|
+
c = Email.new(:HELLO, 'test')
|
|
63
|
+
d = Message.new(:FOOBAR, 'foobar')
|
|
64
|
+
f = Foobar.new
|
|
65
|
+
|
|
66
|
+
e.assert ctx
|
|
67
|
+
e.assert a; e.match; e.retract a
|
|
68
|
+
e.assert b; e.match; e.retract b
|
|
69
|
+
e.assert c; e.match; e.retract c
|
|
70
|
+
e.assert d; e.match; e.retract d
|
|
71
|
+
e.assert f; e.match; e.retract f
|
|
72
|
+
|
|
73
|
+
assert_equal 3, ctx.get(:rule1)
|
|
74
|
+
assert_equal 2, ctx.get(:rule2)
|
|
75
|
+
assert_equal 1, ctx.get(:rule3)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
data/tests/gets.rb
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
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 'test/unit'
|
|
13
|
+
require 'ruleby'
|
|
14
|
+
|
|
15
|
+
include Ruleby
|
|
16
|
+
|
|
17
|
+
TEST_STR = String.new("it worked")
|
|
18
|
+
|
|
19
|
+
module Get
|
|
20
|
+
|
|
21
|
+
class GetRulebook < Rulebook
|
|
22
|
+
def rules
|
|
23
|
+
|
|
24
|
+
rule [Message, :m, m.status == :HELLO] do |v|
|
|
25
|
+
@engine.assert TEST_STR
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class Test < Test::Unit::TestCase
|
|
32
|
+
|
|
33
|
+
def test_0
|
|
34
|
+
|
|
35
|
+
engine :engine do |e|
|
|
36
|
+
GetRulebook.new(e).rules
|
|
37
|
+
e.assert Message.new(:HELLO, 'test')
|
|
38
|
+
e.match
|
|
39
|
+
|
|
40
|
+
strs = e.retrieve(String)
|
|
41
|
+
|
|
42
|
+
assert_equal 1, strs.size
|
|
43
|
+
|
|
44
|
+
assert_equal TEST_STR, strs[0]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
data/tests/join_nodes.rb
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
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) 2009 Joe Kutner and Matt Smith. All rights reserved.
|
|
8
|
+
#
|
|
9
|
+
# * Authors: Joe Kutner
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
require 'test/unit'
|
|
13
|
+
|
|
14
|
+
require 'ruleby'
|
|
15
|
+
|
|
16
|
+
include Ruleby
|
|
17
|
+
|
|
18
|
+
module JoinNodes
|
|
19
|
+
|
|
20
|
+
class A
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class B
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class JoinNodesRulebook < Rulebook
|
|
29
|
+
def rules
|
|
30
|
+
rule [A], [B], [Context, :c] do |v|
|
|
31
|
+
v[:c].inc :rule1
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class Test < Test::Unit::TestCase
|
|
37
|
+
def test_0
|
|
38
|
+
engine :engine do |e|
|
|
39
|
+
JoinNodesRulebook.new(e).rules
|
|
40
|
+
ctx = Context.new
|
|
41
|
+
a = A.new
|
|
42
|
+
b = B.new
|
|
43
|
+
e.assert ctx
|
|
44
|
+
e.assert a
|
|
45
|
+
e.assert b
|
|
46
|
+
e.match
|
|
47
|
+
assert_equal 1, ctx.get(:rule1)
|
|
48
|
+
e.retract a
|
|
49
|
+
e.match
|
|
50
|
+
assert_equal 1, ctx.get(:rule1)
|
|
51
|
+
e.assert B.new
|
|
52
|
+
e.match
|
|
53
|
+
assert_equal 1, ctx.get(:rule1)
|
|
54
|
+
e.assert A.new
|
|
55
|
+
e.match
|
|
56
|
+
assert_equal 3, ctx.get(:rule1)
|
|
57
|
+
e.retract b
|
|
58
|
+
e.match
|
|
59
|
+
assert_equal 4, ctx.get(:rule1)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|