wongi-engine 0.3.9 → 0.4.0.pre.alpha2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +2 -2
- data/.gitignore +2 -0
- data/README.md +12 -12
- data/lib/wongi-engine/alpha_index.rb +58 -0
- data/lib/wongi-engine/alpha_memory.rb +2 -24
- data/lib/wongi-engine/beta/aggregate_node.rb +17 -15
- data/lib/wongi-engine/beta/assignment_node.rb +7 -2
- data/lib/wongi-engine/beta/beta_node.rb +36 -23
- data/lib/wongi-engine/beta/filter_node.rb +8 -13
- data/lib/wongi-engine/beta/join_node.rb +17 -29
- data/lib/wongi-engine/beta/ncc_node.rb +14 -26
- data/lib/wongi-engine/beta/ncc_partner.rb +18 -18
- data/lib/wongi-engine/beta/neg_node.rb +25 -55
- data/lib/wongi-engine/beta/optional_node.rb +26 -48
- data/lib/wongi-engine/beta/or_node.rb +24 -1
- data/lib/wongi-engine/beta/production_node.rb +9 -3
- data/lib/wongi-engine/beta/root_node.rb +47 -0
- data/lib/wongi-engine/beta.rb +1 -1
- data/lib/wongi-engine/compiler.rb +6 -34
- data/lib/wongi-engine/dsl/action/{base.rb → base_action.rb} +5 -1
- data/lib/wongi-engine/dsl/action/error_generator.rb +1 -1
- data/lib/wongi-engine/dsl/action/simple_action.rb +1 -1
- data/lib/wongi-engine/dsl/action/simple_collector.rb +1 -1
- data/lib/wongi-engine/dsl/action/statement_generator.rb +21 -22
- data/lib/wongi-engine/dsl/action/trace_action.rb +1 -1
- data/lib/wongi-engine/dsl/clause/fact.rb +2 -6
- data/lib/wongi-engine/dsl.rb +1 -25
- data/lib/wongi-engine/graph.rb +1 -1
- data/lib/wongi-engine/network/debug.rb +2 -10
- data/lib/wongi-engine/network.rb +44 -105
- data/lib/wongi-engine/overlay.rb +589 -0
- data/lib/wongi-engine/template.rb +22 -2
- data/lib/wongi-engine/token.rb +10 -26
- data/lib/wongi-engine/token_assignment.rb +15 -0
- data/lib/wongi-engine/version.rb +1 -1
- data/lib/wongi-engine/wme.rb +10 -39
- data/lib/wongi-engine.rb +3 -1
- data/spec/alpha_index_spec.rb +78 -0
- data/spec/bug_specs/issue_4_spec.rb +11 -11
- data/spec/high_level_spec.rb +8 -101
- data/spec/network_spec.rb +8 -6
- data/spec/overlay_spec.rb +161 -3
- data/spec/rule_specs/any_rule_spec.rb +39 -0
- data/spec/rule_specs/assign_spec.rb +1 -1
- data/spec/rule_specs/maybe_rule_spec.rb +58 -1
- data/spec/rule_specs/ncc_spec.rb +78 -19
- data/spec/rule_specs/negative_rule_spec.rb +12 -14
- data/spec/spec_helper.rb +4 -0
- data/spec/wme_spec.rb +0 -32
- metadata +11 -9
- data/lib/wongi-engine/beta/beta_memory.rb +0 -60
- data/lib/wongi-engine/data_overlay.rb +0 -149
- data/spec/rule_specs/or_rule_spec.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49eb7efc3f4049b38a7035208884d2078e0e31e68d396fb647deb81add7a030c
|
4
|
+
data.tar.gz: 6916568d628dbfce536001472c224f0fcee9ab580d4d9f2df30f04edb77bb45d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 437466d4256f5ff96bf741e879054f2e5a7499f3876ed73cbd00b155caf6b5f06cfb60378941e36cba6dd912b8d61cbe8b4cdfec58a0b60f121841cdd63c3bd9
|
7
|
+
data.tar.gz: 5e8ca5469c6b20cb63928a0c4c128f3391ae74c29e09608b0a7045d6d979bc03d9d8b95f49f4282898531ee8f69e7bd9abcd8ebb8164aa4d612292e01b509be3
|
data/.github/workflows/test.yml
CHANGED
@@ -11,10 +11,10 @@ jobs:
|
|
11
11
|
|
12
12
|
strategy:
|
13
13
|
matrix:
|
14
|
-
ruby-version: ["3.1", "3.0", "2.7", "
|
14
|
+
ruby-version: ["3.1", "3.0", "2.7", "jruby-head"]
|
15
15
|
|
16
16
|
steps:
|
17
|
-
- uses: actions/checkout@
|
17
|
+
- uses: actions/checkout@v3
|
18
18
|
- name: Set up Ruby ${{ matrix.ruby-version }}
|
19
19
|
uses: ruby/setup-ruby@v1
|
20
20
|
with:
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -3,26 +3,26 @@
|
|
3
3
|
[](https://rubygems.org/gems/wongi-engine/)
|
4
4
|
[](https://github.com/ulfurinn/wongi-engine/actions/workflows/test.yml)
|
5
5
|
|
6
|
+
This is a pure-Ruby forward-chaining rule engine based on the classic [Rete algorithm](http://en.wikipedia.org/wiki/Rete_algorithm).
|
7
|
+
|
6
8
|
Ruby >= 2.7 and JRuby are supported. Rubinius should work but isn't actively supported.
|
7
9
|
|
8
|
-
##
|
10
|
+
## Documentation
|
11
|
+
|
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
|
+
|
14
|
+
Instead, follow the [tutorial](https://ulfurinn.github.io/wongi-engine/) and stick to the constructs described in it.
|
9
15
|
|
10
|
-
|
16
|
+
## Upgrading
|
11
17
|
|
12
|
-
|
18
|
+
Until there is a 1.0 release, all minor versions should be treated as potentially breaking.
|
13
19
|
|
14
|
-
|
20
|
+
Always test your rules extensively. There's always a chance of you finding a bug in the engine that is only triggered by a very specific rule configuration.
|
21
|
+
|
22
|
+
[Feature annoucements](https://github.com/ulfurinn/wongi-engine/issues?q=is%3Aissue+label%3Aannoucement)
|
15
23
|
|
16
24
|
[Open discussions](https://github.com/ulfurinn/wongi-engine/issues?q=is%3Aopen+is%3Aissue+label%3Adiscussion)
|
17
25
|
|
18
26
|
## Acknowledgements
|
19
27
|
|
20
28
|
The Rete implementation in this library largely follows the outline presented in [\[Doorenbos, 1995\]](http://reports-archive.adm.cs.cmu.edu/anon/1995/CMU-CS-95-113.pdf).
|
21
|
-
|
22
|
-
## Contributing
|
23
|
-
|
24
|
-
1. Fork it
|
25
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
26
|
-
3. Commit your changes (`git commit -am 'Added some feature'`)
|
27
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
28
|
-
5. Create new Pull Request
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
class AlphaIndex
|
3
|
+
attr_reader :pattern, :index
|
4
|
+
private :pattern
|
5
|
+
private :index
|
6
|
+
|
7
|
+
def initialize(pattern)
|
8
|
+
@pattern = pattern
|
9
|
+
@index = Hash.new { |h, k| h[k] = [] }
|
10
|
+
end
|
11
|
+
|
12
|
+
def add(wme)
|
13
|
+
collection_for_wme(wme).push(wme)
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove(wme)
|
17
|
+
collection = collection_for_wme(wme)
|
18
|
+
collection.delete(wme)
|
19
|
+
if collection.empty?
|
20
|
+
# release some memory
|
21
|
+
index.delete(hashed_key(wme))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def collection_for_wme(wme)
|
26
|
+
index[hashed_key(wme)]
|
27
|
+
end
|
28
|
+
|
29
|
+
def collections_for_template(template)
|
30
|
+
return nil unless template_matches_pattern?(template)
|
31
|
+
|
32
|
+
# here we know that all fields on which we're indexing are concrete in the template
|
33
|
+
collection_for_wme(template)
|
34
|
+
end
|
35
|
+
|
36
|
+
private def template_matches_pattern?(template)
|
37
|
+
template_element_matches_pattern?(:subject, template.subject) &&
|
38
|
+
template_element_matches_pattern?(:predicate, template.predicate) &&
|
39
|
+
template_element_matches_pattern?(:object, template.object)
|
40
|
+
end
|
41
|
+
|
42
|
+
private def template_element_matches_pattern?(member, template_element)
|
43
|
+
if Template.concrete?(template_element)
|
44
|
+
pattern.include?(member)
|
45
|
+
else
|
46
|
+
!pattern.include?(member)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private def key(wme)
|
51
|
+
pattern.map { wme.public_send(_1) }
|
52
|
+
end
|
53
|
+
|
54
|
+
private def hashed_key(wme)
|
55
|
+
key(wme).map(&:hash)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -2,16 +2,14 @@ module Wongi::Engine
|
|
2
2
|
class AlphaMemory
|
3
3
|
attr_reader :betas, :template, :rete
|
4
4
|
|
5
|
-
def initialize(template, rete
|
5
|
+
def initialize(template, rete)
|
6
6
|
@template = template
|
7
7
|
@rete = rete
|
8
8
|
@betas = []
|
9
|
-
@wmes = []
|
10
9
|
@frozen = false
|
11
10
|
end
|
12
11
|
|
13
12
|
def activate(wme)
|
14
|
-
wme.overlay.add_wme(wme, self)
|
15
13
|
# TODO: it used to activate before adding to the list. mandated by the original thesis. investigate. it appears to create duplicate tokens - needs a remedy in collecting nodes
|
16
14
|
betas.each do |beta|
|
17
15
|
beta.alpha_activate wme
|
@@ -19,18 +17,12 @@ module Wongi::Engine
|
|
19
17
|
end
|
20
18
|
|
21
19
|
def deactivate(wme)
|
22
|
-
wme
|
20
|
+
# p deactivate: {wme:}
|
23
21
|
betas.each do |beta|
|
24
22
|
beta.alpha_deactivate wme
|
25
23
|
end
|
26
24
|
end
|
27
25
|
|
28
|
-
def snapshot!(alpha)
|
29
|
-
alpha.wmes.map(&:dup).each do |wme|
|
30
|
-
activate wme
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
26
|
def inspect
|
35
27
|
"<Alpha #{__id__} template=#{template}>"
|
36
28
|
end
|
@@ -38,19 +30,5 @@ module Wongi::Engine
|
|
38
30
|
def to_s
|
39
31
|
inspect
|
40
32
|
end
|
41
|
-
|
42
|
-
def size
|
43
|
-
wmes.count
|
44
|
-
end
|
45
|
-
|
46
|
-
def wmes
|
47
|
-
Enumerator.new do |y|
|
48
|
-
rete.overlays.each do |overlay|
|
49
|
-
overlay.raw_wmes(self).dup.each do |wme|
|
50
|
-
y << wme
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
33
|
end
|
56
34
|
end
|
@@ -27,41 +27,43 @@ module Wongi::Engine
|
|
27
27
|
|
28
28
|
def alpha_activate(wme)
|
29
29
|
# we need to re-run all WMEs through the aggregator, so the new incoming one doesn't matter
|
30
|
-
|
31
|
-
evaluate(wme, token)
|
30
|
+
tokens.each do |token|
|
31
|
+
evaluate(wme: wme, token: token)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
35
|
def alpha_deactivate(wme)
|
36
36
|
# we need to re-run all WMEs through the aggregator, so the new incoming one doesn't matter
|
37
|
-
|
38
|
-
evaluate(wme, token)
|
37
|
+
tokens.each do |token|
|
38
|
+
evaluate(wme: wme, token: token)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
42
|
def beta_activate(token)
|
43
|
-
|
43
|
+
return if tokens.find { |t| t.duplicate? token }
|
44
|
+
|
45
|
+
overlay.add_token(token)
|
46
|
+
evaluate(wme: nil, token: token)
|
44
47
|
end
|
45
48
|
|
46
49
|
def beta_deactivate(token)
|
47
|
-
|
48
|
-
|
49
|
-
child.beta_deactivate t if t.parent == token
|
50
|
-
end
|
51
|
-
end
|
50
|
+
overlay.remove_token(token)
|
51
|
+
beta_deactivate_children(token: token)
|
52
52
|
end
|
53
53
|
|
54
54
|
def refresh_child(child)
|
55
|
-
|
56
|
-
evaluate(nil, token, child)
|
55
|
+
tokens.each do |token|
|
56
|
+
evaluate(wme: nil, token: token, child: child)
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
def evaluate(wme
|
60
|
+
def evaluate(wme:, token:, child: nil)
|
61
61
|
# clean up previous decisions
|
62
|
-
|
62
|
+
# # TODO: optimise: only clean up if the value changed
|
63
|
+
beta_deactivate_children(token: token)
|
63
64
|
|
64
|
-
|
65
|
+
template = specialize(alpha.template, tests, token)
|
66
|
+
candidates = select_wmes(template) { |asserted_wme| matches?(token, asserted_wme) }
|
65
67
|
|
66
68
|
return if candidates.empty?
|
67
69
|
|
@@ -7,12 +7,17 @@ module Wongi::Engine
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def beta_activate(token, _wme = nil, _assignments = {})
|
10
|
+
return if tokens.find { |t| t.duplicate? token }
|
11
|
+
|
12
|
+
overlay.add_token(token)
|
10
13
|
children.each do |child|
|
11
|
-
|
14
|
+
value = @body.respond_to?(:call) ? @body.call(token) : @body
|
15
|
+
child.beta_activate Token.new(child, token, nil, { @variable => value })
|
12
16
|
end
|
13
17
|
end
|
14
18
|
|
15
19
|
def beta_deactivate(token)
|
20
|
+
overlay.remove_token(token)
|
16
21
|
children.each do |child|
|
17
22
|
child.tokens.each do |t|
|
18
23
|
if t.parent == token
|
@@ -24,7 +29,7 @@ module Wongi::Engine
|
|
24
29
|
end
|
25
30
|
|
26
31
|
def refresh_child(child)
|
27
|
-
|
32
|
+
tokens.each do |token|
|
28
33
|
child.beta_activate Token.new(child, token, nil, { @variable => @body.respond_to?(:call) ? @body.call(token) : @body })
|
29
34
|
end
|
30
35
|
end
|
@@ -1,26 +1,5 @@
|
|
1
1
|
module Wongi::Engine
|
2
2
|
class BetaNode
|
3
|
-
module TokenContainer
|
4
|
-
def tokens
|
5
|
-
Enumerator.new do |y|
|
6
|
-
rete.overlays.each do |overlay|
|
7
|
-
overlay.raw_tokens(self).dup.each do |token|
|
8
|
-
y << token unless token.deleted?
|
9
|
-
end
|
10
|
-
overlay.raw_tokens(self).reject!(&:deleted?)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def empty?
|
16
|
-
tokens.first.nil?
|
17
|
-
end
|
18
|
-
|
19
|
-
def size
|
20
|
-
tokens.count
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
3
|
include CoreExt
|
25
4
|
|
26
5
|
attr_writer :rete
|
@@ -58,6 +37,7 @@ module Wongi::Engine
|
|
58
37
|
abstract :beta_activate
|
59
38
|
abstract :beta_deactivate
|
60
39
|
abstract :beta_reactivate
|
40
|
+
abstract :refresh_child
|
61
41
|
|
62
42
|
def assignment_node(variable, body)
|
63
43
|
node = AssignmentNode.new self, variable, body
|
@@ -69,8 +49,41 @@ module Wongi::Engine
|
|
69
49
|
parent.refresh_child self
|
70
50
|
end
|
71
51
|
|
72
|
-
def
|
73
|
-
|
52
|
+
def beta_deactivate_children(token: nil, wme: nil, children: self.children)
|
53
|
+
children.each do |child|
|
54
|
+
child.tokens.select { (token.nil? || _1.parent == token) && (wme.nil? || _1.wme == wme) }.each do |child_token|
|
55
|
+
child.beta_deactivate(child_token)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private def select_wmes(template)
|
61
|
+
rete.current_overlay.select(template)
|
62
|
+
end
|
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
|
+
|
73
|
+
def tokens
|
74
|
+
overlay.node_tokens(self)
|
75
|
+
end
|
76
|
+
|
77
|
+
def overlay
|
78
|
+
rete.current_overlay
|
79
|
+
end
|
80
|
+
|
81
|
+
def empty?
|
82
|
+
tokens.first.nil?
|
83
|
+
end
|
84
|
+
|
85
|
+
def size
|
86
|
+
tokens.count
|
74
87
|
end
|
75
88
|
|
76
89
|
private
|
@@ -10,22 +10,20 @@ module Wongi
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def beta_activate(token)
|
13
|
+
return if tokens.find { |t| t.duplicate? token }
|
14
|
+
|
13
15
|
return unless test.passes?(token)
|
14
16
|
|
17
|
+
overlay.add_token(token)
|
18
|
+
|
15
19
|
children.each do |child|
|
16
20
|
child.beta_activate Token.new(child, token, nil, {})
|
17
21
|
end
|
18
22
|
end
|
19
23
|
|
20
24
|
def beta_deactivate(token)
|
21
|
-
|
22
|
-
|
23
|
-
if t.parent == token
|
24
|
-
child.beta_deactivate t
|
25
|
-
# token.destroy
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
25
|
+
overlay.remove_token(token)
|
26
|
+
beta_deactivate_children(token: token)
|
29
27
|
end
|
30
28
|
|
31
29
|
def equivalent?(test)
|
@@ -33,12 +31,9 @@ module Wongi
|
|
33
31
|
end
|
34
32
|
|
35
33
|
def refresh_child(child)
|
36
|
-
|
37
|
-
|
38
|
-
parent.tokens.each do |token|
|
39
|
-
beta_activate token
|
34
|
+
tokens.select { test.passes?(_1) }.each do |token|
|
35
|
+
child.beta_activate Token.new(child, token, nil, {})
|
40
36
|
end
|
41
|
-
self.children = tmp
|
42
37
|
end
|
43
38
|
end
|
44
39
|
end
|
@@ -1,19 +1,5 @@
|
|
1
1
|
module Wongi
|
2
2
|
module Engine
|
3
|
-
TokenAssignment = Struct.new(:wme, :field) do
|
4
|
-
def call(_token = nil)
|
5
|
-
wme.send field
|
6
|
-
end
|
7
|
-
|
8
|
-
def inspect
|
9
|
-
"#{field} of #{wme}"
|
10
|
-
end
|
11
|
-
|
12
|
-
def to_s
|
13
|
-
inspect
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
3
|
class BetaTest
|
18
4
|
attr_reader :field, :variable
|
19
5
|
|
@@ -57,8 +43,10 @@ module Wongi
|
|
57
43
|
end
|
58
44
|
|
59
45
|
def alpha_activate(wme)
|
46
|
+
# p alpha_activate: {class: self.class, object_id:, wme:}
|
60
47
|
assignments = collect_assignments(wme)
|
61
|
-
|
48
|
+
|
49
|
+
tokens.each do |token|
|
62
50
|
next unless matches?(token, wme)
|
63
51
|
|
64
52
|
children.each do |beta|
|
@@ -68,15 +56,17 @@ module Wongi
|
|
68
56
|
end
|
69
57
|
|
70
58
|
def alpha_deactivate(wme)
|
71
|
-
|
72
|
-
child.tokens.each do |token|
|
73
|
-
child.beta_deactivate token if token.wme == wme
|
74
|
-
end
|
75
|
-
end
|
59
|
+
beta_deactivate_children(wme: wme)
|
76
60
|
end
|
77
61
|
|
78
62
|
def beta_activate(token)
|
79
|
-
|
63
|
+
# p beta_activate: {class: self.class, object_id:, token:}
|
64
|
+
return if tokens.find { |t| t.duplicate? token }
|
65
|
+
|
66
|
+
overlay.add_token(token)
|
67
|
+
|
68
|
+
template = specialize(alpha.template, tests, token)
|
69
|
+
select_wmes(template).each do |wme|
|
80
70
|
next unless matches?(token, wme)
|
81
71
|
|
82
72
|
assignments = collect_assignments(wme)
|
@@ -87,17 +77,15 @@ module Wongi
|
|
87
77
|
end
|
88
78
|
|
89
79
|
def beta_deactivate(token)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
end
|
94
|
-
end
|
80
|
+
# p beta_deactivate: {class: self.class, object_id:, token:}
|
81
|
+
overlay.remove_token(token)
|
82
|
+
beta_deactivate_children(token: token)
|
95
83
|
end
|
96
84
|
|
97
85
|
def refresh_child(child)
|
98
|
-
alpha.
|
86
|
+
select_wmes(alpha.template).each do |wme|
|
99
87
|
assignments = collect_assignments(wme)
|
100
|
-
|
88
|
+
tokens.each do |token|
|
101
89
|
child.beta_activate Token.new(child, token, wme, assignments) if matches?(token, wme)
|
102
90
|
end
|
103
91
|
end
|
@@ -106,7 +94,7 @@ module Wongi
|
|
106
94
|
protected
|
107
95
|
|
108
96
|
def matches?(token, wme)
|
109
|
-
|
97
|
+
tests.each do |test|
|
110
98
|
return false unless test.matches?(token, wme)
|
111
99
|
end
|
112
100
|
true
|
@@ -1,52 +1,40 @@
|
|
1
1
|
module Wongi
|
2
2
|
module Engine
|
3
3
|
class NccNode < BetaNode
|
4
|
-
include TokenContainer
|
5
|
-
|
6
4
|
attr_accessor :partner
|
7
5
|
|
8
6
|
def beta_activate(token)
|
9
|
-
|
7
|
+
# p beta_activate: {class: self.class, object_id:, token:}
|
8
|
+
return if tokens.find { |t| t.duplicate? token }
|
10
9
|
|
11
|
-
|
12
|
-
t.overlay.add_token(t, self)
|
10
|
+
overlay.add_token(token)
|
13
11
|
partner.tokens.each do |ncc_token|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
ncc_token.owner = t
|
12
|
+
if partner.owner_for(ncc_token) == token
|
13
|
+
overlay.add_ncc_token(token, ncc_token)
|
14
|
+
end
|
18
15
|
end
|
19
|
-
return
|
16
|
+
return if overlay.ncc_tokens_for(token).any?
|
20
17
|
|
21
18
|
children.each do |child|
|
22
|
-
child.beta_activate Token.new(child,
|
19
|
+
child.beta_activate Token.new(child, token, nil, {})
|
23
20
|
end
|
24
21
|
end
|
25
22
|
|
26
23
|
def beta_deactivate(token)
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
t.overlay.remove_token(t, self)
|
31
|
-
t.deleted!
|
32
|
-
partner.tokens.select { |ncc| ncc.owner == t }.each do |ncc_token|
|
33
|
-
ncc_token.owner = nil
|
34
|
-
t.ncc_results.delete(ncc_token)
|
35
|
-
end
|
36
|
-
children.each do |beta|
|
37
|
-
beta.tokens.select { |child_token| child_token.parent == t }.each do |child_token|
|
38
|
-
beta.beta_deactivate(child_token)
|
39
|
-
end
|
40
|
-
end
|
24
|
+
# p beta_deactivate: {class: self.class, object_id:, token:}
|
25
|
+
overlay.remove_token(token)
|
26
|
+
beta_deactivate_children(token: token)
|
41
27
|
end
|
42
28
|
|
43
29
|
def ncc_activate(token)
|
30
|
+
# p ncc_activate: {class: self.class, object_id:, token:}
|
44
31
|
children.each do |child|
|
45
32
|
child.beta_activate Token.new(child, token, nil, {})
|
46
33
|
end
|
47
34
|
end
|
48
35
|
|
49
36
|
def ncc_deactivate(token)
|
37
|
+
# p ncc_deactivate: {class: self.class, object_id:, token:}
|
50
38
|
children.each do |beta|
|
51
39
|
beta.tokens.select { |t| t.parent == token }.each do |t|
|
52
40
|
beta.beta_deactivate t
|
@@ -56,7 +44,7 @@ module Wongi
|
|
56
44
|
|
57
45
|
def refresh_child(child)
|
58
46
|
tokens.each do |token|
|
59
|
-
child.beta_activate Token.new(child, token, nil, {}) if token.
|
47
|
+
child.beta_activate Token.new(child, token, nil, {}) if overlay.ncc_tokens_for(token).empty?
|
60
48
|
end
|
61
49
|
end
|
62
50
|
end
|
@@ -1,39 +1,39 @@
|
|
1
1
|
module Wongi
|
2
2
|
module Engine
|
3
3
|
class NccPartner < BetaNode
|
4
|
-
include TokenContainer
|
5
|
-
|
6
4
|
attr_accessor :ncc, :divergent
|
7
5
|
|
8
6
|
def beta_activate(token)
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
# p beta_activate: {class: self.class, object_id:, token:}
|
8
|
+
return if tokens.find { |t| t.duplicate? token }
|
9
|
+
|
10
|
+
overlay.add_token(token)
|
11
|
+
|
12
|
+
owner = owner_for(token)
|
12
13
|
return unless owner
|
13
14
|
|
14
|
-
owner
|
15
|
-
t.owner = owner
|
15
|
+
overlay.add_ncc_token(owner, token)
|
16
16
|
owner.node.ncc_deactivate owner
|
17
17
|
end
|
18
18
|
|
19
|
-
def beta_deactivate(
|
20
|
-
|
21
|
-
return unless token
|
19
|
+
def beta_deactivate(token)
|
20
|
+
# p beta_deactivate: {class: self.class, object_id:, token:}
|
22
21
|
|
23
|
-
token
|
22
|
+
# fetch the owner before deleting the token
|
23
|
+
owner = overlay.ncc_owner(token)
|
24
24
|
|
25
|
-
|
25
|
+
overlay.remove_token(token)
|
26
26
|
return unless owner
|
27
27
|
|
28
|
-
owner.
|
29
|
-
ncc.ncc_activate owner if owner.ncc_results.empty?
|
28
|
+
ncc.ncc_activate owner if overlay.ncc_tokens_for(owner).empty?
|
30
29
|
end
|
31
30
|
|
32
|
-
private
|
33
|
-
|
34
31
|
def owner_for(token)
|
35
|
-
|
36
|
-
|
32
|
+
# find a token in the NCC node that has the same lineage as this token:
|
33
|
+
# - the NCC token will be a direct descendant of the divergent, therefore
|
34
|
+
# - one of this token's ancestors will be a duplicate of that token
|
35
|
+
# TODO: this should be more resilient, but child token creation does not allow for much else at the moment
|
36
|
+
ncc.tokens.find { |t| token.ancestors.any? { |ancestor| ancestor.duplicate?(t) } }
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|