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

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -3
  3. data/README.md +1 -1
  4. data/lib/wongi-engine/beta/aggregate_node.rb +2 -1
  5. data/lib/wongi-engine/beta/beta_node.rb +9 -0
  6. data/lib/wongi-engine/beta/join_node.rb +3 -2
  7. data/lib/wongi-engine/beta/ncc_partner.rb +1 -1
  8. data/lib/wongi-engine/beta/neg_node.rb +5 -6
  9. data/lib/wongi-engine/beta/optional_node.rb +3 -1
  10. data/lib/wongi-engine/compiler.rb +2 -2
  11. data/lib/wongi-engine/dsl/action/statement_generator.rb +34 -10
  12. data/lib/wongi-engine/dsl/clause/fact.rb +1 -4
  13. data/lib/wongi-engine/version.rb +1 -1
  14. data/spec/action_class_spec.rb +1 -1
  15. data/spec/{rule_specs/aggregate_spec.rb → aggregate_spec.rb} +1 -1
  16. data/spec/{rule_specs/any_rule_spec.rb → any_rule_spec.rb} +4 -4
  17. data/spec/{filter_specs/assert_test_spec.rb → assert_test_spec.rb} +5 -5
  18. data/spec/{rule_specs/assign_spec.rb → assign_spec.rb} +6 -6
  19. data/spec/{rule_specs/assuming_spec.rb → assuming_spec.rb} +3 -3
  20. data/spec/beta_node_spec.rb +1 -1
  21. data/spec/bug_specs/issue_4_spec.rb +4 -4
  22. data/spec/dataset_spec.rb +1 -1
  23. data/spec/generation_spec.rb +7 -7
  24. data/spec/{filter_specs/greater_than_equality_test_spec.rb → greater_than_equality_test_spec.rb} +1 -1
  25. data/spec/high_level_spec.rb +12 -12
  26. data/spec/{filter_specs/less_test_spec.rb → less_test_spec.rb} +1 -1
  27. data/spec/{filter_specs/less_than_equality_test_spec.rb → less_than_equality_test_spec.rb} +1 -1
  28. data/spec/{rule_specs/maybe_rule_spec.rb → maybe_rule_spec.rb} +7 -7
  29. data/spec/{rule_specs/ncc_spec.rb → ncc_spec.rb} +28 -5
  30. data/spec/{rule_specs/negative_rule_spec.rb → negative_rule_spec.rb} +3 -25
  31. data/spec/network_spec.rb +4 -3
  32. data/spec/overlay_spec.rb +3 -2
  33. data/spec/ruleset_spec.rb +6 -6
  34. data/spec/simple_action_spec.rb +1 -1
  35. data/spec/wme_spec.rb +4 -4
  36. metadata +14 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf15effd33cba1faca7c6b2a6ce8b8a4730981a9b778fb5d41e11b4dce7803f8
4
- data.tar.gz: 8d3cc0c99061e7097747b4339c55a83c11e79bb0edfbba4a0c95e4c850301595
3
+ metadata.gz: d3401b29a9b186b8594549d45feddda88e885dae9a03d42f7006ad7e6fecbd0b
4
+ data.tar.gz: c315eb40f0c2a05d6a5995016fd700b6ecdb7795588fa929b81b68d86722c481
5
5
  SHA512:
6
- metadata.gz: 7140c583e23389df8f5c959b7ed31220782d72afcbd10f0883ba7314f24534656f1ca3fed33ed9d49a7962da8cbe7535f48bf160be88eef86d3798efe7ccece5
7
- data.tar.gz: 3f0e9957b8497666a25c3ac933bcfe93495ed2c4cdac2f4eb27468c95401a107139f6a23840816241f94c0811edf69ab373818dd33def938d3ae9450505c68b5
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
 
data/README.md CHANGED
@@ -11,7 +11,7 @@ Ruby >= 2.7 and JRuby are supported. Rubinius should work but isn't actively sup
11
11
 
12
12
  There is no API documentation, as most of the library's interfaces are for internal use only and would not be safe to use directly.
13
13
 
14
- Instead, follow the [tutorial](http://ulfurinn.github.io/wongi-engine/) and stick to the constructs described in it.
14
+ Instead, follow the [tutorial](https://ulfurinn.github.io/wongi-engine/) and stick to the constructs described in it.
15
15
 
16
16
  ## Upgrading
17
17
 
@@ -62,7 +62,8 @@ module Wongi::Engine
62
62
  # # TODO: optimise: only clean up if the value changed
63
63
  beta_deactivate_children(token: token)
64
64
 
65
- candidates = select_wmes(alpha.template) { |asserted_wme| matches?(token, asserted_wme) }
65
+ template = specialize(alpha.template, tests, token)
66
+ candidates = select_wmes(template) { |asserted_wme| matches?(token, asserted_wme) }
66
67
 
67
68
  return if candidates.empty?
68
69
 
@@ -61,6 +61,15 @@ module Wongi::Engine
61
61
  rete.current_overlay.select(template)
62
62
  end
63
63
 
64
+ private def specialize(template, tests, token)
65
+ tests.each_with_object(template.dup) do |test, template|
66
+ var = test.variable
67
+ if token.has_var?(var)
68
+ template.public_send("#{test.field}=", token[var])
69
+ end
70
+ end
71
+ end
72
+
64
73
  def tokens
65
74
  overlay.node_tokens(self)
66
75
  end
@@ -65,7 +65,8 @@ module Wongi
65
65
 
66
66
  overlay.add_token(token)
67
67
 
68
- select_wmes(alpha.template).each do |wme|
68
+ template = specialize(alpha.template, tests, token)
69
+ select_wmes(template).each do |wme|
69
70
  next unless matches?(token, wme)
70
71
 
71
72
  assignments = collect_assignments(wme)
@@ -93,7 +94,7 @@ module Wongi
93
94
  protected
94
95
 
95
96
  def matches?(token, wme)
96
- @tests.each do |test|
97
+ tests.each do |test|
97
98
  return false unless test.matches?(token, wme)
98
99
  end
99
100
  true
@@ -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))
@@ -44,7 +43,9 @@ module Wongi
44
43
  return if tokens.find { |t| t.duplicate? token }
45
44
 
46
45
  overlay.add_token(token)
47
- select_wmes(alpha.template).each do |wme|
46
+
47
+ template = specialize(alpha.template, tests, token)
48
+ select_wmes(template).each do |wme|
48
49
  overlay.add_neg_join_result(NegJoinResult.new(token, wme)) if matches?(token, wme)
49
50
  end
50
51
  return if overlay.neg_join_results_for(token: token).any?
@@ -69,8 +70,6 @@ module Wongi
69
70
  end
70
71
  end
71
72
 
72
- protected
73
-
74
73
  def matches?(token, wme)
75
74
  puts "matching #{wme} against #{token}" if debug?
76
75
  @tests.each do |test|
@@ -55,8 +55,10 @@ module Wongi
55
55
  return if tokens.find { |t| t.duplicate? token }
56
56
 
57
57
  overlay.add_token(token)
58
+
58
59
  match = false
59
- select_wmes(alpha.template).each do |wme|
60
+ template = specialize(alpha.template, tests, token)
61
+ select_wmes(template).each do |wme|
60
62
  assignments = collect_assignments(wme)
61
63
  next unless matches? token, wme
62
64
 
@@ -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-alpha1".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.alpha1
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-14 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.