wongi-engine 0.3.9 → 0.4.0.pre.alpha2

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +2 -2
  3. data/.gitignore +2 -0
  4. data/README.md +12 -12
  5. data/lib/wongi-engine/alpha_index.rb +58 -0
  6. data/lib/wongi-engine/alpha_memory.rb +2 -24
  7. data/lib/wongi-engine/beta/aggregate_node.rb +17 -15
  8. data/lib/wongi-engine/beta/assignment_node.rb +7 -2
  9. data/lib/wongi-engine/beta/beta_node.rb +36 -23
  10. data/lib/wongi-engine/beta/filter_node.rb +8 -13
  11. data/lib/wongi-engine/beta/join_node.rb +17 -29
  12. data/lib/wongi-engine/beta/ncc_node.rb +14 -26
  13. data/lib/wongi-engine/beta/ncc_partner.rb +18 -18
  14. data/lib/wongi-engine/beta/neg_node.rb +25 -55
  15. data/lib/wongi-engine/beta/optional_node.rb +26 -48
  16. data/lib/wongi-engine/beta/or_node.rb +24 -1
  17. data/lib/wongi-engine/beta/production_node.rb +9 -3
  18. data/lib/wongi-engine/beta/root_node.rb +47 -0
  19. data/lib/wongi-engine/beta.rb +1 -1
  20. data/lib/wongi-engine/compiler.rb +6 -34
  21. data/lib/wongi-engine/dsl/action/{base.rb → base_action.rb} +5 -1
  22. data/lib/wongi-engine/dsl/action/error_generator.rb +1 -1
  23. data/lib/wongi-engine/dsl/action/simple_action.rb +1 -1
  24. data/lib/wongi-engine/dsl/action/simple_collector.rb +1 -1
  25. data/lib/wongi-engine/dsl/action/statement_generator.rb +21 -22
  26. data/lib/wongi-engine/dsl/action/trace_action.rb +1 -1
  27. data/lib/wongi-engine/dsl/clause/fact.rb +2 -6
  28. data/lib/wongi-engine/dsl.rb +1 -25
  29. data/lib/wongi-engine/graph.rb +1 -1
  30. data/lib/wongi-engine/network/debug.rb +2 -10
  31. data/lib/wongi-engine/network.rb +44 -105
  32. data/lib/wongi-engine/overlay.rb +589 -0
  33. data/lib/wongi-engine/template.rb +22 -2
  34. data/lib/wongi-engine/token.rb +10 -26
  35. data/lib/wongi-engine/token_assignment.rb +15 -0
  36. data/lib/wongi-engine/version.rb +1 -1
  37. data/lib/wongi-engine/wme.rb +10 -39
  38. data/lib/wongi-engine.rb +3 -1
  39. data/spec/alpha_index_spec.rb +78 -0
  40. data/spec/bug_specs/issue_4_spec.rb +11 -11
  41. data/spec/high_level_spec.rb +8 -101
  42. data/spec/network_spec.rb +8 -6
  43. data/spec/overlay_spec.rb +161 -3
  44. data/spec/rule_specs/any_rule_spec.rb +39 -0
  45. data/spec/rule_specs/assign_spec.rb +1 -1
  46. data/spec/rule_specs/maybe_rule_spec.rb +58 -1
  47. data/spec/rule_specs/ncc_spec.rb +78 -19
  48. data/spec/rule_specs/negative_rule_spec.rb +12 -14
  49. data/spec/spec_helper.rb +4 -0
  50. data/spec/wme_spec.rb +0 -32
  51. metadata +11 -9
  52. data/lib/wongi-engine/beta/beta_memory.rb +0 -60
  53. data/lib/wongi-engine/data_overlay.rb +0 -149
  54. data/spec/rule_specs/or_rule_spec.rb +0 -40
@@ -1,5 +1,5 @@
1
1
  module Wongi
2
2
  module Engine
3
- VERSION = "0.3.9".freeze
3
+ VERSION = "0.4.0-alpha2".freeze
4
4
  end
5
5
  end
@@ -1,49 +1,20 @@
1
1
  module Wongi::Engine
2
2
  WME = Struct.new(:subject, :predicate, :object) do
