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
@@ -19,41 +19,4 @@ module Ruleby
19
19
  yield e if block_given?
20
20
  return e
21
21
  end
22
- end
23
-
24
- class String
25
- unless ''.respond_to?(:to_proc)
26
- def to_proc &block
27
- params = []
28
- expr = self
29
- sections = expr.split(/\s*->\s*/m)
30
- if sections.length > 1 then
31
- eval sections.reverse!.inject { |e, p| "(Proc.new { |#{p.split(/\s/).join(', ')}| #{e} })" }, block && block.binding
32
- elsif expr.match(/\b_\b/)
33
- eval "Proc.new { |_| #{expr} }", block && block.binding
34
- else
35
- leftSection = expr.match(/^\s*(?:[+*\/%&|\^\.=<>\[]|!=)/m)
36
- rightSection = expr.match(/[+\-*\/%&|\^\.=<>!]\s*$/m)
37
- if leftSection || rightSection then
38
- if (leftSection) then
39
- params.push('$left')
40
- expr = '$left' + expr
41
- end
42
- if (rightSection) then
43
- params.push('$right')
44
- expr = expr + '$right'
45
- end
46
- else
47
- self.gsub(
48
- /(?:\b[A-Z]|\.[a-zA-Z_$])[a-zA-Z_$\d]*|[a-zA-Z_$][a-zA-Z_$\d]*:|self|arguments|'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"/, ''
49
- ).scan(
50
- /([a-z_$][a-z_$\d]*)/i
51
- ) do |v|
52
- params.push(v) unless params.include?(v)
53
- end
54
- end
55
- eval "Proc.new { |#{params.join(', ')}| #{expr} }", block && block.binding
56
- end
57
- end
58
- end
59
22
  end
@@ -0,0 +1,17 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{ruleby}
3
+ s.version = "0.9.b7"
4
+
5
+ s.authors = [%q{Joe Kutner}, %q{Matt Smith}]
6
+ s.description = %q{Ruleby is a rule engine written in the Ruby language. It is a system for executing a set
7
+ of IF-THEN statements known as production rules. These rules are matched to objects using
8
+ the forward chaining Rete algorithm. Ruleby provides an internal Domain Specific Language
9
+ (DSL) for building the productions that make up a Ruleby program.
10
+ }
11
+ s.email = %q{jpkutner@gmail.com}
12
+ s.homepage = %q{http://ruleby.org}
13
+ s.files = `git ls-files`.split("\n")
14
+ s.require_paths = [%q{lib}]
15
+ s.rubyforge_project = %q{ruleby}
16
+ s.summary = %q{Rete based Ruby Rule Engine}
17
+ end
@@ -0,0 +1,252 @@
1
+ require 'spec_helper'
2
+
3
+ class AndOrFact
4
+ attr :value, true
5
+ def initialize(v=nil); @value = v; end
6
+ end
7
+
8
+ class AndOrFact2
9
+ attr :value, true
10
+ def initialize(v=nil); @value = v; end
11
+ end
12
+
13
+ class AndOrFact3
14
+ attr :value, true
15
+ def initialize(v=nil); @value = v; end
16
+ end
17
+
18
+ class AndOrFact4
19
+ attr :value, true
20
+ def initialize(v=nil); @value = v; end
21
+ end
22
+
23
+ class AndOrFact5
24
+ attr :value, true
25
+ def initialize(v=nil); @value = v; end
26
+ end
27
+
28
+ class AndOrFact6
29
+ attr :value, true
30
+ def initialize(v=nil); @value = v; end
31
+ end
32
+
33
+ include Ruleby
34
+
35
+ class AndOrRulebook < Rulebook
36
+ def rules
37
+ rule AND(
38
+ OR([AndOrFact, m.value > 0]),
39
+ OR(
40
+ OR([AndOrFact, m.value == 1]),
41
+ AND(
42
+ AND([AndOrFact, m.value < 1]),
43
+ OR([AndOrFact, m.value == nil], [:not, AndOrFact])))) do
44
+ assert Success.new
45
+ end
46
+
47
+ # rule [AndOrFact, m.value > 0],
48
+ # OR(
49
+ # [AndOrFact, m.value == 1],
50
+ # AND(
51
+ # [AndOrFact, m.value < 1],
52
+ # OR([AndOrFact, m.value == nil], [:not, AndOrFact]))) do
53
+ # assert Success.new
54
+ # end
55
+ end
56
+
57
+ def rules2
58
+ rule OR(AND(OR(OR([AndOrFact, m.value == 1])))) do |v|
59
+ assert Success.new
60
+ end
61
+ end
62
+
63
+ def rules3
64
+ rule OR([AndOrFact, m.value == 1], [AndOrFact, m.value == 2], [AndOrFact, m.value == 3]), [AndOrFact, m.value == 4] do |v|
65
+ assert Success.new
66
+ end
67
+ end
68
+
69
+ def rules4
70
+ rule AND([AndOrFact, :a, m.value == 1], [AndOrFact2, :a2, m.value == 2]) do |v|
71
+ raise "nil" if v[:a].nil?
72
+ raise "nil" if v[:a2].nil?
73
+ assert Success.new
74
+ end
75
+ end
76
+
77
+ def rules5
78
+ rule OR(AND([AndOrFact, :a, {m.value == 1 => :x}], [AndOrFact2, m.value == b(:x)])) do |v|
79
+ assert Success.new
80
+ end
81
+ end
82
+
83
+ def rules6
84
+ rule OR(
85
+ AND([:collect, AndOrFact, m.value == 1]),
86
+ AND([:collect, AndOrFact2, m.value == 2])
87
+ ), AND(
88
+ AND([AndOrFact3, m.value == 65]),
89
+ OR(
90
+ OR(
91
+ OR([AndOrFact4, m.value == 4]),
92
+ AND([AndOrFact5, m.value == 5])
93
+ ),
94
+ OR([AndOrFact6, m.value == 6])
95
+ )
96
+ ) do |v|
97
+ assert Success.new
98
+ end
99
+ # all that really matters
100
+ # rule OR(
101
+ # [AndOrFact, m.value == 1],
102
+ # [AndOrFact2, m.value == 2]
103
+ # ), AND(
104
+ # [AndOrFact3, m.value == 65],
105
+ # OR(
106
+ # [AndOrFact4, m.value == 4],
107
+ # [AndOrFact5, m.value == 5]
108
+ # )
109
+ # ) do |v|
110
+ # assert Success.new
111
+ # end
112
+ end
113
+ end
114
+
115
+ describe Ruleby::Core::Engine do
116
+ # describe "AND/OR" do
117
+ context "crazy AND/OR rules" do
118
+ subject do
119
+ engine :engine do |e|
120
+ AndOrRulebook.new(e).rules
121
+ end
122
+ end
123
+
124
+ before do
125
+ subject.assert AndOrFact.new(1)
126
+ subject.match
127
+ end
128
+
129
+ it "should have matched" do
130
+ subject.errors.should == []
131
+ subject.retrieve(Success).size.should == 1
132
+ end
133
+ end
134
+
135
+ context "nested AND/OR rule" do
136
+ subject do
137
+ engine :engine do |e|
138
+ AndOrRulebook.new(e).rules2
139
+ end
140
+ end
141
+
142
+ before do
143
+ subject.assert AndOrFact.new(1)
144
+ subject.match
145
+ end
146
+
147
+ it "should have matched" do
148
+ subject.errors.should == []
149
+ subject.retrieve(Success).size.should == 1
150
+ end
151
+ end
152
+
153
+ context "multi OR rule" do
154
+ subject do
155
+ engine :engine do |e|
156
+ AndOrRulebook.new(e).rules3
157
+ end
158
+ end
159
+
160
+ context "with one 1 and one 4" do
161
+ before do
162
+ subject.assert AndOrFact.new(1)
163
+ subject.assert AndOrFact.new(4)
164
+ subject.match
165
+ end
166
+
167
+ it "should have matched" do
168
+ subject.errors.should == []
169
+ subject.retrieve(Success).size.should == 1
170
+ end
171
+ end
172
+ end
173
+
174
+ context "nested AND/OR rule" do
175
+ subject do
176
+ engine :engine do |e|
177
+ AndOrRulebook.new(e).rules4
178
+ end
179
+ end
180
+
181
+ before do
182
+ subject.assert AndOrFact.new(1)
183
+ subject.assert AndOrFact2.new(2)
184
+ subject.match
185
+ end
186
+
187
+ it "should have matched" do
188
+ subject.errors.should == []
189
+ subject.retrieve(Success).size.should == 1
190
+ end
191
+ end
192
+
193
+ context "nested AND/OR rule" do
194
+ subject do
195
+ engine :engine do |e|
196
+ AndOrRulebook.new(e).rules5
197
+ end
198
+ end
199
+
200
+ before do
201
+ subject.assert AndOrFact.new(1)
202
+ subject.assert AndOrFact2.new(1)
203
+ subject.match
204
+ end
205
+
206
+ it "should have matched" do
207
+ subject.errors.should == []
208
+ subject.retrieve(Success).size.should == 1
209
+ end
210
+ end
211
+
212
+ context "another crazy nested AND/OR rule" do
213
+ subject do
214
+ engine :engine do |e|
215
+ AndOrRulebook.new(e).rules6
216
+ end
217
+ end
218
+
219
+ before do
220
+ subject.assert AndOrFact.new(1)
221
+ subject.assert AndOrFact3.new(3)
222
+ subject.assert AndOrFact4.new(4)
223
+ subject.match
224
+ end
225
+
226
+ it "should not have matched" do
227
+ subject.errors.should == []
228
+ subject.retrieve(Success).size.should == 0
229
+ end
230
+ end
231
+
232
+ context "another crazy nested AND/OR rule" do
233
+ subject do
234
+ engine :engine do |e|
235
+ AndOrRulebook.new(e).rules6
236
+ end
237
+ end
238
+
239
+ before do
240
+ subject.assert AndOrFact.new(1)
241
+ subject.assert AndOrFact3.new(65)
242
+ subject.assert AndOrFact4.new(4)
243
+ subject.match
244
+ end
245
+
246
+ it "should have matched" do
247
+ subject.errors.should == []
248
+ subject.retrieve(Success).size.should == 1
249
+ end
250
+ end
251
+ # end
252
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ruleby::Core::Engine do
4
+
5
+ end
@@ -0,0 +1,1021 @@
1
+ require 'spec_helper'
2
+
3
+ class A
4
+ attr :value, true
5
+ def initialize(v=nil); @value = v; end
6
+ end
7
+
8
+ class B
9
+ attr :value1, true
10
+ attr :value2, true
11
+ def initialize(v1=nil, v2=nil); @value1 = v1; @value2 = v2; end
12
+ end
13
+
14
+ class C
15
+
16
+ end
17
+
18
+ include Ruleby
19
+
20
+ class CollectRulebook < Rulebook
21
+ def rules_with_one_pattern
22
+ rule [:collect, A, :a] do |v|
23
+ assert v[:a]
24
+ assert Success.new
25
+ end
26
+ end
27
+
28
+ def rules_with_one_pattern_and_other_conditions
29
+ rule [:collect, A, :a, m.value == "foo"] do |v|
30
+ assert v[:a]
31
+ assert Success.new
32
+ end
33
+
34
+ rule [:collect, B, :b, m.value1 == "foo", m.value2 == "bar"] do |v|
35
+ assert v[:b]
36
+ assert Success.new
37
+ end
38
+ end
39
+
40
+ def rules_with_one_pattern_inside_and
41
+ rule AND([:collect, A, :a]) do |v|
42
+ assert v[:a]
43
+ assert Success.new
44
+ end
45
+ end
46
+
47
+ def rules_with_two_collect_patterns_of_same_type
48
+ rule [:collect, A, :a1], [:collect, A, :a2] do |v|
49
+ assert v[:a1]
50
+ assert v[:a2]
51
+ assert Success.new
52
+ end
53
+ end
54
+
55
+ def rules_with_two_collect_patterns_of_different_type
56
+ rule [:collect, A, :a], [:collect, B, :b] do |v|
57
+ assert v[:a]
58
+ assert v[:b]
59
+ assert Success.new
60
+ end
61
+ end
62
+
63
+ def rules_with_one_pattern_and_a_not_on_right
64
+ rule [:collect, A, :a], [:not, B] do |v|
65
+ assert v[:a]
66
+ assert Success.new
67
+ end
68
+ end
69
+
70
+ def rules_with_one_pattern_and_a_not_on_left
71
+ rule [:not, B], [:collect, A, :a] do |v|
72
+ assert v[:a]
73
+ assert Success.new
74
+ end
75
+ end
76
+
77
+ def rules_with_more_than_one_pattern
78
+ rule [:collect, A, :a], [B, :b] do |v|
79
+ assert v[:a]
80
+ assert Success.new(:right)
81
+ end
82
+
83
+ rule [B, :b], [:collect, A, :a] do |v|
84
+ assert v[:a]
85
+ assert Success.new(:left)
86
+ end
87
+ end
88
+
89
+ def rules_with_more_than_one_pattern_on_each_side
90
+ rule [B, :b], [:collect, A, :a], [C, :c] do |v|
91
+ assert v[:a]
92
+ assert Success.new
93
+ end
94
+ end
95
+
96
+ def rules_with_chaining
97
+ rule [:collect, A, :a] do |v|
98
+ assert v[:a]
99
+ assert Success.new
100
+ end
101
+
102
+ rule [C, :c] do |v|
103
+ assert A.new
104
+ end
105
+ end
106
+
107
+ def rules_with_binding
108
+ rule [:collect, A, :a], [B, :b, m.value1(:a, &c{|v1, a| a.size > 0 and a[0].object.value == v1})] do |v|
109
+ assert Success.new
110
+ end
111
+
112
+ rule [:collect, A, :a], [B, :b, m.value1(:a, &c{|v1, a| a.size > 1 and a[0].object.value == v1})] do |v|
113
+ assert Success.new
114
+ end
115
+ end
116
+
117
+ def rules_with_chaining_and_binding
118
+ rule [:collect, A, :a], [B, :b, m.value1(:a, &c{|v1, a| a.size > 0})] do |v|
119
+ assert v[:a]
120
+ assert Success.new
121
+ end
122
+
123
+ rule [C, :c] do |v|
124
+ assert A.new
125
+ end
126
+ end
127
+ end
128
+
129
+ describe Ruleby::Core::Engine do
130
+
131
+ describe ":collect" do
132
+
133
+ shared_examples_for "not patterns with [:collect, A] and [:not, B] when there is a B" do
134
+ it "should not match" do
135
+ s = subject.retrieve Success
136
+ s.should_not be_nil
137
+ s.size.should == 0
138
+ end
139
+
140
+ it "should match once B is retracted" do
141
+ a = subject.retrieve B
142
+ subject.retract a[0]
143
+
144
+ subject.match
145
+
146
+ s = subject.retrieve Success
147
+ s.size.should == 1
148
+ a = subject.retrieve A
149
+ a.size.should == 2
150
+ end
151
+ end
152
+
153
+ shared_examples_for "one :collect A rule and one A" do
154
+ it "should retrieve Success" do
155
+ s = subject.retrieve Success
156
+ s.should_not be_nil
157
+ s.size.should == 1
158
+
159
+ s = subject.retrieve Array
160
+ s.should_not be_nil
161
+ s.size.should == 1
162
+
163
+ a = s[0]
164
+ a.size.should == 1
165
+ a[0].object.class.should == A
166
+ end
167
+
168
+ it "should retract without error" do
169
+ s = subject.retrieve Success
170
+ s.size.should == 1
171
+ subject.retract s[0]
172
+
173
+ a = subject.retrieve A
174
+ a.size.should == 1
175
+ subject.retract a[0]
176
+
177
+ subject.match
178
+
179
+ s = subject.retrieve Success
180
+ s.size.should == 0
181
+ a = subject.retrieve A
182
+ a.size.should == 0
183
+ end
184
+ end
185
+
186
+ shared_examples_for "one :collect A rule and two As" do
187
+ it "should retrieve Success" do
188
+ s = subject.retrieve Success
189
+ s.should_not be_nil
190
+ s.size.should == 1
191
+
192
+ s = subject.retrieve Array
193
+ s.should_not be_nil
194
+ s.size.should == 1
195
+
196
+ a = s[0]
197
+ a.size.should == 2
198
+ a[0].object.class.should == A
199
+ a[1].object.class.should == A
200
+ end
201
+
202
+ it "should retract without error" do
203
+ s = subject.retrieve Success
204
+ subject.retract s[0]
205
+ a = subject.retrieve A
206
+ subject.retract a[0]
207
+
208
+ subject.match
209
+
210
+ s = subject.retrieve Success
211
+ s.size.should == 1
212
+ a = subject.retrieve A
213
+ a.size.should == 1
214
+
215
+ s = subject.retrieve Success
216
+ subject.retract s[0]
217
+ a = subject.retrieve A
218
+ subject.retract a[0]
219
+
220
+ subject.match
221
+
222
+ s = subject.retrieve Success
223
+ s.size.should == 0
224
+ a = subject.retrieve A
225
+ a.size.should == 0
226
+ end
227
+ end
228
+
229
+ context "as one pattern" do
230
+ subject do
231
+ engine :engine do |e|
232
+ CollectRulebook.new(e).rules_with_one_pattern
233
+ end
234
+ end
235
+
236
+ context "with one A" do
237
+ before do
238
+ subject.assert A.new
239
+ subject.match
240
+ end
241
+
242
+ it_should_behave_like "one :collect A rule and one A"
243
+ end
244
+
245
+ context "with more than one A" do
246
+ before do
247
+ subject.assert A.new
248
+ subject.assert A.new
249
+ subject.match
250
+ end
251
+
252
+ it_should_behave_like "one :collect A rule and two As"
253
+ end
254
+ end
255
+
256
+ context "as one pattern and other conditions" do
257
+ subject do
258
+ engine :engine do |e|
259
+ CollectRulebook.new(e).rules_with_one_pattern_and_other_conditions
260
+ end
261
+ end
262
+
263
+ context "with one A('foo')" do
264
+ before do
265
+ subject.assert A.new("foo")
266
+ subject.match
267
+ end
268
+
269
+ it_should_behave_like "one :collect A rule and one A"
270
+ end
271
+
272
+ context "with more than one A('foo')" do
273
+ before do
274
+ subject.assert A.new("foo")
275
+ subject.assert A.new("foo")
276
+ subject.match
277
+ end
278
+
279
+ it_should_behave_like "one :collect A rule and two As"
280
+ end
281
+
282
+ context "with one A('foo') and one A(nil)" do
283
+ before do
284
+ subject.assert A.new("foo")
285
+ subject.assert A.new
286
+ subject.match
287
+ end
288
+
289
+ it "should retrieve Success" do
290
+ s = subject.retrieve Success
291
+ s.should_not be_nil
292
+ s.size.should == 1
293
+
294
+ s = subject.retrieve Array
295
+ s.should_not be_nil
296
+ s.size.should == 1
297
+
298
+ a = s[0]
299
+ a.size.should == 1
300
+ a[0].object.class.should == A
301
+ end
302
+ end
303
+
304
+ context "with more than one A('foo') and one A(nil)" do
305
+ before do
306
+ subject.assert A.new("foo")
307
+ subject.assert A.new("foo")
308
+ subject.assert A.new
309
+ subject.match
310
+ end
311
+
312
+ it "should retrieve Success" do
313
+ s = subject.retrieve Success
314
+ s.should_not be_nil
315
+ s.size.should == 1
316
+
317
+ s = subject.retrieve Array
318
+ s.should_not be_nil
319
+ s.size.should == 1
320
+
321
+ a = s[0]
322
+ a.size.should == 2
323
+ a[0].object.class.should == A
324
+ a[1].object.class.should == A
325
+ end
326
+ end
327
+
328
+ context "with one A(nil)" do
329
+ before do
330
+ subject.assert A.new
331
+ subject.match
332
+ end
333
+
334
+ it "should not succeed" do
335
+ s = subject.retrieve Success
336
+ s.should_not be_nil
337
+ s.size.should == 0
338
+ end
339
+ end
340
+
341
+ context "with one B(foo,nil)" do
342
+ before do
343
+ subject.assert B.new("foo")
344
+ subject.match
345
+ end
346
+
347
+ it "should not succeed" do
348
+ s = subject.retrieve Success
349
+ s.should_not be_nil
350
+ s.size.should == 0
351
+ end
352
+ end
353
+
354
+ context "with one B(foo,bar)" do
355
+ before do
356
+ subject.assert B.new("foo", "bar")
357
+ subject.match
358
+ end
359
+
360
+ it "should not succeed" do
361
+ s = subject.retrieve Success
362
+ s.should_not be_nil
363
+ s.size.should == 1
364
+ end
365
+ end
366
+
367
+ context "with one B(foo,bar) and one B(foo,nil)" do
368
+ before do
369
+ subject.assert B.new("foo", "bar")
370
+ subject.assert B.new("foo", "bar")
371
+ subject.assert B.new("foo")
372
+ subject.match
373
+ end
374
+
375
+ it "should succeed" do
376
+ s = subject.retrieve Success
377
+ s.should_not be_nil
378
+ s.size.should == 1
379
+
380
+ s = subject.retrieve Array
381
+ s.should_not be_nil
382
+ s.size.should == 1
383
+
384
+ a = s[0]
385
+ a.size.should == 2
386
+ a[0].object.class.should == B
387
+ a[1].object.class.should == B
388
+ end
389
+ end
390
+ end
391
+
392
+ context "as one pattern inside AND" do
393
+ subject do
394
+ engine :engine do |e|
395
+ CollectRulebook.new(e).rules_with_one_pattern_inside_and
396
+ end
397
+ end
398
+
399
+ context "with one A" do
400
+ before do
401
+ subject.assert A.new
402
+ subject.match
403
+ end
404
+
405
+ it_should_behave_like "one :collect A rule and one A"
406
+ end
407
+
408
+ context "with more than one A" do
409
+ before do
410
+ subject.assert A.new
411
+ subject.assert A.new
412
+ subject.match
413
+ end
414
+
415
+ it_should_behave_like "one :collect A rule and two As"
416
+ end
417
+ end
418
+
419
+ context "as two patterns of same type" do
420
+ subject do
421
+ engine :engine do |e|
422
+ CollectRulebook.new(e).rules_with_two_collect_patterns_of_same_type
423
+ end
424
+ end
425
+
426
+ context "with one A" do
427
+ before do
428
+ subject.assert A.new
429
+ subject.match
430
+ end
431
+
432
+ it "should match" do
433
+ s = subject.retrieve Success
434
+ s.should_not be_nil
435
+ s.size.should == 1
436
+
437
+ s = subject.retrieve Array
438
+ s.should_not be_nil
439
+ s.size.should == 2
440
+
441
+ a = s[0]
442
+ a.size.should == 1
443
+ a[0].object.class.should == A
444
+
445
+ a = s[1]
446
+ a.size.should == 1
447
+ a[0].object.class.should == A
448
+ end
449
+ end
450
+ end
451
+
452
+ context "as two patterns of different type" do
453
+ subject do
454
+ engine :engine do |e|
455
+ CollectRulebook.new(e).rules_with_two_collect_patterns_of_different_type
456
+ end
457
+ end
458
+
459
+ context "with one A" do
460
+ before do
461
+ subject.assert A.new
462
+ subject.match
463
+ end
464
+
465
+ it "should not match" do
466
+ s = subject.retrieve Success
467
+ s.should_not be_nil
468
+ s.size.should == 0
469
+ end
470
+ end
471
+
472
+ context "with one A and one B" do
473
+ before do
474
+ subject.assert A.new
475
+ subject.assert B.new
476
+ subject.match
477
+ end
478
+
479
+ it "should not match" do
480
+ s = subject.retrieve Success
481
+ s.should_not be_nil
482
+ s.size.should == 1
483
+
484
+ s = subject.retrieve Array
485
+ s.should_not be_nil
486
+ s.size.should == 2
487
+
488
+ classes = []
489
+
490
+ a = s[0]
491
+ a.size.should == 1
492
+ classes << a[0].object.class
493
+
494
+ a = s[1]
495
+ a.size.should == 1
496
+ classes << a[0].object.class
497
+
498
+ classes.should include(A, B)
499
+ end
500
+ end
501
+ end
502
+
503
+ context "as one pattern and a not on left" do
504
+ subject do
505
+ engine :engine do |e|
506
+ CollectRulebook.new(e).rules_with_one_pattern_and_a_not_on_left
507
+ end
508
+ end
509
+
510
+ context "with one A" do
511
+ before do
512
+ subject.assert A.new
513
+ subject.match
514
+ end
515
+
516
+ it_should_behave_like "one :collect A rule and one A"
517
+ end
518
+
519
+ context "with more than one A" do
520
+ before do
521
+ subject.assert A.new
522
+ subject.assert A.new
523
+ subject.match
524
+ end
525
+
526
+ it_should_behave_like "one :collect A rule and two As"
527
+ end
528
+
529
+ context "with more than one A and a B" do
530
+ before do
531
+ subject.assert A.new
532
+ subject.assert A.new
533
+ subject.assert B.new
534
+ subject.match
535
+ end
536
+
537
+ it_should_behave_like "not patterns with [:collect, A] and [:not, B] when there is a B"
538
+ end
539
+
540
+ context "with more than one A and a B" do
541
+ before do
542
+ subject.assert B.new
543
+ subject.assert A.new
544
+ subject.assert A.new
545
+ subject.match
546
+ end
547
+
548
+ it_should_behave_like "not patterns with [:collect, A] and [:not, B] when there is a B"
549
+ end
550
+ end
551
+
552
+ context "as one pattern and a not on right" do
553
+ subject do
554
+ engine :engine do |e|
555
+ CollectRulebook.new(e).rules_with_one_pattern_and_a_not_on_right
556
+ end
557
+ end
558
+
559
+ context "with one A" do
560
+ before do
561
+ subject.assert A.new
562
+ subject.match
563
+ end
564
+
565
+ it_should_behave_like "one :collect A rule and one A"
566
+ end
567
+
568
+ context "with more than one A" do
569
+ before do
570
+ subject.assert A.new
571
+ subject.assert A.new
572
+ subject.match
573
+ end
574
+
575
+ it_should_behave_like "one :collect A rule and two As"
576
+ end
577
+
578
+ context "with more than one A's first and a B" do
579
+ before do
580
+ subject.assert A.new
581
+ subject.assert A.new
582
+ subject.assert B.new
583
+ subject.match
584
+ end
585
+
586
+ it_should_behave_like "not patterns with [:collect, A] and [:not, B] when there is a B"
587
+ end
588
+
589
+ context "with more than one A and a B first" do
590
+ before do
591
+ subject.assert B.new
592
+ subject.assert A.new
593
+ subject.assert A.new
594
+ subject.match
595
+ end
596
+
597
+ it_should_behave_like "not patterns with [:collect, A] and [:not, B] when there is a B"
598
+ end
599
+ end
600
+
601
+ context "as two patterns" do
602
+ subject do
603
+ engine :engine do |e|
604
+ CollectRulebook.new(e).rules_with_more_than_one_pattern
605
+ end
606
+ end
607
+
608
+ context "with one A" do
609
+ before do
610
+ subject.assert A.new
611
+ subject.assert B.new
612
+ subject.match
613
+ end
614
+
615
+ it "should retrieve Success" do
616
+ s = subject.retrieve Success
617
+ s.should_not be_nil
618
+ s.size.should == 2
619
+
620
+ s = subject.retrieve Array
621
+ s.should_not be_nil
622
+ s.size.should == 2
623
+
624
+ a = s[0]
625
+ a.size.should == 1
626
+ a[0].object.class.should == A
627
+
628
+ a = s[1]
629
+ a.size.should == 1
630
+ a[0].object.class.should == A
631
+ end
632
+ end
633
+
634
+ context "with more than one A" do
635
+ before do
636
+ subject.assert A.new
637
+ subject.assert B.new
638
+ subject.assert A.new
639
+ subject.match
640
+ end
641
+
642
+ it "should retrieve Success" do
643
+ s = subject.retrieve Success
644
+ s.should_not be_nil
645
+ s.size.should == 2
646
+
647
+ s = subject.retrieve Array
648
+ s.should_not be_nil
649
+ s.size.should == 2 # one array for each rule
650
+
651
+ a = s[0]
652
+ a.size.should == 2
653
+ a[0].object.class.should == A
654
+ a[1].object.class.should == A
655
+
656
+ a = s[1]
657
+ a.size.should == 2
658
+ a[0].object.class.should == A
659
+ a[1].object.class.should == A
660
+ end
661
+
662
+ it "should retract A without error" do
663
+ s = subject.retrieve Success
664
+ subject.retract s[0]
665
+ subject.retract s[1]
666
+ a = subject.retrieve A
667
+ subject.retract a[0]
668
+
669
+ subject.match
670
+
671
+ s = subject.retrieve Success
672
+ s.size.should == 2
673
+ a = subject.retrieve A
674
+ a.size.should == 1
675
+
676
+ s = subject.retrieve Success
677
+ subject.retract s[0]
678
+ subject.retract s[1]
679
+ a = subject.retrieve A
680
+ subject.retract a[0]
681
+
682
+ subject.match
683
+
684
+ s = subject.retrieve Success
685
+ s.size.should == 0
686
+ a = subject.retrieve A
687
+ a.size.should == 0
688
+ end
689
+
690
+ it "should retract B without error" do
691
+ s = subject.retrieve Success
692
+ subject.retract s[0]
693
+ subject.retract s[1]
694
+ b = subject.retrieve B
695
+ subject.retract b[0]
696
+
697
+ subject.match
698
+
699
+ s = subject.retrieve Success
700
+ s.size.should == 0
701
+ a = subject.retrieve A
702
+ a.size.should == 2
703
+ b = subject.retrieve B
704
+ b.size.should == 0
705
+ end
706
+ end
707
+ end
708
+
709
+ context "as patterns on each side" do
710
+ subject do
711
+ engine :engine do |e|
712
+ CollectRulebook.new(e).rules_with_more_than_one_pattern_on_each_side
713
+ end
714
+ end
715
+
716
+ context "with one A" do
717
+ context "and no C" do
718
+ before do
719
+ subject.assert A.new
720
+ subject.assert B.new
721
+ subject.match
722
+ end
723
+
724
+ it "should retrieve Success" do
725
+ s = subject.retrieve Success
726
+ s.should_not be_nil
727
+ s.size.should == 0
728
+ end
729
+ end
730
+
731
+ context "and all other facts" do
732
+ before do
733
+ subject.assert A.new
734
+ subject.assert B.new
735
+ subject.assert C.new
736
+ subject.match
737
+ end
738
+
739
+ it "should retrieve Success" do
740
+ s = subject.retrieve Success
741
+ s.should_not be_nil
742
+ s.size.should == 1
743
+
744
+ s = subject.retrieve Array
745
+ s.should_not be_nil
746
+ s.size.should == 1
747
+
748
+ a = s[0]
749
+ a.size.should == 1
750
+ a[0].object.class.should == A
751
+ end
752
+ end
753
+ end
754
+
755
+ context "with more than one A" do
756
+ before do
757
+ subject.assert A.new
758
+ subject.assert B.new
759
+ subject.assert C.new
760
+ subject.assert A.new
761
+ subject.match
762
+ end
763
+
764
+ it "should retrieve Success" do
765
+ s = subject.retrieve Success
766
+ s.should_not be_nil
767
+ s.size.should == 1
768
+
769
+ s = subject.retrieve Array
770
+ s.should_not be_nil
771
+ s.size.should == 1
772
+
773
+ a = s[0]
774
+ a.size.should == 2
775
+ a[0].object.class.should == A
776
+ a[1].object.class.should == A
777
+ end
778
+
779
+ it "should retract A without error" do
780
+ s = subject.retrieve Success
781
+ subject.retract s[0]
782
+ a = subject.retrieve A
783
+ subject.retract a[0]
784
+
785
+ subject.match
786
+
787
+ s = subject.retrieve Success
788
+ s.size.should == 1
789
+ a = subject.retrieve A
790
+ a.size.should == 1
791
+
792
+ s = subject.retrieve Success
793
+ subject.retract s[0]
794
+ a = subject.retrieve A
795
+ subject.retract a[0]
796
+
797
+ subject.match
798
+
799
+ s = subject.retrieve Success
800
+ s.size.should == 0
801
+ a = subject.retrieve A
802
+ a.size.should == 0
803
+ end
804
+
805
+ it "should retract B without error" do
806
+ s = subject.retrieve Success
807
+ subject.retract s[0]
808
+ b = subject.retrieve B
809
+ subject.retract b[0]
810
+
811
+ subject.match
812
+
813
+ s = subject.retrieve Success
814
+ s.size.should == 0
815
+ a = subject.retrieve A
816
+ a.size.should == 2
817
+ b = subject.retrieve B
818
+ b.size.should == 0
819
+ end
820
+
821
+ it "should retract C without error" do
822
+ s = subject.retrieve Success
823
+ subject.retract s[0]
824
+ b = subject.retrieve C
825
+ subject.retract b[0]
826
+
827
+ subject.match
828
+
829
+ s = subject.retrieve Success
830
+ s.size.should == 0
831
+ a = subject.retrieve A
832
+ a.size.should == 2
833
+ b = subject.retrieve C
834
+ b.size.should == 0
835
+ end
836
+ end
837
+ end
838
+
839
+ context "as rule chain" do
840
+ subject do
841
+ engine :engine do |e|
842
+ CollectRulebook.new(e).rules_with_chaining
843
+ end
844
+ end
845
+
846
+ context "with one C" do
847
+ before do
848
+ subject.assert C.new
849
+ subject.match
850
+ end
851
+
852
+ it "should retrieve Success" do
853
+ s = subject.retrieve Success
854
+ s.should_not be_nil
855
+ s.size.should == 1
856
+
857
+ s = subject.retrieve Array
858
+ s.should_not be_nil
859
+ s.size.should == 1
860
+
861
+ a = s[0]
862
+ a.size.should == 1
863
+ a[0].object.class.should == A
864
+ end
865
+ end
866
+
867
+ context "with many C's" do
868
+ before do
869
+ subject.assert C.new
870
+ subject.assert C.new
871
+ subject.assert C.new
872
+ subject.assert C.new
873
+ subject.assert C.new
874
+ subject.match
875
+ end
876
+
877
+ it "should retrieve Success" do
878
+ s = subject.retrieve Success
879
+ s.should_not be_nil
880
+ s.size.should == 1
881
+
882
+ s = subject.retrieve Array
883
+ s.should_not be_nil
884
+ s.size.should == 1
885
+
886
+ a = s[0]
887
+ a.size.should == 5
888
+ a[0].object.class.should == A
889
+ end
890
+ end
891
+ end
892
+
893
+ context "with rule binding" do
894
+ subject do
895
+ engine :engine do |e|
896
+ CollectRulebook.new(e).rules_with_binding
897
+ end
898
+ end
899
+
900
+ context "with one A and one B that have == values" do
901
+ before do
902
+ a = A.new
903
+ a.value = 1
904
+ b = B.new
905
+ b.value1 = 1
906
+ subject.assert b
907
+ subject.assert a
908
+ subject.match
909
+ end
910
+
911
+ it "should retrieve Success" do
912
+ s = subject.retrieve Success
913
+ s.size.should == 1
914
+ end
915
+ end
916
+
917
+ context "with one A and one B that have == values asserted in reverse order" do
918
+ before do
919
+ a = A.new
920
+ a.value = 1
921
+ b = B.new
922
+ b.value1 = 1
923
+ subject.assert a
924
+ subject.assert b
925
+ subject.match
926
+ end
927
+
928
+ it "should retrieve Success" do
929
+ s = subject.retrieve Success
930
+ s.size.should == 1
931
+ end
932
+ end
933
+
934
+ context "with one A and one B that have != values" do
935
+ before do
936
+ a = A.new
937
+ a.value = 1
938
+ b = B.new
939
+ b.value1 = 42
940
+ subject.assert a
941
+ subject.assert b
942
+ subject.match
943
+ end
944
+
945
+ it "should retrieve Success" do
946
+ s = subject.retrieve Success
947
+ s.size.should == 0
948
+ end
949
+ end
950
+
951
+ context "with one A and one B that have != values" do
952
+ before do
953
+ a1 = A.new
954
+ a1.value = 1
955
+ b1 = B.new
956
+ b1.value1 = 1
957
+ a2 = A.new
958
+ a2.value = 1
959
+ subject.assert a1
960
+ subject.assert b1
961
+ subject.assert a2
962
+ subject.match
963
+ end
964
+
965
+ it "should retrieve Success" do
966
+ s = subject.retrieve Success
967
+ s.size.should == 2
968
+ end
969
+ end
970
+ end
971
+
972
+
973
+ context "as rule chain with binding" do
974
+ subject do
975
+ engine :engine do |e|
976
+ CollectRulebook.new(e).rules_with_chaining_and_binding
977
+ end
978
+ end
979
+
980
+ context "with one B and one C" do
981
+ before do
982
+ subject.assert B.new
983
+ subject.assert C.new
984
+ subject.match
985
+ end
986
+
987
+ it "should retrieve Success" do
988
+ subject.retrieve(Success).size.should == 1
989
+ end
990
+ end
991
+
992
+ context "with many C's and two B" do
993
+ before do
994
+ subject.assert C.new
995
+ subject.assert C.new
996
+ subject.assert B.new
997
+ subject.assert C.new
998
+ subject.assert C.new
999
+ subject.assert C.new
1000
+ subject.assert B.new
1001
+ subject.match
1002
+ end
1003
+
1004
+ it "should retrieve Success" do
1005
+ subject.retrieve(Success).size.should == 2
1006
+
1007
+ s = subject.retrieve Array
1008
+ s.size.should == 2
1009
+
1010
+ a = s[0]
1011
+ a.size.should == 5
1012
+ a.each {|o| o.object.class.should == A }
1013
+
1014
+ a = s[1]
1015
+ a.size.should == 5
1016
+ a.each {|o| o.object.class.should == A }
1017
+ end
1018
+ end
1019
+ end
1020
+ end
1021
+ end