wongi-engine 0.1.0.alpha1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -1
- data/lib/wongi-engine/core_ext.rb +2 -2
- data/lib/wongi-engine/dsl/actions/statement_generator.rb +2 -1
- data/lib/wongi-engine/network.rb +25 -17
- data/lib/wongi-engine/version.rb +1 -1
- data/lib/wongi-engine/wme.rb +10 -7
- data/spec/generation_spec.rb +116 -0
- data/wongi-engine.gemspec +40 -0
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 242f2dfca581ccebad701500d1329f56645942a6
|
4
|
+
data.tar.gz: fa9eb4e93e95ab0e06ea8dc5475fbfc8b07e5fcd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9db3fcb8333c246b124137c6ba0dcb654816283f3c389c2162268230a2c9acc818f986956a4f0171057564f3dd172ec9b6fdc551b9e1523a44d864ac8fbf7f98
|
7
|
+
data.tar.gz: 1bdef60a60134e32676e880c61f02948e42b34c43baff937b7713d40e2b17fb40c5e5da19fceb1b9845c2f61753ee93e591fd166c1a477e96d5b83fe6d9a472b
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ This library contains a rule engine written in Ruby. It's based on the [Rete alg
|
|
6
6
|
|
7
7
|
## Word of caution
|
8
8
|
|
9
|
-
This is complex and fragile machinery, and there may be subtle bugs that are only revealed with nontrivial usage.
|
9
|
+
This is complex and fragile machinery, and there may be subtle bugs that are only revealed with nontrivial usage. Be conservative with upgrades, test your rules extensively, and please report any behaviour that is not consistent with your expectations.
|
10
10
|
|
11
11
|
## Tutorial
|
12
12
|
|
@@ -459,6 +459,10 @@ The Rete implementation in this library largely follows the outline presented in
|
|
459
459
|
|
460
460
|
## Changelog
|
461
461
|
|
462
|
+
### 0.1.0
|
463
|
+
|
464
|
+
* massively rewritten rule activation; this simplifies development and debugging and opens the road for useful features such as fully reversible custom actions
|
465
|
+
|
462
466
|
### 0.0.17
|
463
467
|
|
464
468
|
* introduced the `assuming` matcher
|
@@ -18,12 +18,12 @@ module Wongi::Engine
|
|
18
18
|
names_hash.each do |name, def_value|
|
19
19
|
|
20
20
|
varname = "@#{name}".to_sym
|
21
|
-
|
21
|
+
predname = "#{name}?".to_sym
|
22
22
|
setname = "#{name}=".to_sym
|
23
23
|
exclname = "#{name}!".to_sym
|
24
24
|
noexclname = "no_#{name}!".to_sym
|
25
25
|
|
26
|
-
define_method
|
26
|
+
define_method predname do
|
27
27
|
if instance_variable_defined?( varname )
|
28
28
|
instance_variable_get( varname )
|
29
29
|
else
|
@@ -33,6 +33,7 @@ module Wongi::Engine
|
|
33
33
|
|
34
34
|
# link to rete here to ensure proper linking with token
|
35
35
|
wme = WME.new subject, predicate, object, rete
|
36
|
+
wme.manual = false
|
36
37
|
|
37
38
|
production.tracer.trace( action: self, wme: wme ) if production.tracer
|
38
39
|
if existing = rete.exists?( wme )
|
@@ -58,7 +59,7 @@ module Wongi::Engine
|
|
58
59
|
l << wme if wme.generating_tokens.empty?
|
59
60
|
end
|
60
61
|
end.each do |wme|
|
61
|
-
rete.retract wme
|
62
|
+
rete.retract wme, automatic: true
|
62
63
|
end
|
63
64
|
end
|
64
65
|
|
data/lib/wongi-engine/network.rb
CHANGED
@@ -92,9 +92,9 @@ module Wongi::Engine
|
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
-
def retract wme
|
95
|
+
def retract wme, options = { }
|
96
96
|
@next_cascade ||= []
|
97
|
-
@next_cascade << [:retract, wme]
|
97
|
+
@next_cascade << [:retract, wme, options]
|
98
98
|
if @current_cascade.nil?
|
99
99
|
@current_cascade = @next_cascade
|
100
100
|
@next_cascade = nil
|
@@ -105,12 +105,12 @@ module Wongi::Engine
|
|
105
105
|
def process_cascade
|
106
106
|
iterations = 0
|
107
107
|
while @current_cascade
|
108
|
-
@current_cascade.each do |(operation, wme)|
|
108
|
+
@current_cascade.each do |(operation, wme, options)|
|
109
109
|
case operation
|
110
110
|
when :assert
|
111
111
|
real_assert wme
|
112
112
|
when :retract
|
113
|
-
real_retract wme
|
113
|
+
real_retract wme, options
|
114
114
|
end
|
115
115
|
end
|
116
116
|
@current_cascade = @next_cascade
|
@@ -130,7 +130,10 @@ module Wongi::Engine
|
|
130
130
|
wme.context = @current_context
|
131
131
|
end
|
132
132
|
|
133
|
-
|
133
|
+
if existing = @cache[wme]
|
134
|
+
existing.manual! if wme.manual?
|
135
|
+
return
|
136
|
+
end
|
134
137
|
|
135
138
|
# puts "ASSERTING #{wme}"
|
136
139
|
@cache[wme] = wme
|
@@ -218,27 +221,31 @@ module Wongi::Engine
|
|
218
221
|
end
|
219
222
|
end
|
220
223
|
|
221
|
-
def real_retract wme,
|
224
|
+
def real_retract wme, options
|
222
225
|
|
223
226
|
if wme.is_a? Array
|
224
|
-
return real_retract( WME.new(*wme),
|
227
|
+
return real_retract( WME.new(*wme), options )
|
225
228
|
end
|
226
229
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
+
real = @cache[wme]
|
231
|
+
|
232
|
+
return if real.nil?
|
233
|
+
if real.generated? # still some generator tokens left
|
234
|
+
if real.manual?
|
235
|
+
real.manual = false
|
236
|
+
else
|
237
|
+
raise "cannot retract automatic facts"
|
238
|
+
end
|
239
|
+
else
|
240
|
+
if options[:automatic] && real.manual? # auto-retracting a fact that has been added manually
|
241
|
+
return
|
230
242
|
end
|
231
243
|
end
|
232
244
|
|
233
|
-
|
234
|
-
wme
|
235
|
-
else
|
236
|
-
#find(wme.subject, wme.predicate, wme.object)
|
237
|
-
@cache[wme]
|
245
|
+
if @current_context
|
246
|
+
@current_context.retracted_wmes << wme
|
238
247
|
end
|
239
248
|
|
240
|
-
return if real.nil?
|
241
|
-
raise "Cannot retract inferred statements" unless real.manual?
|
242
249
|
@cache.delete(real)
|
243
250
|
|
244
251
|
alphas_for( real ).each { |a| a.deactivate real }
|
@@ -323,6 +330,7 @@ module Wongi::Engine
|
|
323
330
|
end
|
324
331
|
end
|
325
332
|
|
333
|
+
# TODO: contexts are probably broken with the new improved handling of manual & generated
|
326
334
|
def retract_context name
|
327
335
|
return unless @contexts.has_key?(name)
|
328
336
|
|
data/lib/wongi-engine/version.rb
CHANGED
data/lib/wongi-engine/wme.rb
CHANGED
@@ -9,9 +9,12 @@ module Wongi::Engine
|
|
9
9
|
attr_reader :alphas, :tokens, :generating_tokens
|
10
10
|
attr_reader :neg_join_results, :opt_join_results
|
11
11
|
attr_predicate :deleted
|
12
|
+
attr_predicate :manual
|
12
13
|
|
13
14
|
def initialize s, p, o, r = nil
|
14
15
|
|
16
|
+
manual!
|
17
|
+
|
15
18
|
@deleted = false
|
16
19
|
@alphas = []
|
17
20
|
@tokens = []
|
@@ -30,11 +33,15 @@ module Wongi::Engine
|
|
30
33
|
end
|
31
34
|
|
32
35
|
def import_into r
|
33
|
-
self.class.new subject, predicate, object, r
|
36
|
+
self.class.new( subject, predicate, object, r ).tap do |wme|
|
37
|
+
wme.manual = self.manual?
|
38
|
+
end
|
34
39
|
end
|
35
40
|
|
36
41
|
def dup
|
37
|
-
self.class.new subject, predicate, object, rete
|
42
|
+
self.class.new( subject, predicate, object, rete ).tap do |wme|
|
43
|
+
wme.manual = self.manual?
|
44
|
+
end
|
38
45
|
end
|
39
46
|
|
40
47
|
def == other
|
@@ -49,12 +56,8 @@ module Wongi::Engine
|
|
49
56
|
end
|
50
57
|
end
|
51
58
|
|
52
|
-
def manual?
|
53
|
-
generating_tokens.empty?
|
54
|
-
end
|
55
|
-
|
56
59
|
def generated?
|
57
|
-
!
|
60
|
+
!generating_tokens.empty?
|
58
61
|
end
|
59
62
|
|
60
63
|
# def destroy
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Wongi::Engine::StatementGenerator do
|
4
|
+
|
5
|
+
let( :engine ) { Wongi::Engine.create }
|
6
|
+
|
7
|
+
let( :transitive_rule ) {
|
8
|
+
rule {
|
9
|
+
forall {
|
10
|
+
has :P, :transitive, true
|
11
|
+
has :X, :P, :Y
|
12
|
+
has :Y, :P, :Z
|
13
|
+
}
|
14
|
+
make {
|
15
|
+
gen :X, :P, :Z
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
let( :production ) { engine << transitive_rule }
|
21
|
+
|
22
|
+
shared_examples 'generation' do
|
23
|
+
|
24
|
+
it 'should generate facts' do
|
25
|
+
engine << %w( Alice relative Bob )
|
26
|
+
engine << %w( Bob relative Dwight )
|
27
|
+
|
28
|
+
expect( production ).to have(1).token
|
29
|
+
expect( engine.find *%w( Alice relative Dwight ) ).not_to be_nil
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should retrct generated facts' do
|
33
|
+
engine << %w( Alice relative Bob )
|
34
|
+
engine << %w( Bob relative Dwight )
|
35
|
+
engine.retract %w( Bob relative Dwight )
|
36
|
+
|
37
|
+
expect( production ).to have(0).tokens
|
38
|
+
expect( engine.find *%w( Alice relative Dwight ) ).to be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'transitive diamond' do
|
42
|
+
|
43
|
+
before :each do
|
44
|
+
engine << %w( Alice relative Bob )
|
45
|
+
engine << %w( Bob relative Dwight )
|
46
|
+
engine << %w( Alice relative Claire )
|
47
|
+
engine << %w( Claire relative Dwight )
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should be created', :debug do
|
51
|
+
expect( production ).to have(2).tokens
|
52
|
+
expect( engine.find *%w( Alice relative Dwight ) ).not_to be_nil
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should remain after a single retraction' do
|
56
|
+
engine.retract %w( Claire relative Dwight )
|
57
|
+
|
58
|
+
expect( production ).to have(1).token
|
59
|
+
expect( engine.find *%w( Alice relative Dwight ) ).not_to be_nil
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should be destroyed after both retractions' do
|
63
|
+
engine.retract %w( Claire relative Dwight )
|
64
|
+
engine.retract %w( Alice relative Bob )
|
65
|
+
|
66
|
+
expect( production ).to have(0).tokens
|
67
|
+
expect( engine.find *%w( Alice relative Dwight ) ).to be_nil
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
context "pre-asserted", :pre do
|
75
|
+
|
76
|
+
before :each do
|
77
|
+
engine << [ "relative", :transitive, true ]
|
78
|
+
end
|
79
|
+
|
80
|
+
it_behaves_like 'generation'
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
context "post-asserted", :post do
|
85
|
+
|
86
|
+
before :each do
|
87
|
+
production
|
88
|
+
engine << [ "relative", :transitive, true ]
|
89
|
+
end
|
90
|
+
|
91
|
+
it_behaves_like 'generation'
|
92
|
+
|
93
|
+
it 'should not retract generated facts marked as manual', :wip do
|
94
|
+
engine << %w( Alice relative Bob )
|
95
|
+
engine << %w( Bob relative Dwight )
|
96
|
+
engine << %w( Alice relative Dwight )
|
97
|
+
engine.retract %w( Alice relative Bob )
|
98
|
+
|
99
|
+
expect( production ).to have(0).tokens
|
100
|
+
expect( engine.find *%w( Alice relative Dwight ) ).not_to be_nil
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should retract generated facts unmarked as manual', :wip do
|
104
|
+
engine << %w( Alice relative Bob )
|
105
|
+
engine << %w( Bob relative Dwight )
|
106
|
+
engine << %w( Alice relative Dwight )
|
107
|
+
engine.retract %w( Alice relative Dwight )
|
108
|
+
engine.retract %w( Alice relative Bob )
|
109
|
+
|
110
|
+
expect( production ).to have(0).tokens
|
111
|
+
expect( engine.find *%w( Alice relative Dwight ) ).to be_nil
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/wongi-engine/version', __FILE__)
|
3
|
+
|
4
|
+
module GemHelper
|
5
|
+
def self.git?
|
6
|
+
File.exists?(".git")
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.hg?
|
10
|
+
File.exists?(".hg")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Gem::Specification.new do |gem|
|
15
|
+
gem.authors = ["Valeri Sokolov"]
|
16
|
+
gem.email = ["ulfurinn@ulfurinn.net"]
|
17
|
+
gem.description = %q{A rule engine.}
|
18
|
+
gem.summary = %q{A forward-chaining rule engine in pure Ruby.}
|
19
|
+
gem.homepage = "https://github.com/ulfurinn/wongi-engine"
|
20
|
+
gem.licenses = %w(MIT)
|
21
|
+
|
22
|
+
if GemHelper.git?
|
23
|
+
gem.files = `git ls-files`.split($\)
|
24
|
+
elsif GemHelper.hg?
|
25
|
+
gem.files = `hg st -cn`.split($\)
|
26
|
+
else
|
27
|
+
raise "cannot enumerate files: not a git or hg repository"
|
28
|
+
end
|
29
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
30
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
31
|
+
gem.name = "wongi-engine"
|
32
|
+
gem.require_paths = ["lib"]
|
33
|
+
gem.version = Wongi::Engine::VERSION
|
34
|
+
|
35
|
+
gem.add_development_dependency 'rake', '~> 10.0'
|
36
|
+
# gem.add_development_dependency 'pry', '~> 0.10'
|
37
|
+
# gem.add_development_dependency 'pry-byebug', '~> 2.0'
|
38
|
+
gem.add_development_dependency 'rspec', '~> 3.1'
|
39
|
+
gem.add_development_dependency 'rspec-collection_matchers', '~> 1.1'
|
40
|
+
end
|
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.1.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Valeri Sokolov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 3.1
|
33
|
+
version: '3.1'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 3.1
|
40
|
+
version: '3.1'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec-collection_matchers
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -130,6 +130,7 @@ files:
|
|
130
130
|
- spec/dsl_spec.rb
|
131
131
|
- spec/filter_specs/assert_test_spec.rb
|
132
132
|
- spec/filter_specs/less_test_spec.rb
|
133
|
+
- spec/generation_spec.rb
|
133
134
|
- spec/high_level_spec.rb
|
134
135
|
- spec/network_spec.rb
|
135
136
|
- spec/rule_specs/any_rule_spec.rb
|
@@ -142,6 +143,7 @@ files:
|
|
142
143
|
- spec/simple_action_spec.rb
|
143
144
|
- spec/spec_helper.rb
|
144
145
|
- spec/wme_spec.rb
|
146
|
+
- wongi-engine.gemspec
|
145
147
|
homepage: https://github.com/ulfurinn/wongi-engine
|
146
148
|
licenses:
|
147
149
|
- MIT
|
@@ -157,9 +159,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
157
159
|
version: '0'
|
158
160
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
159
161
|
requirements:
|
160
|
-
- - "
|
162
|
+
- - ">="
|
161
163
|
- !ruby/object:Gem::Version
|
162
|
-
version:
|
164
|
+
version: '0'
|
163
165
|
requirements: []
|
164
166
|
rubyforge_project:
|
165
167
|
rubygems_version: 2.4.5
|
@@ -173,6 +175,7 @@ test_files:
|
|
173
175
|
- spec/dsl_spec.rb
|
174
176
|
- spec/filter_specs/assert_test_spec.rb
|
175
177
|
- spec/filter_specs/less_test_spec.rb
|
178
|
+
- spec/generation_spec.rb
|
176
179
|
- spec/high_level_spec.rb
|
177
180
|
- spec/network_spec.rb
|
178
181
|
- spec/rule_specs/any_rule_spec.rb
|