3
- include CoreExt
3
+ def self.from_concrete_template(template)
4
+ raise "template #{template} is not concrete" unless template.concrete?
4
5
 
5
- attr_reader :rete
6
-
7
- attr_reader :generating_tokens
8
- attr_reader :neg_join_results, :opt_join_results
9
- attr_accessor :overlay
10
-
11
- attr_predicate :deleted
12
- attr_predicate :manual
13
-
14
- def initialize(s, p, o, r = nil)
15
- manual!
16
-
17
- @deleted = false
18
- @alphas = []
19
- @generating_tokens = []
20
- @neg_join_results = []
21
- @opt_join_results = []
22
-
23
- @rete = r
24
-
25
- # TODO: reintroduce Network#import when bringing back RDF support
26
- super(s, p, o)
27
- end
28
-
29
- def import_into(r)
30
- self.class.new(subject, predicate, object, r).tap do |wme|
31
- wme.overlay = overlay
32
- wme.manual = manual?
33
- end
6
+ new(template.subject, template.predicate, template.object)
34
7
  end
35
8
 
36
9
  def dup
37
- self.class.new(subject, predicate, object, rete).tap do |wme|
38
- wme.overlay = overlay
39
- wme.manual = manual?
40
- end
10
+ self.class.new(subject, predicate, object)
41
11
  end
42
12
 
43
13
  def ==(other)
44
- subject == other.subject && predicate == other.predicate && object == other.object
14
+ other && subject == other.subject && predicate == other.predicate && object == other.object
45
15
  end
46
16
 
17
+ # @param template Wongi::Engine::Template
47
18
  def =~(template)
48
19
  raise Wongi::Engine::Error, "Cannot match a WME against a #{template.class}" unless template.is_a?(Template)
49
20
 
@@ -51,10 +22,6 @@ module Wongi::Engine
51
22
  result if result.match?
52
23
  end
53
24
 
54
- def generated?
55
- !generating_tokens.empty?
56
- end
57
-
58
25
  def inspect
59
26
  "{#{subject.inspect} #{predicate.inspect} #{object.inspect}}"
60
27
  end
@@ -67,6 +34,10 @@ module Wongi::Engine
67
34
  @hash ||= [subject.hash, predicate.hash, object.hash].hash
68
35
  end
69
36
 
37
+ def eql?(other)
38
+ subject.eql?(other.subject) && predicate.eql?(other.predicate) && object.eql?(other.object)
39
+ end
40
+
70
41
  protected
71
42
 
72
43
  def match_member(mine, theirs)
data/lib/wongi-engine.rb CHANGED
@@ -21,12 +21,14 @@ require 'wongi-engine/wme'
21
21
  require 'wongi-engine/wme_match_data'
22
22
  require 'wongi-engine/token'
23
23
  require 'wongi-engine/filter'
24
+ require 'wongi-engine/alpha_index'
24
25
  require 'wongi-engine/alpha_memory'
26
+ require 'wongi-engine/token_assignment'
25
27
  require 'wongi-engine/beta'
26
28
  require 'wongi-engine/dsl'
27
29
  require 'wongi-engine/ruleset'
28
30
  require 'wongi-engine/compiler'
29
- require 'wongi-engine/data_overlay'
31
+ require 'wongi-engine/overlay'
30
32
  require 'wongi-engine/enumerators'
31
33
  require 'wongi-engine/network'
32
34
  require 'wongi-engine/graph'
