wongi-engine 0.3.9 → 0.4.0.pre.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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 +16 -15
  8. data/lib/wongi-engine/beta/assignment_node.rb +7 -2
  9. data/lib/wongi-engine/beta/beta_node.rb +27 -23
  10. data/lib/wongi-engine/beta/filter_node.rb +8 -13
  11. data/lib/wongi-engine/beta/join_node.rb +15 -28
  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 +23 -55
  15. data/lib/wongi-engine/beta/optional_node.rb +24 -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-alpha1".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