wongi-engine 0.4.0.pre.alpha2 → 0.4.0.pre.alpha3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -3
  3. data/lib/wongi-engine/beta/ncc_partner.rb +1 -1
  4. data/lib/wongi-engine/beta/neg_node.rb +2 -5
  5. data/lib/wongi-engine/compiler.rb +2 -2
  6. data/lib/wongi-engine/dsl/action/statement_generator.rb +34 -10
  7. data/lib/wongi-engine/dsl/clause/fact.rb +1 -4
  8. data/lib/wongi-engine/version.rb +1 -1
  9. data/spec/action_class_spec.rb +1 -1
  10. data/spec/{rule_specs/aggregate_spec.rb → aggregate_spec.rb} +1 -1
  11. data/spec/{rule_specs/any_rule_spec.rb → any_rule_spec.rb} +4 -4
  12. data/spec/{filter_specs/assert_test_spec.rb → assert_test_spec.rb} +5 -5
  13. data/spec/{rule_specs/assign_spec.rb → assign_spec.rb} +6 -6
  14. data/spec/{rule_specs/assuming_spec.rb → assuming_spec.rb} +3 -3
  15. data/spec/beta_node_spec.rb +1 -1
  16. data/spec/bug_specs/issue_4_spec.rb +4 -4
  17. data/spec/dataset_spec.rb +1 -1
  18. data/spec/generation_spec.rb +7 -7
  19. data/spec/{filter_specs/greater_than_equality_test_spec.rb → greater_than_equality_test_spec.rb} +1 -1
  20. data/spec/high_level_spec.rb +12 -12
  21. data/spec/{filter_specs/less_test_spec.rb → less_test_spec.rb} +1 -1
  22. data/spec/{filter_specs/less_than_equality_test_spec.rb → less_than_equality_test_spec.rb} +1 -1
  23. data/spec/{rule_specs/maybe_rule_spec.rb → maybe_rule_spec.rb} +7 -7
  24. data/spec/{rule_specs/ncc_spec.rb → ncc_spec.rb} +28 -5
  25. data/spec/{rule_specs/negative_rule_spec.rb → negative_rule_spec.rb} +3 -25
  26. data/spec/network_spec.rb +4 -3
  27. data/spec/overlay_spec.rb +3 -2
  28. data/spec/ruleset_spec.rb +6 -6
  29. data/spec/simple_action_spec.rb +1 -1
  30. data/spec/wme_spec.rb +4 -4
  31. metadata +14 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 49eb7efc3f4049b38a7035208884d2078e0e31e68d396fb647deb81add7a030c
4
- data.tar.gz: 6916568d628dbfce536001472c224f0fcee9ab580d4d9f2df30f04edb77bb45d
3
+ metadata.gz: d3401b29a9b186b8594549d45feddda88e885dae9a03d42f7006ad7e6fecbd0b
4
+ data.tar.gz: c315eb40f0c2a05d6a5995016fd700b6ecdb7795588fa929b81b68d86722c481
5
5
  SHA512:
6
- metadata.gz: 437466d4256f5ff96bf741e879054f2e5a7499f3876ed73cbd00b155caf6b5f06cfb60378941e36cba6dd912b8d61cbe8b4cdfec58a0b60f121841cdd63c3bd9
7
- data.tar.gz: 5e8ca5469c6b20cb63928a0c4c128f3391ae74c29e09608b0a7045d6d979bc03d9d8b95f49f4282898531ee8f69e7bd9abcd8ebb8164aa4d612292e01b509be3
6
+ metadata.gz: ac9b80adde1174fec2361b6411d500a2c5c3c4c2659bac11d42bc630b088ccede7e3bf7d407560d099206daec5da97b0379d4c949e9891c9981d2f5847a815aa
7
+ data.tar.gz: 03fa343a219317c31384446debde04894d00841bd6b780a45af31968fc5a1af8fb5ca04f2d6bf17bd445e046ab8de76828c797da47ac89f68715405e128a404a
data/.rubocop.yml CHANGED
@@ -42,9 +42,6 @@ Naming/MethodParameterName:
42
42
  Layout/LineLength:
43
43
  Enabled: false
44
44
 
45
- RSpec:
46
- Enabled: false
47
-
48
45
  Style/SafeNavigation:
49
46
  Enabled: false
50
47
 
@@ -25,7 +25,7 @@ module Wongi
25
25
  overlay.remove_token(token)
26
26
  return unless owner
27
27
 
28
- ncc.ncc_activate owner if overlay.ncc_tokens_for(owner).empty?
28
+ ncc.ncc_activate(owner) if overlay.ncc_tokens_for(owner).empty?
29
29
  end
30
30
 
31
31
  def owner_for(token)
@@ -5,17 +5,16 @@ module Wongi
5
5
  class NegNode < BetaNode
6
6
  attr_reader :alpha, :tests
7
7
 
8
- def initialize(parent, tests, alpha, unsafe)
8
+ def initialize(parent, tests, alpha)
9
9
  super(parent)
10
10
  @tests = tests
11
11
  @alpha = alpha
12
- @unsafe = unsafe
13
12
  end
14
13
 
15
14
  def alpha_activate(wme, children: self.children)
16
15
  # p alpha_activate: {class: self.class, object_id:, wme:}