@@ -0,0 +1,78 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ describe Wongi::Engine::AlphaIndex do
5
+ let(:index) { described_class.new(pattern) }
6
+ let(:wme) { Wongi::Engine::WME.new(1, 2, 3) }
7
+
8
+ # private access
9
+ let(:collection) { index.send(:index) }
10
+
11
+ before do
12
+ index.add(wme)
13
+ end
14
+
15
+ context 'index by subject' do
16
+ let(:pattern) { %i[subject] }
17
+ let(:key) { hashed_key([1]) }
18
+
19
+ it 'indexes by pattern' do
20
+ expect(collection.keys).to eq([key])
21
+ expect(collection[key]).to eq([wme])
22
+ end
23
+ end
24
+
25
+ context 'index by predicate' do
26
+ let(:pattern) { %i[predicate] }
27
+ let(:key) { hashed_key([2]) }
28
+
29
+ it 'indexes by pattern' do
30
+ expect(collection.keys).to eq([key])
31
+ expect(collection[key]).to eq([wme])
32
+ end
33
+ end
34
+
35
+ context 'index by object' do
36
+ let(:pattern) { %i[object] }
37
+ let(:key) { hashed_key([3]) }
38
+
39
+ it 'indexes by pattern' do
40
+ expect(collection.keys).to eq([key])
41
+ expect(collection[key]).to eq([wme])
42
+ end
43
+ end
44
+
45
+ context 'index by subject and predicate' do
46
+ let(:pattern) { %i[subject predicate] }
47
+ let(:key) { hashed_key([1, 2]) }
48
+
49
+ it 'indexes by pattern' do
50
+ expect(collection.keys).to eq([key])
51
+ expect(collection[key]).to eq([wme])
52
+ end
53
+ end
54
+
55
+ context 'index by subject and object' do
56
+ let(:pattern) { %i[subject object] }
57
+ let(:key) { hashed_key([1, 3]) }
58
+
59
+ it 'indexes by pattern' do
60
+ expect(collection.keys).to eq([key])
61
+ expect(collection[key]).to eq([wme])
62
+ end
63
+ end
64
+
65
+ context 'index by predicate and object' do
66
+ let(:pattern) { %i[predicate object] }
67
+ let(:key) { hashed_key([2, 3]) }
68
+
69
+ it 'indexes by pattern' do
70
+ expect(collection.keys).to eq([key])
71
+ expect(collection[key]).to eq([wme])
72
+ end
73
+ end
74
+
75
+ def hashed_key(key)
76
+ key.map(&:hash)
77
+ end
78
+ end
@@ -22,8 +22,8 @@ describe "issue 4" do
22
22
  numbers = engine.select :_, :is_number, true
23
23
  evens = engine.select :_, :is_even, true
24
24
 
25
- expect(numbers).to be_empty
26
- expect(evens.length).to eq(10)
25
+ expect(numbers.size).to eq(0)
26
+ expect(evens.size).to eq(10)
27
27
  end
28
28
 
29
29
  it "should correctly retract post-added items from within a rule" do
@@ -42,13 +42,13 @@ describe "issue 4" do
42
42
  }
43
43
  end
44
44
 
45
- 10.times { |i| engine << [i, :is_number, true] }
45
+ 5.times { |i| engine << [i, :is_number, true] }
46
46
 
47
47
  numbers = engine.select :_, :is_number, true
48
48
  evens = engine.select :_, :is_even, true
49
49
 
50
- expect(numbers).to be_empty
51
- expect(evens.length).to eq(10)
50
+ expect(numbers.size).to eq(0)
51
+ expect(evens.size).to eq(5)
52
52
  end
53
53
 
54
54
  # cascaded processing affects this
@@ -80,9 +80,9 @@ describe "issue 4" do
80
80
  evens = engine.select :_, :is_even, true
81
81
  odds = engine.select :_, :is_odd, true
82
82
 
83
- expect(numbers).to be_empty
84
- expect(evens).to have(5).items
85
- expect(odds).to have(5).items
83
+ expect(numbers.size).to eq(0)
84
+ expect(evens.size).to eq(5)
85
+ expect(odds.size).to eq(5)
86
86
  end
87
87
 
88
88
  it "should not lose track when another rule affects a set" do
@@ -126,8 +126,8 @@ describe "issue 4" do
126
126
  evens = engine.select :_, :is_even, true
127
127
  odds = engine.select :_, :is_odd, true
128
128
 
129
- expect(numbers.length).to eq(5)
130
- expect(evens.length).to eq(5)
131
- expect(odds.length).to eq(5)
129
+ expect(numbers.size).to eq(5)
130
+ expect(evens.size).to eq(5)
131
+ expect(odds.size).to eq(5)
132
132
  end
133
133
  end
@@ -30,9 +30,9 @@ describe 'the engine' do
30
30
  engine << Wongi::Engine::WME.new("Alice", "friend", "Bob")
