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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a090dea5a39943ac1d887178b7d1b9a5df3f618c
4
- data.tar.gz: ceca588238658bf643d067e3502643e554719ef5
3
+ metadata.gz: 242f2dfca581ccebad701500d1329f56645942a6
4
+ data.tar.gz: fa9eb4e93e95ab0e06ea8dc5475fbfc8b07e5fcd
5
5
  SHA512:
6
- metadata.gz: cb3fc2402bcdfe4afbd0b0f5bf72aba18b32f40a89c013c429f0507414f7f243962f42dff3aa31bf412a5345f449eb6d13b152fc9f6120efacd2cade85b4290d
7
- data.tar.gz: 39063e5d6c69a37bd3b10a52badf24666d79c0d887a110f528e3b94b498a5e21273c702e4c8e80f024e0b6d1d6723c8cdb91ab9cb200a4c56ad35b487580ffcf
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. Please test your rules extensively and report any behaviour that is not consistent with your expectations.
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
- getname = "#{name}?".to_sym
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 getname do
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
 
@@ -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
- return if @cache.has_key?(wme)
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, is_real = false
224
+ def real_retract wme, options
222
225
 
223
226
  if wme.is_a? Array
224
- return real_retract( WME.new(*wme), is_real )
227
+ return real_retract( WME.new(*wme), options )
225
228
  end
226
229
 
227
- if ! is_real
228
- if @current_context
229
- @current_context.retracted_wmes << wme
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
- real = if is_real
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
 
@@ -1,5 +1,5 @@
1
1
  module Wongi
2
2
  module Engine
3
- VERSION = "0.1.0.alpha1"
3
+ VERSION = "0.1.0"
4
4
  end
5
5
  end
@@ -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
- !manual?
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.alpha1
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-02 00:00:00.000000000 Z
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.0
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.0
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: 1.3.1
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