17
16
  tokens.each do |token|
18
- next unless matches?(token, wme) && (@unsafe || !token.generated?(wme)) # feedback loop protection
17
+ next unless matches?(token, wme)
19
18
 
20
19
  # order matters for proper invalidation
21
20
  overlay.add_neg_join_result(NegJoinResult.new(token, wme))
@@ -71,8 +70,6 @@ module Wongi
71
70
  end
72
71
  end
73
72
 
74
- protected
75
-
76
73
  def matches?(token, wme)
77
74
  puts "matching #{wme} against #{token}" if debug?
78
75
  @tests.each do |test|
@@ -42,9 +42,9 @@ module Wongi::Engine
42
42
  node.tap(&:refresh)
43
43
  end
44
44
 
45
- def neg_node(condition, tests, unsafe)
45
+ def neg_node(condition, tests)
46
46
  alpha = rete.compile_alpha(condition)
47
- self.node = NegNode.new(node, tests, alpha, unsafe).tap do |node|
47
+ self.node = NegNode.new(node, tests, alpha).tap do |node|
48
48
  alpha.betas << node unless alpha_deaf
49
49
  node.refresh
50
50
  end
@@ -1,3 +1,5 @@
1
+ require "set"
2
+
1
3
  module Wongi::Engine
2
4
  module DSL::Action
3
5
  class StatementGenerator < BaseAction
@@ -16,23 +18,45 @@ module Wongi::Engine
16
18
  subject, predicate, object = template.resolve!(token)
17
19
 
18
20
  wme = WME.new(subject, predicate, object)
19
-
20
- origin = GeneratorOrigin.new(token, self)
21
+ wme = overlay.find(wme) || wme
21
22
 
22
23
  production.tracer.trace(action: self, wme: wme) if production.tracer
23
- if (existing = overlay.find(wme))
24
- # do not mark purely manual tokens as generated, because a circular rule such as the symmetric friend generator this would cause both sides to become self-sustaining
25
- # TODO: but this may have to be smarter, because there may be more indirect ways of creating such a situation
26
- if overlay.generated?(wme)
27
- token.generated_wmes << existing
28
- overlay.assert(existing, generator: origin)
29
- end
30
- else
24
+
25
+ if should_assert?(wme, token)
26
+ origin = GeneratorOrigin.new(token, self)
31
27
  token.generated_wmes << wme
32
28
  overlay.assert(wme, generator: origin)
33
29
  end
34
30
  end
35
31
 
32
+ private def should_assert?(wme, token)
33
+ considered_tokens = Set.new
34
+ tokens_to_consider = [token]
35
+ until tokens_to_consider.empty?
36
+ token = tokens_to_consider.shift
37
+ considered_tokens.add(token)
38
+
39
+ # self-affirming reasoning
40
+ return false if token.wme == wme
41
+
42
+ # asserting this WME would invalidate the match
43
+ return false if token.node.is_a?(NegNode) && token.node.matches?(token, wme)
44
+
45
+ if token.parent && !considered_tokens.include?(token.parent)
46
+ tokens_to_consider.push(token.parent)
47
+ end
48
+
49
+ if token.wme
50
+ overlay.generators(token.wme).each do |generator|
51
+ tokens_to_consider.push(generator.token) unless considered_tokens.include?(generator.token)
52
+ end
53
+ end
54
+ end
55
+
56
+ # we could not prove that the new WME should not be asserted
57
+ true
58
+ end
59
+
36
60
  def deexecute(token)
37
61
  origin = GeneratorOrigin.new(token, self)
38
62
 
@@ -5,7 +5,6 @@ module Wongi::Engine
5
5
  attr_predicate :debug
6
6
 
7
7
  def initialize(s, p, o, options = {})
8
- @unsafe = options[:unsafe] || false
9
8
  debug! if options[:debug]
10
9
  super(s, p, o)
11
10
  end
@@ -43,13 +42,11 @@ module Wongi::Engine
43
42
  end
44
43
 
45
44
  class Neg < Has
46
- attr_reader :unsafe
47
-
48
45
  def compile(context)
49
46
  tests, assignment = parse_variables(context)
50
47
  raise DefinitionError, "Negative matches may not introduce new variables: #{assignment.variables}" unless assignment.root?
51
48
 
52
- context.tap { |c| c.neg_node(self, tests, unsafe) }
49
+ context.tap { |c| c.neg_node(self, tests) }
53
50
  end
54
51
 
55
52
  def inspect
@@ -1,5 +1,5 @@
1
1
  module Wongi
2
2
  module Engine
3
- VERSION = "0.4.0-alpha2".freeze
3
+ VERSION = "0.4.0-alpha3".freeze
4
4
  end
5
5
  end
@@ -23,7 +23,7 @@ describe 'action classes' do
23
23
  end
24
24
  end
25
25
 
26
- it 'should have appropriate callbacks executed' do
26
+ it 'has appropriate callbacks executed' do
27
27
  executed = 0
28
28
  deexecuted = 0
29
29
 
@@ -9,7 +9,7 @@ describe 'aggregate' do
9
9
  let(:production) { engine.productions[rule_name] }
10
10
 
11
11
  context 'generic clause' do
12
- it 'should return a single token' do
12
+ it 'returns a single token' do
13
13
  engine << rule(rule_name) do