31
31
 
32
32
  expect(engine.facts.to_a.length).to eq(3)
33
- expect(engine.facts.select(&:manual?).length).to eq(2)
34
- generated = engine.facts.find(&:generated?)
35
- expect(generated).to be == Wongi::Engine::WME.new("Bob", "friend", "Alice")
33
+ expect(engine.facts.select { engine.current_overlay.manual?(_1) }.length).to eq(2)
34
+ generated = engine.facts.select { engine.current_overlay.generated?(_1) }
35
+ expect(generated).to eq([Wongi::Engine::WME.new("Bob", "friend", "Alice")])
36
36
  end
37
37
 
38
38
  it 'should generate wmes with an added rule' do
@@ -51,8 +51,8 @@ describe 'the engine' do
51
51
  }
52
52
  }
53
53
 
54
- expect(engine.facts.to_a.length).to eq(3)
55
- expect(engine.facts.select(&:manual?).size).to eq(2)
54
+ expect(engine.facts.to_a.size).to eq(3)
55
+ expect(engine.facts.select { engine.current_overlay.manual?(_1) }.size).to eq(2)
56
56
  end
57
57
 
58
58
  it 'should not get confused by recursive activations' do
@@ -70,9 +70,9 @@ describe 'the engine' do
70
70
  engine << [:p, "reflexive", true]
71
71
  engine << %i[x p y]
72
72
 
73
- expect(engine.wmes.to_a.length).to eq(4)
74
- expect(engine.select(:x, :p, :x).length).to eq(1)
75
- expect(engine.select(:y, :p, :y).length).to eq(1)
73
+ expect(engine.wmes.count).to eq(4)
74
+ expect(engine.select(:x, :p, :x).count).to eq(1)
75
+ expect(engine.select(:y, :p, :y).count).to eq(1)
76
76
  end
77
77
  end
78
78
 
@@ -236,97 +236,4 @@ describe 'the engine' do
236
236
  expect(engine.results["test-query"].size).to eq(1)
237
237
  end
238
238
  end
239
-
240
- context 'with timelines' do
241
- it 'should not match with no past point' do
242
- production = engine.rule {
243
- forall {
244
- has 1, 2, 3, time: -1
245
- }
246
- }
247
-
248
- expect(production.size).to eq(0)
249
-
250
- engine << [1, 2, 3]
251
-
252
- expect(production.size).to eq(0)
253
- end
254
-
255
- it 'should match a simple past point' do
256
- production = engine.rule {
257
- forall {
258
- has 1, 2, 3, time: -1
259
- }
260
- }
261
-
262
- engine << [1, 2, 3]
263
- engine.snapshot!
264
-
265
- expect(production.size).to eq(1)
266
- end
267
-
268
- context 'using the :asserted clause' do
269
- it 'should match asserted items' do
270
- count = 0
271
- production = engine.rule do
272
- forall {
273
- asserted 1, 2, 3
274
- }
275
- make { action { count += 1 } }
276
- end
277
- expect(production.size).to eq(0)
278
- engine.snapshot!
279
- engine << [1, 2, 3]
280
- expect(production.size).to eq(1)
281
- # puts count
282
- end
283
-
284
- it 'should not match kept items' do
285
- count = 0
286
- production = engine.rule do
287
- forall {
288
- asserted 1, 2, 3
289
- }
290
- make { action { count += 1 } }
291
- end
292
- engine << [1, 2, 3]
293
- expect(production.size).to eq(1)
294
- engine.snapshot!
295
- expect(production.size).to eq(0)
296
- # puts count
297
- end
298
- end
299
-
300
- context 'using the :kept clause' do
301
- it 'should match kept items' do
302
- count = 0
303
- production = engine.rule do
304
- forall {
305
- kept 1, 2, 3
306
- }
307
- make { action { count += 1 } }
308
- end
309
- engine << [1, 2, 3]
310
- expect(production.size).to eq(0)
311
- engine.snapshot!
312
- expect(production.size).to eq(1)
313
- # puts count
314
- end
315
-
316
- it 'should not match asserted wmes' do
317
- count = 0
318
- production = engine.rule do
319
- forall {
320
- kept 1, 2, 3
321
- }
322
- make { action { count += 1 } }
323
- end
324
- expect(production.size).to eq(0)
325
- engine.snapshot!
326
- engine << [1, 2, 3]
327
- expect(production.size).to eq(0)
328
- # puts count
329
- end
330
- end
331
- end
332
239
  end
