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.
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