14
14
  forall {
15
15
  aggregate :_, :weight, :_, on: :object, function: :min, assign: :X
@@ -6,7 +6,7 @@ describe "ANY rule" do
6
6
  let(:engine) { Wongi::Engine.create }
7
7
 
8
8
  context "with just one option" do
9
- it "should act like a positive matcher" do
9
+ it "acts like a positive matcher" do
10
10
  engine << rule('one-option') {
11
11
  forall {
12
12
  any {
@@ -43,17 +43,17 @@ describe "ANY rule" do
43
43
  end
44
44
  end
45
45
 
46
- it 'should fire on the first path' do
46
+ it 'fires on the first path' do
47
47
  engine << [:x, :path1, true]
48
48
  expect(production.tokens).to have(1).item
49
49
  end
50
50
 
51
- it 'should fire on the second path' do
51
+ it 'fires on the second path' do
52
52
  engine << [:x, :path2, true]
53
53
  expect(production.tokens).to have(1).item
54
54
  end
55
55
 
56
- it 'should fire twice on both paths at once and be retractable' do
56
+ it 'fires twice on both paths at once and be retractable' do
57
57
  engine << [:x, :path1, true]
58
58
  engine << [:x, :path2, true]
59
59
  expect(production.tokens).to have(2).items
@@ -10,7 +10,7 @@ describe "ASSERT test" do
10
10
  @production = (engine << rule('test-rule', &block))
11
11
  end
12
12
 
13
- it "should pass with a constant 'true'" do
13
+ it "passes with a constant 'true'" do
14
14
  test_rule {
15
15
  forall {
16
16
  assert { |_token|
@@ -22,7 +22,7 @@ describe "ASSERT test" do
22
22
  expect(production).to have(1).token
23
23
  end
24
24
 
25
- it "should fail with a constant 'false'" do
25
+ it "fails with a constant 'false'" do
26
26
  test_rule {
27
27
  forall {
28
28
  assert { |_token|
@@ -34,7 +34,7 @@ describe "ASSERT test" do
34
34
  expect(production).to have(0).tokens
35
35
  end
36
36
 
37
- it "should use the token with no arguments" do
37
+ it "uses the token with no arguments" do
38
38
  test_rule {
39
39
  forall {
40
40
  has :X, "is", :Y
@@ -50,7 +50,7 @@ describe "ASSERT test" do
50
50
  expect(production.tokens.first[:X]).to eq("resistance")
51
51
  end
52
52
 
53
- it "should be retractable" do
53
+ it "is retractable" do
54
54
  test_rule {
55
55
  forall {
56
56
  has :X, "is", :Y
@@ -65,7 +65,7 @@ describe "ASSERT test" do
65
65
  expect(production).to have(0).tokens
66
66
  end
67
67
 
68
- it "should use individual variables with arguments" do
68
+ it "uses individual variables with arguments" do
69
69
  test_rule {
70
70
  forall {
71
71
  has :X, "is", :Y
@@ -4,7 +4,7 @@ describe "ASSIGN rule" do
4
4
  include Wongi::Engine::DSL
5
5
  let(:engine) { Wongi::Engine.create }
6
6
 
7
- it "should assign simple expressions" do
7
+ it "assigns simple expressions" do
8
8
  production = engine << rule {
9
9
  forall {
10
10
  assign :X do
@@ -16,7 +16,7 @@ describe "ASSIGN rule" do
16
16
  expect(production.tokens.first[:X]).to eq(42)
17
17
  end
18
18
 
19
- it 'should be available in the make section' do
19
+ it 'is available in the make section' do
20
20
  production = engine << rule {
21
21
  forall {
22
22
  has 1, 2, :X
@@ -32,7 +32,7 @@ describe "ASSIGN rule" do
32
32
  expect(production.tokens.first[:Y]).to be == 42
33
33
  end
34
34
 
35
- it "should be able to access previous assignments" do
35
+ it "is able to access previous assignments" do
36
36
  production = engine << rule {
37
37
  forall {
38
38
  has 1, 2, :X
@@ -46,7 +46,7 @@ describe "ASSIGN rule" do
46
46
  expect(production.tokens.first[:Y]).to eq(10)
47
47
  end
48
48
 
49
- it 'should be deactivatable' do
49
+ it 'is deactivatable' do
50
50
  prod = engine << rule {
51
51
  forall {
52
52
  has 1, 2, :X
@@ -62,7 +62,7 @@ describe "ASSIGN rule" do
62
62
  expect(prod).to have(0).tokens
63
63
  end
64
64
 
65
- it 'should be evaluated once' do
65
+ it 'is evaluated once' do
66
66
  x = 0
67
67
  engine << rule {
68
68
  forall {
@@ -80,7 +80,7 @@ describe "ASSIGN rule" do
80
80
  expect(x).to be == 1
81
81
  end
82
82
 
83
- it 'should handle booleans' do
83
+ it 'handles booleans' do
84
84
  engine << rule do
85
85
  forall {
86
86
  has :a, :b, :c
@@ -4,7 +4,7 @@ describe Wongi::Engine::AssumingClause do
4
4
  include Wongi::Engine::DSL
5
5
  let(:engine) { Wongi::Engine.create }
6
6
 
7
- it 'should include base rules' do
7
+ it 'includes base rules' do
8
8
  engine << rule(:base) {
9
9
  forall {
10
10
  has :x, :y, :Z
@@ -26,7 +26,7 @@ describe Wongi::Engine::AssumingClause do
26
26
  expect(result).to eq(1 => :a, 2 => :b)
27
27
  end
28
28
 
29
- it 'should check for base rule\'s existence' do
29
+ it 'checks for base rule''s existence' do
30
30
  f = lambda {
31
31
  engine << rule {
32
32
  forall {
@@ -38,7 +38,7 @@ describe Wongi::Engine::AssumingClause do
38
38
  expect(&f).to raise_error Wongi::Engine::UndefinedBaseRule
39
39
  end
40
40
 
41
- it 'should come first in a rule' do
41
+ it 'comes first in a rule' do
42
42
  f = lambda {
43
43
  engine << rule(:base) {
44
44
  forall {
@@ -6,7 +6,7 @@ describe Wongi::Engine::BetaNode do
6
6
  let(:engine) { Wongi::Engine.create }
7
7
 
8
8
  describe '#tokens' do
9
- it 'should be enumerable' do
9
+ it 'is enumerable' do
10
10
  production = engine << rule {
11
11
  forall {
12
12
  has :x, :y, :Z
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe "issue 4" do
4
- it "should correctly retract pre-added items from within a rule" do
4
+ it "correctlies retract pre-added items from within a rule" do
5
5
  engine = Wongi::Engine.create
6
6
 
7
7
  10.times { |i| engine << [i, :is_number, true] }
@@ -26,7 +26,7 @@ describe "issue 4" do
26
26
  expect(evens.size).to eq(10)
27
27
  end
28
28
 
29
- it "should correctly retract post-added items from within a rule" do
29
+ it "correctlies retract post-added items from within a rule" do
30
30
  engine = Wongi::Engine.create
31
31
 
32
32
  engine.rule 'segregate' do
@@ -52,7 +52,7 @@ describe "issue 4" do
52
52
  end
53
53
 
54
54
  # cascaded processing affects this
55
- it "should not retract later items from within a rule" do
55
+ it "does not retract later items from within a rule" do
56
56
  engine = Wongi::Engine.create
57
57
 
58
58
  10.times { |i| engine << [i, :is_number, true] }
@@ -85,7 +85,7 @@ describe "issue 4" do
85
85
  expect(odds.size).to eq(5)
86
86
  end
87
87
 
88
- it "should not lose track when another rule affects a set" do
88
+ it "does not lose track when another rule affects a set" do
89
89
  engine = Wongi::Engine.create
90
90
 
91
91
  10.times { |i| engine << [i, :is_number, true] }
data/spec/dataset_spec.rb CHANGED
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Wongi::Engine::Network do
4
4
  include Wongi::Engine::DSL
5
- it 'should expose compiled productions' do
5
+ it 'exposes compiled productions' do
6
6
  ds = Wongi::Engine::Network.new
7
7
 
8
8
  ds << rule('test-rule') {
@@ -20,7 +20,7 @@ describe Wongi::Engine::DSL::Action::StatementGenerator do
20
20
  let(:production) { engine << transitive_rule }
21
21
 
22
22
  shared_examples 'generation' do
23
- it 'should generate facts' do
23
+ it 'generates facts' do
24
24
  engine << %w[Alice relative Bob]
25
25
  engine << %w[Bob relative Dwight]
26
26
 
@@ -28,7 +28,7 @@ describe Wongi::Engine::DSL::Action::StatementGenerator do
28
28
  expect(engine.find(*%w[Alice relative Dwight])).not_to be_nil
29
29
  end
30
30
 
31
- it 'should retrct generated facts' do
31
+ it 'retracts generated facts' do
32
32
  engine << %w[Alice relative Bob]
33
33
  engine << %w[Bob relative Dwight]
34
34
  engine.retract %w[Bob relative Dwight]
@@ -45,19 +45,19 @@ describe Wongi::Engine::DSL::Action::StatementGenerator do
45
45
  engine << %w[Claire relative Dwight]
46
46
  end
47
47
 
48
- it 'should be created' do
48
+ it 'is created' do
49
49
  expect(production).to have(2).tokens
50
50
  expect(engine.find(*%w[Alice relative Dwight])).not_to be_nil
51
51
  end
52
52
 
53
- it 'should remain after a single retraction' do
53
+ it 'remains after a single retraction' do
54
54
  engine.retract %w[Claire relative Dwight]
55
55
 
56
56
  expect(production).to have(1).token
57
57
  expect(engine.find(*%w[Alice relative Dwight])).not_to be_nil
58
58
  end
59
59
 
60
- it 'should be destroyed after both retractions' do
60
+ it 'is destroyed after both retractions' do
61
61
  engine.retract %w[Claire relative Dwight]
62
62
  engine.retract %w[Alice relative Bob]
63
63
 
@@ -83,7 +83,7 @@ describe Wongi::Engine::DSL::Action::StatementGenerator do
83
83
 
84
84
  it_behaves_like 'generation'
85
85
 
86
- it 'should not retract generated facts marked as manual', :wip do
86
+ it 'does not retract generated facts marked as manual', :wip do
87
87
  engine << %w[Alice relative Bob]
88
88
  engine << %w[Bob relative Dwight]
89
89
  engine << %w[Alice relative Dwight]
@@ -93,7 +93,7 @@ describe Wongi::Engine::DSL::Action::StatementGenerator do
93
93
  expect(engine.find(*%w[Alice relative Dwight])).not_to be_nil
94
94
  end
95
95
 
96
- it 'should retract generated facts unmarked as manual', :wip do
96
+ it 'retracts generated facts unmarked as manual', :wip do
97
97
  engine << %w[Alice relative Bob]
98
98
  engine << %w[Bob relative Dwight]
99
99
  engine << %w[Alice relative Dwight]
@@ -12,7 +12,7 @@ describe 'Greater Than Or Equal test' do
12
12
  @production = (engine << rule('test-rule', &block))
13
13
  end
14
14
 
15
- it 'should interact with optional node correctly' do
15
+ it 'interacts with optional node correctly' do
16
16
  # before the fix, filters would try to piggy-back on optional templates
17
17
 
18
18
  test_rule do
@@ -15,7 +15,7 @@ describe 'the engine' do
15
15
  let(:engine) { Wongi::Engine.create }
16
16
 
17
17
  context 'with a simple generative positive rule' do
18
- it 'should generate wmes with an existing rule' do
18
+ it 'generates wmes with an existing rule' do
19
19
  engine << rule('symmetric') {
20
20
  forall {
21
21
  has :P, "symmetric", true
@@ -35,7 +35,7 @@ describe 'the engine' do
35
35
  expect(generated).to eq([Wongi::Engine::WME.new("Bob", "friend", "Alice")])
36
36
  end
37
37
 
38
- it 'should generate wmes with an added rule' do
38
+ it 'generates wmes with an added rule' do
39
39
  engine << Wongi::Engine::WME.new("friend", "symmetric", true)
40
40
  engine << Wongi::Engine::WME.new("Alice", "friend", "Bob")
41
41
 
@@ -55,7 +55,7 @@ describe 'the engine' do
55
55
  expect(engine.facts.select { engine.current_overlay.manual?(_1) }.size).to eq(2)
56
56
  end
57
57
 
58
- it 'should not get confused by recursive activations' do
58
+ it 'does not get confused by recursive activations' do
59
59
  engine << rule('reflexive') {
60
60
  forall {
61
61
  has :Predicate, "reflexive", true
@@ -76,7 +76,7 @@ describe 'the engine' do
76
76
  end
77
77
  end
78
78
 
79
- it 'should check equality' do
79
+ it 'checks equality' do
80
80
  node = engine << rule('equality') {
81
81
  forall {
82
82
  fact :A, "same", :B
@@ -88,7 +88,7 @@ describe 'the engine' do
88
88
  expect(node.size).to eq(1)
89
89
  end
90
90
 
91
- it 'should compare things' do
91
+ it 'compares things' do
92
92
  engine << rule('less') {
93
93
  forall {
94
94
  has :A, :age, :N1
@@ -121,7 +121,7 @@ describe 'the engine' do
121
121
  expect(items.size).to eq(1)
122
122
  end
123
123
 
124
- it 'should use collectors' do
124
+ it 'uses collectors' do
125
125
  engine << rule('collector') {
126
126
  forall {
127
127
  has :X, :_, 42
@@ -139,7 +139,7 @@ describe 'the engine' do
139
139
  expect(collection.first).to eq("answer")
140
140
  end
141
141
 
142
- it "should properly show error messages" do
142
+ it "properlies show error messages" do
143
143
  engine << rule("Error rule") {
144
144
  forall {
145
145
  has :_, :_, :TestNumber
@@ -156,7 +156,7 @@ describe 'the engine' do
156
156
  expect(error_messages).to eq(["An error has occurred"])
157
157
  end
158
158
 
159
- it 'should use generic collectors' do
159
+ it 'uses generic collectors' do
160
160
  engine << rule('generic-collector') {
161
161
  forall {
162
162
  has :X, :_, 42
@@ -174,7 +174,7 @@ describe 'the engine' do
174
174
  expect(collection.first).to eq("answer")
175
175
  end
176
176
 
177
- it 'should accept several rules' do
177
+ it 'accepts several rules' do
178
178
  expect {
179
179
  engine << rule('generic-collector') {
180
180
  forall {
@@ -196,7 +196,7 @@ describe 'the engine' do
196
196
  }.not_to raise_error
197
197
  end
198
198
 
199
- it 'should process negative nodes' do
199
+ it 'processes negative nodes' do
200
200
  production = (engine << rule('negative') {
201
201
  forall {
202
202
  neg :_, :_, 42
@@ -220,14 +220,14 @@ describe 'the engine' do
220
220
  }
221
221
  end
222
222
 
223
- it 'should run' do
223
+ it 'runs' do
224
224
  engine << ["answer", "is", 42]
225
225
  engine.execute "test-query", { X: "answer" }
226
226
  expect(engine.results["test-query"].size).to eq(1)
227
227
  expect(engine.results["test-query"].tokens.first[:Y]).to eq(42)
228
228
  end
229
229
 
230
- it 'should run several times' do
230
+ it 'runs several times' do
231
231
  engine << ["answer", "is", 42]
232
232
  engine << %w[question is 6x9]
233
233
  engine.execute "test-query", { X: "answer" }
@@ -10,7 +10,7 @@ describe "LESS test" do
10
10
  @production = (engine << rule('test-rule', &block))
11
11
  end
12
12
 
13
- it "should interact with optional node correctly" do
13
+ it "interacts with optional node correctly" do
14
14
  # before the fix, filters would try to piggy-back on optional templates
15
15
 
16
16
  test_rule {
@@ -12,7 +12,7 @@ describe 'Less Than Or Equal test' do
12
12
  @production = (engine << rule('test-rule', &block))
13
13
  end
14
14
 
15
- it 'should interact with optional node correctly' do
15
+ it 'interacts with optional node correctly' do
16
16
  # before the fix, filters would try to piggy-back on optional templates
17
17
 
18
18
  test_rule do
@@ -12,7 +12,7 @@ describe "MAYBE rule" do
12
12
  }
13
13
  }
14
14
 
15
- it "should pass with existing facts" do
15
+ it "passes with existing facts" do
16
16
  production = engine << maybe_rule
17
17
 
18
18
  engine << [1, 2, 3]
@@ -24,7 +24,7 @@ describe "MAYBE rule" do
24
24
  expect(production.tokens.first[:Y]).to eq(5)
25
25
  end
26
26
 
27
- it "should pass with missing facts" do
27
+ it "passes with missing facts" do
28
28
  production = engine << maybe_rule
29
29
 
30
30
  engine << [1, 2, 3]
@@ -35,7 +35,7 @@ describe "MAYBE rule" do
35
35
  expect(production.tokens.first[:Y]).to be_nil
36
36
  end
37
37
 
38
- it "should pass with pre-added missing facts" do
38
+ it "passes with pre-added missing facts" do
39
39
  engine << [1, 2, 3]
40
40
 
41
41
  production = engine << maybe_rule
@@ -46,7 +46,7 @@ describe "MAYBE rule" do
46
46
  expect(production.tokens.first[:Y]).to be_nil
47
47
  end
48
48
 
49
- it 'should pass with retracted facts' do
49
+ it 'passes with retracted facts' do
50
50
  prod = engine << maybe_rule
51
51
 
52
52
  engine << [1, 2, 3]
@@ -59,7 +59,7 @@ describe "MAYBE rule" do
59
59
  expect(prod.tokens.first[:Y]).to be_nil
60
60
  end
61
61
 
62
- it 'should work with repeated activations' do
62
+ it 'works with repeated activations' do
63
63
  prod = engine << maybe_rule
64
64
 
65
65
  engine << [1, 2, 3]
@@ -77,7 +77,7 @@ describe "MAYBE rule" do
77
77
  }
78
78
  end
79
79
 
80
- it 'should work with with overlays' do
80
+ it 'works with with overlays' do
81
81
  prod = engine << maybe_rule
82
82
 
83
83
  engine << [1, 2, 3]
@@ -96,7 +96,7 @@ describe "MAYBE rule" do
96
96
  expect(prod.tokens.first[:Y]).to be == 5
97
97
  end
98
98
 
99
- it 'should handle retracted parent tokens' do
99
+ it 'handles retracted parent tokens' do
100
100
  prod = engine << maybe_rule
101
101
 
102
102
  engine << [1, 2, 3]
@@ -29,7 +29,7 @@ describe Wongi::Engine::NccNode do
29
29
  }
30
30
  end
31
31
 
32
- it 'should pass with a mismatching subchain' do
32
+ it 'passes with a mismatching subchain' do
33
33
  engine << ncc_rule
34
34
  production = engine.productions['ncc']
35
35
 
@@ -46,7 +46,7 @@ describe Wongi::Engine::NccNode do
46
46
  expect(production).to have(0).token
47
47
  end
48
48
 
49
- it 'should remain consistent after retraction' do
49
+ it 'remains consistent after retraction' do
50
50
  engine << ncc_rule
51
51
  production = engine.productions['ncc']
52
52
 
@@ -81,7 +81,7 @@ describe Wongi::Engine::NccNode do
81
81
  expect(production).to have(0).tokens
82
82
  end
83
83
 
84
- it 'should clean up correctly' do
84
+ it 'cleans up correctly' do
85
85
  engine.rule :rule1 do
86
86
  forall {
87
87
  has :light_kitchen, :value, :on
@@ -135,7 +135,7 @@ describe Wongi::Engine::NccNode do
135
135
  expect(engine.select(:light_bathroom, :last_user, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :god)]
136
136
  end
137
137
 
138
- it 'should clean up correctly with a different activation order' do
138
+ it 'cleans up correctly with a different activation order' do
139
139
  engine.rule :rule1 do
140
140
  forall {
141
141
  has :light_kitchen, :value, :on
@@ -191,7 +191,7 @@ describe Wongi::Engine::NccNode do
191
191
  expect(engine.select(:light_bathroom, :last_user, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :god)]
192
192
  end
193
193
 
194
- it 'should ncc-deactivate without destroying tokens' do
194
+ it 'ncc-deactivates without destroying tokens' do
195
195
  engine << rule {
196
196
  forall {
197
197
  has :Student, :is_a, :student
@@ -254,6 +254,29 @@ describe Wongi::Engine::NccNode do
254
254
  expect(prod).to have(1).tokens
255
255
  end
256
256
 
257
+ # TODO: this is probably pretty hard to fix, but it's good to fixate the behaviour
258
+ it "causes infinite loops" do
259
+ counter = 0
260
+ exception = StandardError.new
261
+
262
+ looping_rule = rule {
263
+ forall {
264
+ none {
265
+ has :a, :b, :c
266
+ }
267
+ }
268
+ make {
269
+ action {
270
+ counter += 1
271
+ raise exception if counter > 1
272
+ }
273
+ gen :a, :b, :c
274
+ }
275
+ }
276
+
277
+ expect { engine << looping_rule }.to raise_exception(exception)
278
+ end
279
+
257
280
  context "with overlays" do
258
281
  context 'should pass with a mismatching subchain' do
259
282
  specify "variation 1" do
@@ -20,7 +20,7 @@ describe "negative rule" do
20
20
  expect(prod).to have(0).tokens
21
21
  end
22
22
 
23
- it "should not introduce variables" do
23
+ it "does not introduce variables" do
24
24
  proc = lambda {
25
25
  engine << rule('one-option') {
26
26
  forall {
@@ -68,7 +68,7 @@ describe "negative rule" do
68
68
  expect(prod).to have(1).tokens
69
69
  end
70
70
 
71
- it "should not create infinite feedback loops by default" do
71
+ it "does not create self-negating facts" do
72
72
  engine << rule('feedback') {
73
73
  forall {
74
74
  neg :a, :b, :_
@@ -78,28 +78,6 @@ describe "negative rule" do
78
78
  }
79
79
  }
80
80
 
81
- engine.should have(1).facts
82
- end
83
-
84
- it "should create infinite feedback loops with unsafe option" do
85
- counter = 0
86
- exception = Class.new(StandardError)
87
-
88
- proc = lambda {
89
- engine << rule('feedback') {
90
- forall {
91
- neg :a, :b, :_, unsafe: true
92
- }
93
- make {
94
- action {
95
- counter += 1
96
- raise exception if counter > 5
97
- }
98
- gen :a, :b, :c
99
- }
100
- }
101
- }
102
-
103
- expect(&proc).to raise_error(exception)
81
+ expect(engine).to have(0).facts
104
82
  end
105
83
  end
data/spec/network_spec.rb CHANGED
@@ -3,15 +3,16 @@ require 'spec_helper'
3
3
  describe Wongi::Engine::Network do
4
4
  include Wongi::Engine::DSL
5
5
 
6
- let(:engine) { Wongi::Engine.create }
7
6
  subject { engine }
8
7
 
9
- it 'should assert facts' do
8
+ let(:engine) { Wongi::Engine.create }
9
+
10
+ it 'asserts facts' do
10
11
  subject << [1, 2, 3]
11
12
  expect(subject.select(:_, 2, :_)).to have(1).item
12
13
  end
13
14
 
14
- it 'should retract facts' do
15
+ it 'retracts facts' do
15
16
  subject << [1, 2, 3]
16
17
  subject.retract [1, 2, 3]
17
18
  expect(subject.select(:_, 2, :_).count).to eq(0)
data/spec/overlay_spec.rb CHANGED
@@ -102,6 +102,7 @@ describe Wongi::Engine::Overlay do
102
102
  allow(engine).to receive(:real_assert)
103
103
  allow(engine).to receive(:real_retract)
104
104
  end
105
+
105
106
  it "maintains visibility on each layer" do
106
107
  child1 = overlay.new_child
107
108
  child2 = child1.new_child
@@ -145,7 +146,7 @@ describe Wongi::Engine::Overlay do
145
146
  end
146
147
  end
147
148
 
148
- it 'should be disposable' do
149
+ it 'is disposable' do
149
150
  production = engine << rule {
150
151
  forall {
151
152
  has 1, 2, :X
@@ -177,7 +178,7 @@ describe Wongi::Engine::Overlay do
177
178
  expect(production).to have(1).token
178
179
  end
179
180
 
180
- it 'should generate into correct overlays' do
181
+ it 'generates into correct overlays' do
181
182
  production = engine << rule {
182
183
  forall {
183
184
  has 1, 2, :X
data/spec/ruleset_spec.rb CHANGED
@@ -6,37 +6,37 @@ describe Wongi::Engine::Ruleset do
6
6
  end
7
7
 
8
8
  context 'initially' do
9
- it 'should have no rules' do
9
+ it 'has no rules' do
10
10
  expect(Wongi::Engine::Ruleset.rulesets).to be_empty
11
11
  end
12
12
  end
13
13
 
14
14
  context 'when creating' do
15
- it 'should not register itself when not given a name' do
15
+ it 'does not register itself when not given a name' do
16
16
  ruleset = Wongi::Engine::Ruleset.new
17
17
  expect(ruleset.name).to be_nil
18
18
  expect(Wongi::Engine::Ruleset.rulesets).to be_empty
19
19
  end
20
20
 
21
- it 'should have a name' do
21
+ it 'has a name' do
22
22
  ruleset = Wongi::Engine::Ruleset.new 'testing-ruleset'
23
23
  expect(ruleset.name).to be == 'testing-ruleset'
24
24
  end
25
25
 
26
- it 'should register itself when given a name' do
26
+ it 'registers itself when given a name' do
27
27
  ruleset = Wongi::Engine::Ruleset.new 'testing-ruleset'
28
28
  expect(Wongi::Engine::Ruleset.rulesets).not_to be_empty
29
29
  expect(Wongi::Engine::Ruleset[ruleset.name]).to be == ruleset
30
30
  end
31
31
  end
32
32
 
33
- it 'should be able to clear registered rulesets' do
33
+ it 'is able to clear registered rulesets' do
34
34
  _ = Wongi::Engine::Ruleset.new 'testing-ruleset'
35
35
  Wongi::Engine::Ruleset.reset
36
36
  expect(Wongi::Engine::Ruleset.rulesets).to be_empty
37
37
  end
38
38
 
39
- it 'should install creating rules into a rete' do
39
+ it 'installs creating rules into a rete' do
40
40
  rete = double 'rete'
41
41
 
42
42
  ruleset = Wongi::Engine::Ruleset.new
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Wongi::Engine::DSL::Action::SimpleAction do
4
4
  let(:engine) { Wongi::Engine.create }
5
5
 
6
- it 'should work with blocks' do
6
+ it 'works with blocks' do
7
7
  count = 0
8
8
 
9
9
  engine.rule do
data/spec/wme_spec.rb CHANGED
@@ -14,14 +14,14 @@ describe Wongi::Engine::WME do
14
14
  }
15
15
 
16
16
  context 'a new WME' do
17
- it 'should initialize and expose members' do
17
+ it 'initializes and expose members' do
18
18
  expect(subject.subject).to be == "a"
19
19
  expect(subject.predicate).to be == "b"
20
20
  expect(subject.object).to be == "c"
21
21
  end
22
22
  end
23
23
 
24
- it 'should compare instances' do
24
+ it 'compares instances' do
25
25
  wme1 = Wongi::Engine::WME.new "a", "b", "c"
26
26
  wme2 = Wongi::Engine::WME.new "a", "b", "c"
27
27
  wme3 = Wongi::Engine::WME.new "a", "b", "d"
@@ -30,11 +30,11 @@ describe Wongi::Engine::WME do
30
30
  expect(wme1).not_to be == wme3
31
31
  end
32
32
 
33
- it 'should not match against non-templates' do
33
+ it 'does not match against non-templates' do
34
34
  expect { subject =~ [1, 2, 3] }.to raise_error(Wongi::Engine::Error)
35
35
  end
36
36
 
37
- it 'should match against templates' do
37
+ it 'matches against templates' do
38
38
  t1 = Wongi::Engine::Template.new "a", :_, :_
39
39
  t2 = Wongi::Engine::Template.new "b", :_, :_
40
40
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wongi-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0.pre.alpha2
4
+ version: 0.4.0.pre.alpha3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Valeri Sokolov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-17 00:00:00.000000000 Z
11
+ date: 2022-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -152,25 +152,25 @@ files:
152
152
  - lib/wongi-engine/wme.rb
153
153
  - lib/wongi-engine/wme_match_data.rb
154
154
  - spec/action_class_spec.rb
155
+ - spec/aggregate_spec.rb
155
156
  - spec/alpha_index_spec.rb
157
+ - spec/any_rule_spec.rb
158
+ - spec/assert_test_spec.rb
159
+ - spec/assign_spec.rb
160
+ - spec/assuming_spec.rb
156
161
  - spec/beta_node_spec.rb
157
162
  - spec/bug_specs/issue_4_spec.rb
158
163
  - spec/dataset_spec.rb
159
- - spec/filter_specs/assert_test_spec.rb
160
- - spec/filter_specs/greater_than_equality_test_spec.rb
161
- - spec/filter_specs/less_test_spec.rb
162
- - spec/filter_specs/less_than_equality_test_spec.rb
163
164
  - spec/generation_spec.rb
165
+ - spec/greater_than_equality_test_spec.rb
164
166
  - spec/high_level_spec.rb
167
+ - spec/less_test_spec.rb
168
+ - spec/less_than_equality_test_spec.rb
169
+ - spec/maybe_rule_spec.rb
170
+ - spec/ncc_spec.rb
171
+ - spec/negative_rule_spec.rb
165
172
  - spec/network_spec.rb
166
173
  - spec/overlay_spec.rb
167
- - spec/rule_specs/aggregate_spec.rb
168
- - spec/rule_specs/any_rule_spec.rb
169
- - spec/rule_specs/assign_spec.rb
170
- - spec/rule_specs/assuming_spec.rb
171
- - spec/rule_specs/maybe_rule_spec.rb
172
- - spec/rule_specs/ncc_spec.rb
173
- - spec/rule_specs/negative_rule_spec.rb
174
174
  - spec/ruleset_spec.rb
175
175
  - spec/simple_action_spec.rb
176
176
  - spec/spec_helper.rb
@@ -196,7 +196,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
196
196
  - !ruby/object:Gem::Version
197
197
  version: 1.3.1
198
198
  requirements: []
199
- rubygems_version: 3.1.6
199
+ rubygems_version: 3.3.7
200
200
  signing_key:
201
201
  specification_version: 4
202
202
  summary: A forward-chaining rule engine in pure Ruby.