data/spec/network_spec.rb CHANGED
@@ -14,7 +14,7 @@ describe Wongi::Engine::Network do
14
14
  it 'should retract facts' do
15
15
  subject << [1, 2, 3]
16
16
  subject.retract [1, 2, 3]
17
- expect(subject.select(:_, 2, :_)).to be_empty
17
+ expect(subject.select(:_, 2, :_).count).to eq(0)
18
18
  end
19
19
 
20
20
  it 'asserted facts end up in productions' do
@@ -80,7 +80,7 @@ describe Wongi::Engine::Network do
80
80
  end
81
81
 
82
82
  it 'retracted facts should propagate through join chains' do
83
- deactivated = nil
83
+ assignments = nil
84
84
 
85
85
  prod = engine << rule {
86
86
  forall {
@@ -88,7 +88,9 @@ describe Wongi::Engine::Network do
88
88
  has :Y, :is, :Z
89
89
  }
90
90
  make {
91
- action deactivate: ->(token) { deactivated = token }
91
+ action deactivate: lambda { |token|
92
+ assignments = token.assignments
93
+ }
92
94
  }
93
95
  }
94
96
 
@@ -99,9 +101,9 @@ describe Wongi::Engine::Network do
99
101
 
100
102
  engine.retract [1, :is, 2]
101
103
  expect(prod).to have(0).tokens
102
- expect(deactivated[:X]).to be == 1
103
- expect(deactivated[:Y]).to be == 2
104
- expect(deactivated[:Z]).to be == 3
104
+ expect(assignments[:X].call).to be == 1
105
+ expect(assignments[:Y].call).to be == 2
106
+ expect(assignments[:Z].call).to be == 3
105
107
  end
106
108
 
107
109
  it 'retraction should reactivate neg nodes' do
data/spec/overlay_spec.rb CHANGED
@@ -1,16 +1,157 @@
1
1
  require 'spec_helper'
2
+ require 'wongi-engine/alpha_index'
3
+ require 'wongi-engine/overlay'
4
+ require 'wongi-engine/wme'
2
5
 
3
- describe Wongi::Engine::DataOverlay do
6
+ describe Wongi::Engine::Overlay do
4
7
  include Wongi::Engine::DSL
5
8
 
6
9
  let(:engine) { Wongi::Engine.create }
7
10
 
