wongi-engine 0.0.14 → 0.0.16
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/.travis.yml +8 -0
- data/README.md +3 -11
- data/lib/wongi-engine/beta/beta_memory.rb +3 -4
- data/lib/wongi-engine/beta/beta_node.rb +5 -0
- data/lib/wongi-engine/beta/neg_node.rb +92 -94
- data/lib/wongi-engine/beta/production_node.rb +1 -2
- data/lib/wongi-engine/dsl/actions/statement_generator.rb +55 -55
- data/lib/wongi-engine/network.rb +442 -443
- data/lib/wongi-engine/token.rb +138 -133
- data/lib/wongi-engine/version.rb +1 -1
- data/lib/wongi-engine/wme.rb +138 -140
- data/spec/bug_specs/issue_4_spec.rb +9 -9
- data/spec/dataset_spec.rb +3 -3
- data/spec/filter_specs/assert_test_spec.rb +6 -6
- data/spec/filter_specs/less_test_spec.rb +1 -1
- data/spec/high_level_spec.rb +34 -34
- data/spec/rule_specs/any_rule_spec.rb +4 -4
- data/spec/rule_specs/assign_spec.rb +3 -4
- data/spec/rule_specs/maybe_rule_spec.rb +12 -12
- data/spec/rule_specs/ncc_spec.rb +6 -6
- data/spec/rule_specs/negative_rule_spec.rb +70 -70
- data/spec/ruleset_spec.rb +8 -8
- data/spec/simple_action_spec.rb +3 -3
- data/spec/spec_helper.rb +2 -0
- data/spec/wme_spec.rb +21 -21
- data/wongi-engine.gemspec +6 -2
- metadata +53 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 685d73d3400c03903114505a66f2bb6ca6ca8ba2
|
4
|
+
data.tar.gz: ac0de44ac50ad33b7a040bcc786e82f50fa16946
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d9402a2c69eda4a5262b66fefdba7212c20e62f5a98a3b9290e0279de58ec7d0a0631ee71cf106bc0dbfd0545302d3665bf4e233f630a1ff907b22203d91933
|
7
|
+
data.tar.gz: 231b6574013f60209ea5debf67bc81afa575fa112861d9afc1a78ef9b098af16985c9fff9f0ca73393e474abba3e263b44f3a6b219dc051b8229ea36ff8619ac
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -2,19 +2,11 @@
|
|
2
2
|
|
3
3
|
This library contains a rule engine written in Ruby. It's based on the [Rete algorithm](http://en.wikipedia.org/wiki/Rete_algorithm) and uses a DSL to express rules in a readable way.
|
4
4
|
|
5
|
-
|
5
|
+
[](https://travis-ci.org/ulfurinn/wongi-engine) (MRI 1.9.3, 2.0, 2.1, 2.2, Rubinius, JRuby)
|
6
6
|
|
7
|
-
|
7
|
+
## Word of caution
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
And then execute:
|
12
|
-
|
13
|
-
$ bundle
|
14
|
-
|
15
|
-
Or install it yourself as:
|
16
|
-
|
17
|
-
$ gem install wongi-engine
|
9
|
+
This is complex and fragile machinery, and there may be subtle bugs that are only revealed with nontrivial usage. Please test your rules extensively and report any behaviour that is not consistent with your expectations.
|
18
10
|
|
19
11
|
## Tutorial
|
20
12
|
|
@@ -29,13 +29,11 @@ module Wongi::Engine
|
|
29
29
|
|
30
30
|
def beta_activate token, wme, assignments
|
31
31
|
dp "MEMORY beta-activated with #{wme} #{wme.object_id}"
|
32
|
-
|
33
|
-
t.node = self
|
34
|
-
existing = @tokens.find { |et| et.duplicate? t }
|
32
|
+
existing = @tokens.reject(&:deleted?).find { |et| et.duplicate? self, token, wme, assignments }
|
35
33
|
if existing
|
36
34
|
t = existing
|
37
35
|
else
|
38
|
-
|
36
|
+
t = Token.new( token, wme, assignments)
|
39
37
|
t.node = self
|
40
38
|
@tokens << t
|
41
39
|
end
|
@@ -46,6 +44,7 @@ module Wongi::Engine
|
|
46
44
|
child.beta_activate t
|
47
45
|
end
|
48
46
|
end
|
47
|
+
t
|
49
48
|
end
|
50
49
|
|
51
50
|
def refresh_child child
|
@@ -1,94 +1,92 @@
|
|
1
|
-
module Wongi
|
2
|
-
module Engine
|
3
|
-
|
4
|
-
NegJoinResult = Struct.new :owner, :wme
|
5
|
-
|
6
|
-
class NegNode < BetaNode
|
7
|
-
|
8
|
-
attr_reader :tokens, :alpha, :tests
|
9
|
-
|
10
|
-
def initialize parent, tests, alpha, unsafe
|
11
|
-
super(parent)
|
12
|
-
@tests, @alpha, @unsafe = tests, alpha, unsafe
|
13
|
-
@tokens = []
|
14
|
-
end
|
15
|
-
|
16
|
-
def alpha_activate wme
|
17
|
-
self.tokens.each do |token|
|
18
|
-
if matches?( token, wme ) && ( @unsafe || ! token.generated?( wme ) )# feedback loop protection
|
19
|
-
# order matters for proper invalidation
|
20
|
-
make_join_result(token, wme)
|
21
|
-
token.delete_children #if token.neg_join_results.empty? # TODO why was this check here? it seems to break things
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def beta_activate token, newwme, assignments
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
t =
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
end
|
94
|
-
end
|
1
|
+
module Wongi
|
2
|
+
module Engine
|
3
|
+
|
4
|
+
NegJoinResult = Struct.new :owner, :wme
|
5
|
+
|
6
|
+
class NegNode < BetaNode
|
7
|
+
|
8
|
+
attr_reader :tokens, :alpha, :tests
|
9
|
+
|
10
|
+
def initialize parent, tests, alpha, unsafe
|
11
|
+
super(parent)
|
12
|
+
@tests, @alpha, @unsafe = tests, alpha, unsafe
|
13
|
+
@tokens = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def alpha_activate wme
|
17
|
+
self.tokens.each do |token|
|
18
|
+
if matches?( token, wme ) && ( @unsafe || ! token.generated?( wme ) )# feedback loop protection
|
19
|
+
# order matters for proper invalidation
|
20
|
+
make_join_result(token, wme)
|
21
|
+
token.delete_children #if token.neg_join_results.empty? # TODO why was this check here? it seems to break things
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def beta_activate token, newwme, assignments
|
27
|
+
existing = @tokens.find { |et| et.duplicate? self, token, newwme, assignments }
|
28
|
+
if existing
|
29
|
+
t = existing
|
30
|
+
else
|
31
|
+
t = Token.new( token, newwme, assignments)
|
32
|
+
t.node = self
|
33
|
+
@tokens << t
|
34
|
+
end
|
35
|
+
@alpha.wmes.each do |wme|
|
36
|
+
if matches?( t, wme )
|
37
|
+
make_join_result(t, wme)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
if t.neg_join_results.empty?
|
41
|
+
self.children.each do |child|
|
42
|
+
child.beta_activate t, nil, {}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def refresh_child child
|
48
|
+
safe_tokens.each do |token|
|
49
|
+
if token.neg_join_results.empty?
|
50
|
+
child.beta_activate token, nil, {}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
alpha.wmes.each do |wme|
|
54
|
+
alpha_activate wme
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def delete_token token
|
59
|
+
tokens.delete token
|
60
|
+
token.neg_join_results.each do |njr|
|
61
|
+
njr.wme.neg_join_results.delete njr if njr.wme
|
62
|
+
end
|
63
|
+
token.neg_join_results.clear
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
def matches? token, wme
|
69
|
+
puts "matching #{wme} against #{token}" if debug?
|
70
|
+
@tests.each do |test|
|
71
|
+
return false unless test.matches?( token, wme )
|
72
|
+
end
|
73
|
+
true
|
74
|
+
end
|
75
|
+
|
76
|
+
def make_join_result token, wme
|
77
|
+
njr = NegJoinResult.new token, wme
|
78
|
+
token.neg_join_results << njr
|
79
|
+
wme.neg_join_results << njr
|
80
|
+
end
|
81
|
+
|
82
|
+
def safe_tokens
|
83
|
+
Enumerator.new do |y|
|
84
|
+
@tokens.dup.each do |token|
|
85
|
+
y << token unless token.deleted?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -1,55 +1,55 @@
|
|
1
|
-
module Wongi::Engine
|
2
|
-
class StatementGenerator < Action
|
3
|
-
|
4
|
-
def initialize template
|
5
|
-
@template = template
|
6
|
-
end
|
7
|
-
|
8
|
-
def execute token
|
9
|
-
|
10
|
-
subject = if Template.variable?( @template.subject )
|
11
|
-
v = token[ @template.subject ]
|
12
|
-
raise "Unbound variable #{@template.subject} in token #{token}" if v.nil?
|
13
|
-
v
|
14
|
-
else
|
15
|
-
@template.subject
|
16
|
-
end
|
17
|
-
|
18
|
-
predicate = if Template.variable?( @template.predicate )
|
19
|
-
v = token[ @template.predicate ]
|
20
|
-
raise "Unbound variable #{@template.predicate} in token #{token}" if v.nil?
|
21
|
-
v
|
22
|
-
else
|
23
|
-
@template.predicate
|
24
|
-
end
|
25
|
-
|
26
|
-
object = if Template.variable?( @template.object )
|
27
|
-
v = token[ @template.object ]
|
28
|
-
raise "Unbound variable #{@template.object} in token #{token}" if v.nil?
|
29
|
-
v
|
30
|
-
else
|
31
|
-
@template.object
|
32
|
-
end
|
33
|
-
|
34
|
-
# link to rete here to ensure proper linking with token
|
35
|
-
wme = WME.new subject, predicate, object, rete
|
36
|
-
|
37
|
-
production.tracer.trace( action: self, wme: wme ) if production.tracer
|
38
|
-
if existing = rete.exists?( wme )
|
39
|
-
generated = existing.generating_tokens.size
|
40
|
-
if generated > 0 && ! token.generated_wmes.include?( existing )
|
41
|
-
token.generated_wmes << existing
|
42
|
-
existing.generating_tokens << token
|
43
|
-
end
|
44
|
-
else
|
45
|
-
token.generated_wmes << wme
|
46
|
-
wme.generating_tokens << token
|
47
|
-
# this MUST be done after we link the wme and the token
|
48
|
-
# in order for neg rule invalidation to work
|
49
|
-
rete << wme
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
end
|
55
|
-
end
|
1
|
+
module Wongi::Engine
|
2
|
+
class StatementGenerator < Action
|
3
|
+
|
4
|
+
def initialize template
|
5
|
+
@template = template
|
6
|
+
end
|
7
|
+
|
8
|
+
def execute token
|
9
|
+
|
10
|
+
subject = if Template.variable?( @template.subject )
|
11
|
+
v = token[ @template.subject ]
|
12
|
+
raise "Unbound variable #{@template.subject} in token #{token}" if v.nil?
|
13
|
+
v
|
14
|
+
else
|
15
|
+
@template.subject
|
16
|
+
end
|
17
|
+
|
18
|
+
predicate = if Template.variable?( @template.predicate )
|
19
|
+
v = token[ @template.predicate ]
|
20
|
+
raise "Unbound variable #{@template.predicate} in token #{token}" if v.nil?
|
21
|
+
v
|
22
|
+
else
|
23
|
+
@template.predicate
|
24
|
+
end
|
25
|
+
|
26
|
+
object = if Template.variable?( @template.object )
|
27
|
+
v = token[ @template.object ]
|
28
|
+
raise "Unbound variable #{@template.object} in token #{token}" if v.nil?
|
29
|
+
v
|
30
|
+
else
|
31
|
+
@template.object
|
32
|
+
end
|
33
|
+
|
34
|
+
# link to rete here to ensure proper linking with token
|
35
|
+
wme = WME.new subject, predicate, object, rete
|
36
|
+
|
37
|
+
production.tracer.trace( action: self, wme: wme ) if production.tracer
|
38
|
+
if existing = rete.exists?( wme )
|
39
|
+
generated = existing.generating_tokens.size
|
40
|
+
if generated > 0 && ! token.generated_wmes.include?( existing )
|
41
|
+
token.generated_wmes << existing
|
42
|
+
existing.generating_tokens << token
|
43
|
+
end
|
44
|
+
else
|
45
|
+
token.generated_wmes << wme
|
46
|
+
wme.generating_tokens << token
|
47
|
+
# this MUST be done after we link the wme and the token
|
48
|
+
# in order for neg rule invalidation to work
|
49
|
+
rete << wme
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|