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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ba0f7c9c09df2f3f379bc1983b32807770014d54
4
- data.tar.gz: 12b8b964c0b03c205ac0e44cdcfe8f10f14c7f34
3
+ metadata.gz: 685d73d3400c03903114505a66f2bb6ca6ca8ba2
4
+ data.tar.gz: ac0de44ac50ad33b7a040bcc786e82f50fa16946
5
5
  SHA512:
6
- metadata.gz: 3e5ea34e087ef89954e1f5543d394883c2c2173c02d4764e816d97c9b764b6f73eed2b247f08033edab3127f752e7417686d0b61853250c533145274304126f7
7
- data.tar.gz: 59c4f2e6af5b95a8d607f5c0bee99885fff6d302fef96f3d45320bada13ae2f5548788196b82cc83f6e3ada93a02c0c966eceaeaf29d8f75f1561c2f3459422e
6
+ metadata.gz: 6d9402a2c69eda4a5262b66fefdba7212c20e62f5a98a3b9290e0279de58ec7d0a0631ee71cf106bc0dbfd0545302d3665bf4e233f630a1ff907b22203d91933
7
+ data.tar.gz: 231b6574013f60209ea5debf67bc81afa575fa112861d9afc1a78ef9b098af16985c9fff9f0ca73393e474abba3e263b44f3a6b219dc051b8229ea36ff8619ac
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.5
6
+ - 2.2.0
7
+ - rbx-2.4.1
8
+ - jruby-19mode
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
- ## Installation
5
+ [![Build Status](https://travis-ci.org/ulfurinn/wongi-engine.svg?branch=master)](https://travis-ci.org/ulfurinn/wongi-engine) (MRI 1.9.3, 2.0, 2.1, 2.2, Rubinius, JRuby)
6
6
 
7
- Add this line to your application's Gemfile:
7
+ ## Word of caution
8
8
 
9
- gem 'wongi-engine'
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
- t = Token.new( token, wme, assignments)
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
- dp "generated token #{t}"
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
@@ -135,6 +135,11 @@ module Wongi::Engine
135
135
  @tokens.empty?
136
136
  end
137
137
 
138
+ def size
139
+ @tokens.size
140
+ end
141
+ alias_method :length, :size
142
+
138
143
  private
139
144
 
140
145
  def propagate_activation token, wme, assignments
@@ -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
- t = Token.new( token, newwme, assignments)
28
- t.node = self
29
- existing = @tokens.find { |et| et.duplicate? t }
30
- if existing
31
- t = existing
32
- else
33
- dp "generated token #{t}"
34
- t.node = self
35
- @tokens << t
36
- end
37
- @alpha.wmes.each do |wme|
38
- if matches?( t, wme )
39
- make_join_result(t, wme)
40
- end
41
- end
42
- if t.neg_join_results.empty?
43
- self.children.each do |child|
44
- child.beta_activate t, nil, {}
45
- end
46
- end
47
- end
48
-
49
- def refresh_child child
50
- safe_tokens.each do |token|
51
- if token.neg_join_results.empty?
52
- child.beta_activate token, nil, {}
53
- end
54
- end
55
- alpha.wmes.each do |wme|
56
- alpha_activate wme
57
- end
58
- end
59
-
60
- def delete_token token
61
- tokens.delete token
62
- token.neg_join_results.each do |njr|
63
- njr.wme.neg_join_results.delete njr if njr.wme
64
- end
65
- token.neg_join_results.clear
66
- end
67
-
68
- protected
69
-
70
- def matches? token, wme
71
- puts "matching #{wme} against #{token}" if debug?
72
- @tests.each do |test|
73
- return false unless test.matches?( token, wme )
74
- end
75
- true
76
- end
77
-
78
- def make_join_result token, wme
79
- njr = NegJoinResult.new token, wme
80
- token.neg_join_results << njr
81
- wme.neg_join_results << njr
82
- end
83
-
84
- def safe_tokens
85
- Enumerator.new do |y|
86
- @tokens.dup.each do |token|
87
- y << token unless token.deleted?
88
- end
89
- end
90
- end
91
-
92
- end
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
@@ -12,8 +12,7 @@ module Wongi
12
12
  end
13
13
 
14
14
  def beta_activate token, wme, assignments
15
- super
16
- generated = @tokens.last
15
+ generated = super
17
16
  @actions.each do |action|
18
17
  # @tokens.each do |t|
19
18
  # action.execute t
@@ -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