11
+ describe "asserting facts" do
12
+ let(:engine) { double(:engine) }
13
+ let(:overlay) { Wongi::Engine::Overlay.new(engine) }
14
+
15
+ before do
16
+ # this cannot be put in the double constructor because it causes an infinite recursion
17
+ allow(engine).to receive(:default_overlay).and_return(overlay)
18
+ allow(engine).to receive(:real_assert)
19
+ end
20
+
21
+ it 'stores WMEs and retrieves by identity' do
22
+ wmes = [
23
+ Wongi::Engine::WME.new(1, 11, 111),
24
+ Wongi::Engine::WME.new(1, 11, 112),
25
+ Wongi::Engine::WME.new(1, 11, 113),
26
+ Wongi::Engine::WME.new(1, 12, 121),
27
+ Wongi::Engine::WME.new(1, 12, 122),
28
+ Wongi::Engine::WME.new(2, 22, 222),
29
+ Wongi::Engine::WME.new(2, 22, 223),
30
+ Wongi::Engine::WME.new(3, 33, 333),
31
+ Wongi::Engine::WME.new(3, 34, 333),
32
+ ]
33
+ wmes.each { overlay.assert(_1) }
34
+
35
+ wmes.each { expect(overlay.find(_1)).to equal(_1) }
36
+ end
37
+
38
+ it 'stores WMEs and retrieves by template' do
39
+ wmes = [
40
+ Wongi::Engine::WME.new(1, 11, 111),
41
+ Wongi::Engine::WME.new(1, 11, 112),
42
+ Wongi::Engine::WME.new(1, 11, 113),
43
+ Wongi::Engine::WME.new(1, 12, 121),
44
+ Wongi::Engine::WME.new(1, 12, 122),
45
+ Wongi::Engine::WME.new(2, 11, 111),
46
+ Wongi::Engine::WME.new(2, 11, 222),
47
+ Wongi::Engine::WME.new(2, 22, 222),
48
+ Wongi::Engine::WME.new(2, 22, 223),
49
+ Wongi::Engine::WME.new(3, 33, 113),
50
+ Wongi::Engine::WME.new(3, 33, 333),
51
+ Wongi::Engine::WME.new(3, 34, 333),
52
+ ]
53
+ wmes.each { overlay.assert(_1) }
54
+
55
+ expect(overlay.select(1, :_, :_)).to have(5).items
56
+ expect(overlay.select(2, :_, :_)).to have(4).items
57
+ expect(overlay.select(3, :_, :_)).to have(3).items
58
+ expect(overlay.select(1, 11, :_)).to have(3).items
59
+ expect(overlay.select(:_, 11, :_)).to have(5).items
60
+ expect(overlay.select(:_, :_, 113)).to have(2).items
61
+ expect(overlay.select(:_, 22, :_)).to have(2).items
62
+ expect(overlay.select(:_, 22, 222)).to have(1).items
63
+ expect(overlay.select(:_, :_, 222)).to have(2).items
64
+ expect(overlay.select(:_, :_, 223)).to have(1).items
65
+
66
+ expect(overlay.select(:_, :_, :_)).to have(wmes.length).items
67
+
68
+ expect(overlay.select(1, 11, 111)).to have(1).items
69
+ expect(overlay.select(1, 11, 111).first).to equal(wmes.first)
70
+ end
71
+ end
72
+
73
+ context "retracting facts" do
74
+ let(:engine) { double(:engine) }
75
+ let(:overlay) { Wongi::Engine::Overlay.new(engine) }
76
+
77
+ before do
78
+ # this cannot be put in the double constructor because it causes an infinite recursion
79
+ allow(engine).to receive(:default_overlay).and_return(overlay)
80
+ allow(engine).to receive(:real_assert)
81
+ allow(engine).to receive(:real_retract)
82
+ end
83
+
84
+ it "removes asserted facts" do
85
+ wme = Wongi::Engine::WME.new(1, 11, 111)
86
+
87
+ overlay.assert(wme)
88
+ expect(overlay.select(:_, :_, :_)).to have(1).items
89
+
90
+ overlay.retract(wme)
91
+ expect(overlay.select(:_, :_, :_)).to have(0).items
92
+ end
93
+ end
94
+
95
+ context "layered" do
96
+ let(:engine) { double(:engine) }
97
+ let(:overlay) { Wongi::Engine::Overlay.new(engine) }
98
+
99
+ before do
100
+ # this cannot be put in the double constructor because it causes an infinite recursion
101
+ allow(engine).to receive(:default_overlay).and_return(overlay)
102
+ allow(engine).to receive(:real_assert)
103
+ allow(engine).to receive(:real_retract)
104
+ end
105
+ it "maintains visibility on each layer" do
106
+ child1 = overlay.new_child
107
+ child2 = child1.new_child
108
+
109
+ wme = Wongi::Engine::WME.new(1, 2, 3)
110
+
111
+ overlay.assert(wme)
112
+ expect(overlay.find(wme)).to eq(wme)
113
+ expect(child1.find(wme)).to eq(wme)
114
+ expect(child2.find(wme)).to eq(wme)
115
+
116
+ child1.retract(wme)
117
+ expect(overlay.find(wme)).to eq(wme)
118
+ expect(child1.find(wme)).to be_nil
119
+ expect(child2.find(wme)).to be_nil
120
+
121
+ child2.assert(wme)
122
+ expect(overlay.find(wme)).to eq(wme)
123
+ expect(child1.find(wme)).to be_nil
124
+ expect(child2.find(wme)).to eq(wme)
125
+
126
+ child2.retract(wme)
127
+ expect(overlay.find(wme)).to eq(wme)
128
+ expect(child1.find(wme)).to be_nil
129
+ expect(child2.find(wme)).to be_nil
130
+
131
+ child1.assert(wme)
132
+ expect(overlay.find(wme)).to eq(wme)
133
+ expect(child1.find(wme)).to eq(wme)
134
+ expect(child2.find(wme)).to eq(wme)
135
+
136
+ overlay.retract(wme)
137
+ expect(overlay.find(wme)).to be_nil
138
+ expect(child1.find(wme)).to be_nil
139
+ expect(child2.find(wme)).to be_nil
140
+
141
+ child1.assert(wme)
142
+ expect(overlay.find(wme)).to be_nil
143
+ expect(child1.find(wme)).to eq(wme)
144
+ expect(child2.find(wme)).to eq(wme)
145
+ end
146
+ end
147
+
8
148
  it 'should be disposable' do
