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
data/spec/errors_spec.rb
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class A
|
|
4
|
+
attr :value, true
|
|
5
|
+
def initialize(v=nil); @value = v; end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
include Ruleby
|
|
9
|
+
|
|
10
|
+
class ErrorsRulebook < Rulebook
|
|
11
|
+
def rules_with_method_that_doesnt_exist
|
|
12
|
+
rule [A, :a, m.foobar == 'quack'] do |v|
|
|
13
|
+
assert Success.new
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def rules_that_raise_errors
|
|
18
|
+
rule [A, :a, m.value(&c{|v| raise ":(" if v == 42; true})] do |v|
|
|
19
|
+
assert Success.new
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe Ruleby::Core::Engine do
|
|
26
|
+
|
|
27
|
+
describe "#errors" do
|
|
28
|
+
context "rules_with_method_that_doesnt_exist" do
|
|
29
|
+
subject do
|
|
30
|
+
engine :engine do |e|
|
|
31
|
+
ErrorsRulebook.new(e).rules_with_method_that_doesnt_exist
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
context "with one A" do
|
|
36
|
+
before do
|
|
37
|
+
subject.assert A.new
|
|
38
|
+
subject.match
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should accumulate an error" do
|
|
42
|
+
r = subject.retrieve Success
|
|
43
|
+
r.size.should == 0
|
|
44
|
+
|
|
45
|
+
errors = subject.errors
|
|
46
|
+
errors.should_not be_nil
|
|
47
|
+
errors.size.should == 1
|
|
48
|
+
errors[0].type.should == :no_method
|
|
49
|
+
errors[0].details[:method].should == :foobar
|
|
50
|
+
errors[0].details[:object].should match /#<A:(.+)>/
|
|
51
|
+
subject.clear_errors
|
|
52
|
+
|
|
53
|
+
subject.errors.should == []
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context "with one A that quacks" do
|
|
58
|
+
before do
|
|
59
|
+
a = A.new
|
|
60
|
+
|
|
61
|
+
# define a method on the instance (not on the class)
|
|
62
|
+
def a.foobar
|
|
63
|
+
"quack"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
subject.assert a
|
|
67
|
+
subject.assert A.new
|
|
68
|
+
subject.match
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "should accumulate one error" do
|
|
72
|
+
r = subject.retrieve Success
|
|
73
|
+
r.size.should == 1
|
|
74
|
+
|
|
75
|
+
errors = subject.errors
|
|
76
|
+
errors.should_not be_nil
|
|
77
|
+
errors.size.should == 1
|
|
78
|
+
errors[0].type.should == :no_method
|
|
79
|
+
errors[0].details[:method].should == :foobar
|
|
80
|
+
errors[0].details[:object].should match /#<A:(.+)>/
|
|
81
|
+
subject.clear_errors
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
context "rules_that_raise_errors" do
|
|
88
|
+
subject do
|
|
89
|
+
engine :engine do |e|
|
|
90
|
+
ErrorsRulebook.new(e).rules_that_raise_errors
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
context "with one A where value==42" do
|
|
95
|
+
before do
|
|
96
|
+
a = A.new
|
|
97
|
+
a.value = 42
|
|
98
|
+
subject.assert a
|
|
99
|
+
subject.match
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "should accumulate an error" do
|
|
103
|
+
r = subject.retrieve Success
|
|
104
|
+
r.size.should == 0
|
|
105
|
+
|
|
106
|
+
errors = subject.errors
|
|
107
|
+
errors.should_not be_nil
|
|
108
|
+
errors.size.should == 1
|
|
109
|
+
errors[0].type.should == :proc_call
|
|
110
|
+
errors[0].details[:message].should == ':('
|
|
111
|
+
errors[0].details[:object].should match /#<A:(.+)>/
|
|
112
|
+
errors[0].details[:method].should == :value
|
|
113
|
+
errors[0].details[:value].should == "42"
|
|
114
|
+
subject.clear_errors
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
context "with one A where value==42, and one A where value==0" do
|
|
119
|
+
before do
|
|
120
|
+
a1 = A.new
|
|
121
|
+
a1.value = 42
|
|
122
|
+
|
|
123
|
+
a2 = A.new
|
|
124
|
+
a2.value = 0
|
|
125
|
+
|
|
126
|
+
subject.assert a1
|
|
127
|
+
subject.assert a2
|
|
128
|
+
subject.match
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it "should accumulate one error" do
|
|
132
|
+
r = subject.retrieve Success
|
|
133
|
+
r.size.should == 1
|
|
134
|
+
|
|
135
|
+
errors = subject.errors
|
|
136
|
+
errors.should_not be_nil
|
|
137
|
+
errors.size.should == 1
|
|
138
|
+
errors[0].type.should == :proc_call
|
|
139
|
+
errors[0].details[:message].should == ':('
|
|
140
|
+
errors[0].details[:object].should match /#<A:(.+)>/
|
|
141
|
+
errors[0].details[:method].should == :value
|
|
142
|
+
errors[0].details[:value].should == "42"
|
|
143
|
+
subject.clear_errors
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class A
|
|
4
|
+
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
include Ruleby
|
|
8
|
+
|
|
9
|
+
class FerrariRulebook < Rulebook
|
|
10
|
+
def rules
|
|
11
|
+
rule [A] do |v|
|
|
12
|
+
assert Success.new
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe Ruleby::Core::Engine do
|
|
18
|
+
|
|
19
|
+
subject do
|
|
20
|
+
engine :engine do |e|
|
|
21
|
+
FerrariRulebook.new(e).rules
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe "simple case" do
|
|
26
|
+
context "with one A" do
|
|
27
|
+
before do
|
|
28
|
+
subject.assert A.new
|
|
29
|
+
subject.match
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should retrieve Success" do
|
|
33
|
+
s = subject.retrieve Success
|
|
34
|
+
s.should_not be_nil
|
|
35
|
+
s.size.should == 1
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class FuncFact
|
|
4
|
+
attr :value, true
|
|
5
|
+
attr :times, true
|
|
6
|
+
def initialize(v=nil); @value = v; @times = 0; end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
include Ruleby
|
|
10
|
+
|
|
11
|
+
class FunctionsRulebook < Rulebook
|
|
12
|
+
def rules_with_simple_function
|
|
13
|
+
rule [FuncFact, :a, f("b", c{|a, b| b == "b"})] do |v|
|
|
14
|
+
assert Success.new
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def rules_with_function_testing_self(arg)
|
|
19
|
+
rule [FuncFact, :a, f(arg, c{|a, b| a.value == b})] do |v|
|
|
20
|
+
assert Success.new
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def rules_that_share_a_function
|
|
25
|
+
func = c{|a, b| a.times += 1; b == "foobar"}
|
|
26
|
+
|
|
27
|
+
rule [FuncFact, :a, m.value > 1, f("foobar", func)] do |v|
|
|
28
|
+
assert Success.new
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
rule [FuncFact, :a, m.value > 2, f("foobar", func)] do |v|
|
|
32
|
+
assert Success.new
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def rules_with_many_args_function
|
|
37
|
+
rule [FuncFact, :a, f([1, 2, 3, 4], c{|a, b, c, d, e| b < e})] do |v|
|
|
38
|
+
assert Success.new
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def rules_with_no_args_function
|
|
43
|
+
rule [FuncFact, :a, f(c{|a| !a.nil?})] do |v|
|
|
44
|
+
assert Success.new
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe Ruleby::Rulebook do
|
|
50
|
+
|
|
51
|
+
describe "#f" do
|
|
52
|
+
context "rules_with_simple_function" do
|
|
53
|
+
subject do
|
|
54
|
+
engine :engine do |e|
|
|
55
|
+
FunctionsRulebook.new(e).rules_with_simple_function
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
context "with one FuncFact" do
|
|
60
|
+
before do
|
|
61
|
+
subject.assert FuncFact.new
|
|
62
|
+
subject.match
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "should match once" do
|
|
66
|
+
r = subject.retrieve Success
|
|
67
|
+
r.size.should == 1
|
|
68
|
+
subject.errors.should == []
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context "rules_with_function_testing_self(:foo)" do
|
|
74
|
+
subject do
|
|
75
|
+
engine :engine do |e|
|
|
76
|
+
FunctionsRulebook.new(e).rules_with_function_testing_self(:foo)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
context "with one FuncFact" do
|
|
81
|
+
before do
|
|
82
|
+
subject.assert FuncFact.new(:foo)
|
|
83
|
+
subject.match
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "should match once" do
|
|
87
|
+
r = subject.retrieve Success
|
|
88
|
+
r.size.should == 1
|
|
89
|
+
subject.errors.should == []
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
context "rules_with_function_testing_self(:bar)" do
|
|
95
|
+
subject do
|
|
96
|
+
engine :engine do |e|
|
|
97
|
+
FunctionsRulebook.new(e).rules_with_function_testing_self(:bar)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
context "with one FuncFact" do
|
|
102
|
+
before do
|
|
103
|
+
subject.assert FuncFact.new(:foo)
|
|
104
|
+
subject.match
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "should not match " do
|
|
108
|
+
r = subject.retrieve Success
|
|
109
|
+
r.size.should == 0
|
|
110
|
+
subject.errors.should == []
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
context "rules_that_share_a_function" do
|
|
116
|
+
subject do
|
|
117
|
+
engine :engine do |e|
|
|
118
|
+
FunctionsRulebook.new(e).rules_that_share_a_function
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
context "with one FuncFact" do
|
|
123
|
+
before do
|
|
124
|
+
@f = FuncFact.new(3)
|
|
125
|
+
subject.assert @f
|
|
126
|
+
subject.match
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "should match, node should be shared, function should be evaled once" do
|
|
130
|
+
r = subject.retrieve Success
|
|
131
|
+
r.size.should == 2
|
|
132
|
+
subject.errors.should == []
|
|
133
|
+
|
|
134
|
+
@f.times.should == 1
|
|
135
|
+
|
|
136
|
+
subject.retract r[0]
|
|
137
|
+
subject.retract r[1]
|
|
138
|
+
subject.retract @f
|
|
139
|
+
subject.match
|
|
140
|
+
|
|
141
|
+
r = subject.retrieve Success
|
|
142
|
+
r.size.should == 0
|
|
143
|
+
subject.errors.should == []
|
|
144
|
+
|
|
145
|
+
subject.assert @f
|
|
146
|
+
subject.match
|
|
147
|
+
|
|
148
|
+
r = subject.retrieve Success
|
|
149
|
+
r.size.should == 2
|
|
150
|
+
subject.errors.should == []
|
|
151
|
+
|
|
152
|
+
@f.times.should == 2
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
context "rules_with_many_args_function" do
|
|
158
|
+
subject do
|
|
159
|
+
engine :engine do |e|
|
|
160
|
+
FunctionsRulebook.new(e).rules_with_many_args_function
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
context "with one FuncFact" do
|
|
165
|
+
before do
|
|
166
|
+
subject.assert FuncFact.new
|
|
167
|
+
subject.match
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it "should match once" do
|
|
171
|
+
r = subject.retrieve Success
|
|
172
|
+
r.size.should == 1
|
|
173
|
+
subject.errors.should == []
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
context "rules_with_no_args_function" do
|
|
179
|
+
subject do
|
|
180
|
+
engine :engine do |e|
|
|
181
|
+
FunctionsRulebook.new(e).rules_with_no_args_function
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
context "with one FuncFact" do
|
|
186
|
+
before do
|
|
187
|
+
subject.assert FuncFact.new
|
|
188
|
+
subject.match
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
it "should match once" do
|
|
192
|
+
r = subject.retrieve Success
|
|
193
|
+
r.size.should == 1
|
|
194
|
+
subject.errors.should == []
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
data/spec/hello_spec.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class HelloFact
|
|
4
|
+
attr :value, true
|
|
5
|
+
def initialize(v=nil); @value = v; end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
include Ruleby
|
|
9
|
+
|
|
10
|
+
class HelloRulebook < Rulebook
|
|
11
|
+
def rules
|
|
12
|
+
rule [HelloFact, :h] do
|
|
13
|
+
assert Success.new
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe Ruleby::Core::Engine do
|
|
19
|
+
subject do
|
|
20
|
+
engine :engine do |e|
|
|
21
|
+
HelloRulebook.new(e).rules
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
before do
|
|
26
|
+
subject.assert HelloFact.new
|
|
27
|
+
subject.match
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should have matched" do
|
|
31
|
+
subject.errors.should == []
|
|
32
|
+
subject.retrieve(Success).size.should == 1
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class NodeShareFact
|
|
4
|
+
attr_reader :times
|
|
5
|
+
|
|
6
|
+
def initialize(v=nil); @value = v; @times = 0; end
|
|
7
|
+
|
|
8
|
+
def value
|
|
9
|
+
@times += 1
|
|
10
|
+
@value
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
include Ruleby
|
|
15
|
+
|
|
16
|
+
class NodeShareRulebook < Rulebook
|
|
17
|
+
def rules
|
|
18
|
+
rule [NodeShareFact, :n, m.value == 5] do
|
|
19
|
+
assert Success.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
rule [NodeShareFact, :n, m.value == 5] do
|
|
23
|
+
assert Success.new
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
rule [NodeShareFact, :n, m.value == 6] do
|
|
27
|
+
assert Success.new
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe Ruleby::Core::Engine do
|
|
33
|
+
|
|
34
|
+
describe "node sharing" do
|
|
35
|
+
subject do
|
|
36
|
+
engine :engine do |e|
|
|
37
|
+
NodeShareRulebook.new(e).rules
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
before do
|
|
42
|
+
@f = NodeShareFact.new(5)
|
|
43
|
+
subject.assert @f
|
|
44
|
+
subject.match
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should have matched" do
|
|
48
|
+
@f.times.should == 1
|
|
49
|
+
subject.errors.should == []
|
|
50
|
+
subject.retrieve(Success).size.should == 2
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|