9
149
  production = engine << rule {
10
150
  forall {
11
151
  has 1, 2, :X
12
152
  }
13
153
  }
154
+
14
155
  engine.with_overlay { |overlay|
15
156
  overlay << [1, 2, 3]
16
157
  expect(production).to have(1).token
@@ -18,6 +159,24 @@ describe Wongi::Engine::DataOverlay do
18
159
  expect(production).to have(0).tokens
19
160
  end
20
161
 
162
+ it 'works with retractions' do
163
+ production = engine << rule {
164
+ forall {
165
+ has 1, 2, :X
166
+ }
167
+ }
168
+
169
+ engine << [1, 2, 3]
170
+ expect(production).to have(1).token
171
+
172
+ engine.with_overlay do |overlay|
173
+ overlay.retract [1, 2, 3]
174
+ expect(production).to have(0).token
175
+ end
176
+
177
+ expect(production).to have(1).token
178
+ end
179
+
21
180
  it 'should generate into correct overlays' do
22
181
  production = engine << rule {
23
182
  forall {
@@ -46,12 +205,11 @@ describe Wongi::Engine::DataOverlay do
46
205
  expect(prod).to have(1).tokens
47
206
 
48
207
  engine.with_overlay do |overlay|
49
- overlay << [:x, :y, :z]
208
+ overlay << %i[x y z]
50
209
  expect(prod).to have(0).tokens
51
210
  end
52
211
 
53
212
  expect(prod).to have(1).tokens
54
-
55
213
  end
56
214
 
57
215
  it 'works with assignments' do
@@ -27,6 +27,45 @@ describe "ANY rule" do
27
27
  end
28
28
  end
29
29
 
30
+ context 'with two options' do
31
+ let :production do
32
+ engine << rule do
33
+ forall {
34
+ any {
35
+ option {
36
+ has :A, :path1, :_
37
+ }
38
+ option {
39
+ has :A, :path2, :_
40
+ }
41
+ }
42
+ }
43
+ end
44
+ end
45
+
46
+ it 'should fire on the first path' do
47
+ engine << [:x, :path1, true]
48
+ expect(production.tokens).to have(1).item
49
+ end
50
+
51
+ it 'should fire on the second path' do
52
+ engine << [:x, :path2, true]
53
+ expect(production.tokens).to have(1).item
54
+ end
55
+
56
+ it 'should fire twice on both paths at once and be retractable' do
57
+ engine << [:x, :path1, true]
58
+ engine << [:x, :path2, true]
59
+ expect(production.tokens).to have(2).items
60
+
61
+ engine.retract [:x, :path2, true]
62
+ expect(production.tokens).to have(1).items
63
+
64
+ engine.retract [:x, :path1, true]
65
+ expect(production.tokens).to have(0).items
66
+ end
67
+ end
68
+
30
69
  context "with several options" do
31
70
  specify "all matching branches must pass" do
32
71
  engine << rule('two-options') {
@@ -80,7 +80,7 @@ describe "ASSIGN rule" do
80
80
  expect(x).to be == 1
81
81
  end
82
82
 
83
- xit 'should handle booleans' do
83
+ it 'should handle booleans' do
84
84
  engine << rule do
85
85
  forall {
86
86
  has :a, :b, :c