wongi-engine 0.4.0.pre.alpha5 → 0.4.0.pre.alpha7

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
  SHA256:
3
- metadata.gz: 279bf4d4b1203e706faa3671e0e74ea20ee52d6236280fa8a7b878abe4df1aee
4
- data.tar.gz: dc3a03595e87e552564c25aa47710cdb036c72a55dde73c00d913544398d6300
3
+ metadata.gz: 600fe3fc20d2cbecf1f59d7ad771eb387f97c9f00144089961dacb247c817475
4
+ data.tar.gz: 94d3cd24972fd5b6b67a1940804dc30d91c960e05ffe143889b5e0bb99e1fb84
5
5
  SHA512:
6
- metadata.gz: 561a05b7b122d4c3e7ac3c0b4cccc80f3cb4a1e74620f69f32d2f17e66054f0ed83dbc196996d3e5cbf9b394740643dd3f5501d512b1014e638b34ab1385b06d
7
- data.tar.gz: 3fd398dbda7b5c0b41dc100ed2c4dbe19aaad939561e3c1dbf30bae8d83ecb76ea3d3720005bfe75f5cb80c4f0a430869894d59c12e735653814367b4235b58c
6
+ metadata.gz: f233a3b5cd1a10ffab78742c5f14f319c24ff64e2d2683cf4e41e304287c832e818802e4224316e460909975b93658c0e13ecef5888101841002c054ea10689a
7
+ data.tar.gz: 11b1c635fa38aa4427dac9eba6807ad8294613a814ac98f588c0eee0eb7febec0542192e7113ea6b02bccffb25063e698e609a6fd1687a48cefbb5fb3a1b4d2f
data/.rubocop.yml CHANGED
@@ -75,3 +75,12 @@ Style/OptionalBooleanParameter:
75
75
 
76
76
  Gemspec/RequiredRubyVersion:
77
77
  Enabled: false
78
+
79
+ RSpec/MultipleExpectations:
80
+ Enabled: false
81
+
82
+ RSpec/ContextWording:
83
+ Enabled: false
84
+
85
+ RSpec/ExampleLength:
86
+ Enabled: false
@@ -16,10 +16,9 @@ module Wongi::Engine
16
16
  def remove(wme)
17
17
  collection = collection_for_wme(wme)
18
18
  collection.delete(wme)
19
- if collection.empty?
20
- # release some memory
21
- index.delete(hashed_key(wme))
22
- end
19
+
20
+ # release some memory
21
+ index.delete(hashed_key(wme)) if collection.empty?
23
22
  end
24
23
 
25
24
  def collection_for_wme(wme)
@@ -12,15 +12,9 @@ module Wongi::Engine
12
12
  end
13
13
 
14
14
  def make_partition_fn(partition)
15
- return nil if partition.nil?
15
+ return nil if partition.empty?
16
16
 
17
- if Template.variable?(partition)
18
- ->(token) { token[partition] }
19
- elsif partition.is_a?(Array) && partition.all? { Template.variable?(_1) }
20
- ->(token) { token.values_at(*partition) }
21
- else
22
- partition
23
- end
17
+ ->(token) { token.values_at(*partition) }
24
18
  end
25
19
 
26
20
  def make_aggregate_fn(agg)
@@ -53,12 +47,16 @@ module Wongi::Engine
53
47
  end
54
48
 
55
49
  def evaluate(child: nil)
56
- groups = if partition
57
- tokens.group_by(&partition).values
58
- else
59
- # just a single group of everything
60
- [tokens]
61
- end
50
+ return if tokens.empty?
51
+
52
+ groups =
53
+ if partition
54
+ tokens.group_by(&partition).values
55
+ else
56
+ # just a single group of everything
57
+ [tokens]
58
+ end
59
+
62
60
 
63
61
  groups.each do |tokens|
64
62
  aggregated = self.aggregate.call(tokens.map(&self.map))
@@ -67,9 +65,9 @@ module Wongi::Engine
67
65
  tokens.each do |token|
68
66
  # TODO: optimize this to work with a diff of actual changes
69
67
  beta_deactivate_children(token: token, children: children)
70
- children.each do |beta|
71
- beta.beta_activate(Token.new(beta, token, nil, assignment))
72
- end
68
+ end
69
+ children.each do |beta|
70
+ beta.beta_activate(Token.new(beta, tokens, nil, assignment))
73
71
  end
74
72
  end
75
73
  end
@@ -20,7 +20,7 @@ module Wongi::Engine
20
20
  overlay.remove_token(token)
21
21
  children.each do |child|
22
22
  child.tokens.each do |t|
23
- if t.parent == token
23
+ if t.child_of?(token)
24
24
  child.beta_deactivate t
25
25
  # token.destroy
26
26
  end
@@ -51,7 +51,7 @@ module Wongi::Engine
51
51
 
52
52
  def beta_deactivate_children(token: nil, wme: nil, children: self.children)
53
53
  children.each do |child|
54
- child.tokens.select { (token.nil? || _1.parent == token) && (wme.nil? || _1.wme == wme) }.each do |child_token|
54
+ child.tokens.select { (token.nil? || _1.child_of?(token)) && (wme.nil? || _1.wme == wme) }.each do |child_token|
55
55
  child.beta_deactivate(child_token)
56
56
  end
57
57
  end
@@ -36,7 +36,7 @@ module Wongi
36
36
  def ncc_deactivate(token)
37
37
  # p ncc_deactivate: {class: self.class, object_id:, token:}
38
38
  children.each do |beta|
39
- beta.tokens.select { |t| t.parent == token }.each do |t|
39
+ beta.tokens.select { |t| t.child_of?(token) }.each do |t|
40
40
  beta.beta_deactivate t
41
41
  end
42
42
  end
@@ -23,7 +23,7 @@ module Wongi
23
23
  if optional
24
24
  # we're going to change the optional state so the old ones need to be removed
25
25
  child.tokens.each do |ct|
26
- child.beta_deactivate(ct) if ct.parent == token
26
+ child.beta_deactivate(ct) if ct.child_of?(token)
27
27
  end
28
28
  end
29
29
  child.beta_activate Token.new(child, token, wme, assignments)
@@ -43,7 +43,7 @@ module Wongi
43
43
 
44
44
  children.each do |child|
45
45
  child.tokens.each do |ct|
46
- child.beta_deactivate(ct) if ct.parent == token
46
+ child.beta_deactivate(ct) if ct.child_of?(token)
47
47
  end
48
48
  child.beta_activate Token.new(child, token, nil, {})
49
49
  end
@@ -32,7 +32,7 @@ module Wongi::Engine
32
32
  overlay.remove_token(token)
33
33
 
34
34
  children.each do |child|
35
- child.tokens.select { _1.parent == token }.each { child.beta_deactivate(_1) }
35
+ child.tokens.select { _1.child_of?(token) }.each { child.beta_deactivate(_1) }
36
36
  end
37
37
 
38
38
  nil
@@ -42,14 +42,12 @@ module Wongi::Engine
42
42
  # asserting this WME would invalidate the match
43
43
  return false if token.node.is_a?(NegNode) && token.node.matches?(token, wme)
44
44
 
45
- if token.parent && !considered_tokens.include?(token.parent)
46
- tokens_to_consider.push(token.parent)
47
- end
45
+ (token.parents - considered_tokens).each { |parent| tokens_to_consider.push(parent) }
46
+
47
+ next unless token.wme
48
48
 
49
- if token.wme
50
- overlay.generators(token.wme).each do |generator|
51
- tokens_to_consider.push(generator.token) unless considered_tokens.include?(generator.token)
52
- end
49
+ overlay.generators(token.wme).each do |generator|
50
+ tokens_to_consider.push(generator.token) unless considered_tokens.include?(generator.token)
53
51
  end
54
52
  end
55
53
 
@@ -6,9 +6,10 @@ module Wongi::Engine
6
6
  def initialize(var, options = {})
7
7
  @var = var
8
8
  @over = options[:over]
9
- @partition = options[:partition]
9
+ @partition = Array(options[:partition])
10
10
  @aggregate = options[:using]
11
11
  @map = options[:map]
12
+ raise "can only partition by variables" unless partition.all? { |p| Template.variable?(p) }
12
13
  end
13
14
 
14
15
  def compile(context)
@@ -254,7 +254,7 @@ module Wongi::Engine
254
254
  end
255
255
  end
256
256
 
257
- def select(s, p, o)
257
+ def select(s, p, o, &block)
258
258
  matching = current_overlay.select(s, p, o)
259
259
  if block_given?
260
260
  matching.each(&block)
@@ -183,14 +183,16 @@ module Wongi::Engine
183
183
  case operation
184
184
  when :assert
185
185
  wme = find_ignoring_hidden(wme) || wme
186
+ visible = !find(wme).nil?
186
187
  add_wme(wme, **options)
187
- rete.real_assert(wme)
188
+ rete.real_assert(wme) unless visible
188
189
  when :retract
189
190
  wme = find_ignoring_hidden(wme)
190
191
  return if wme.nil? # it's perhaps better to return quietly, because complicated cascades may delete a WME while we're going through the queue
191
192
 
193
+ visible = !find(wme).nil?
192
194
  remove_wme(wme, **options)
193
- rete.real_retract(wme)
195
+ rete.real_retract(wme) if visible
194
196
  end
195
197
  end
196
198
  end
@@ -1,26 +1,25 @@
1
+ require 'set'
2
+
1
3
  module Wongi::Engine
2
4
  class Token
3
- attr_reader :children, :wme, :node, :generated_wmes
4
- attr_accessor :parent
5
+ attr_reader :wme, :node, :generated_wmes, :parents
5
6
 
6
- def initialize(node, token, wme, assignments = {})
7
+ def initialize(node, parents, wme, assignments = {})
7
8
  @node = node
8
- @parent = token
9
+ @parents = Set.new(Array(parents))
9
10
  @wme = wme
10
11
  @assignments = assignments
11
- @children = []
12
12
  @deleted = false
13
13
  @ncc_results = []
14
14
  @generated_wmes = []
15
- token.children << self if token
16
15
  end
17
16
 
18
17
  def ancestors
19
- if parent
20
- parent.ancestors.unshift parent
21
- else
22
- []
23
- end
18
+ parents.flat_map(&:ancestors).uniq + parents.to_a
19
+ end
20
+
21
+ def child_of?(token)
22
+ parents.include?(token)
24
23
  end
25
24
 
26
25
  def subst(variable, value)
@@ -50,7 +49,10 @@ module Wongi::Engine
50
49
 
51
50
  # TODO: ignore assignments?
52
51
  def duplicate?(other)
53
- parent.equal?(other.parent) && @wme.equal?(other.wme) && assignments == other.assignments
52
+ instance_of?(other.class) &&
53
+ parents == other.parents &&
54
+ wme == other.wme &&
55
+ assignments == other.assignments
54
56
  end
55
57
 
56
58
  def to_s
@@ -75,22 +77,18 @@ module Wongi::Engine
75
77
  end
76
78
 
77
79
  # for neg feedback loop protection
78
- def generated?(wme)
79
- return true if generated_wmes.any? { |w| w == wme }
80
-
81
- children.any? { |t| t.generated? wme }
82
- end
80
+ # def generated?(wme)
81
+ # return true if generated_wmes.any? { |w| w == wme }
82
+ #
83
+ # children.any? { |t| t.generated? wme }
84
+ # end
83
85
 
84
86
  protected
85
87
 
86
88
  def all_assignments
87
- raise "Assignments is not a hash" unless @assignments.is_a?(Hash)
88
-
89
- if @parent
90
- @parent.assignments.merge @assignments
91
- else
92
- @assignments
93
- end
89
+ parents.each_with_object({}) do |parent, acc|
90
+ acc.merge!(parent.assignments)
91
+ end.merge(@assignments)
94
92
  end
95
93
  end
96
94
 
@@ -99,7 +97,7 @@ module Wongi::Engine
99
97
  @parent = token
100
98
  @wme = wme
101
99
  @assignments = assignments
102
- @children = []
100
+ # @children = []
103
101
  @neg_join_results = []
104
102
  @opt_join_results = []
105
103
  @ncc_results = []
@@ -1,5 +1,5 @@
1
1
  module Wongi
2
2
  module Engine
3
- VERSION = "0.4.0-alpha5".freeze
3
+ VERSION = "0.4.0-alpha7".freeze
4
4
  end
5
5
  end
@@ -10,10 +10,6 @@ module Wongi::Engine
10
10
  self.class.new(subject, predicate, object)
11
11
  end
12
12
 
13
- def ==(other)
14
- other && subject == other.subject && predicate == other.predicate && object == other.object
15
- end
16
-
17
13
  # @param template Wongi::Engine::Template
18
14
  def =~(template)
19
15
  raise Wongi::Engine::Error, "Cannot match a WME against a #{template.class}" unless template.is_a?(Template)
@@ -30,14 +26,6 @@ module Wongi::Engine
30
26
  inspect
31
27
  end
32
28
 
33
- def hash
34
- @hash ||= [subject.hash, predicate.hash, object.hash].hash
35
- end
36
-
37
- def eql?(other)
38
- subject.eql?(other.subject) && predicate.eql?(other.predicate) && object.eql?(other.object)
39
- end
40
-
41
29
  protected
42
30
 
43
31
  def match_member(mine, theirs)
@@ -20,24 +20,21 @@ describe 'aggregate' do
20
20
 
21
21
  engine << [:apple, :weight, 5]
22
22
  expect(production.size).to be == 1
23
- production.tokens.each do |token|
24
- expect(token[:X]).to be == 5
25
- expect(token[:Fruit]).to be == :apple
26
- end
23
+ token = production.tokens.first
24
+ expect(token[:X]).to be == 5
25
+ expect(token[:Fruit]).to be == :apple
27
26
 
28
27
  engine << [:pea, :weight, 2]
29
- expect(production.size).to be == 2
30
- production.tokens.each do |token|
31
- expect(token[:X]).to be == 2
32
- expect(token[:Fruit]).to be == :pea
33
- end
28
+ expect(production.size).to be == 1
29
+ token = production.tokens.first
30
+ expect(token[:X]).to be == 2
31
+ expect(token[:Fruit]).to be == :pea
34
32
 
35
33
  engine.retract [:pea, :weight, 2]
36
34
  expect(production.size).to be == 1
37
- production.tokens.each do |token|
38
- expect(token[:X]).to be == 5
39
- expect(token[:Fruit]).to be == :apple
40
- end
35
+ token = production.tokens.first
36
+ expect(token[:X]).to be == 5
37
+ expect(token[:Fruit]).to be == :apple
41
38
  end
42
39
  end
43
40
 
@@ -53,24 +50,21 @@ describe 'aggregate' do
53
50
 
54
51
  engine << [:pea, :weight, 2]
55
52
  expect(production.size).to be == 1
56
- production.tokens.each do |token|
57
- expect(token[:X]).to be == 2
58
- expect(token[:Fruit]).to be == :pea
59
- end
53
+ token = production.tokens.first
54
+ expect(token[:X]).to be == 2
55
+ expect(token[:Fruit]).to be == :pea
60
56
 
61
57
  engine << [:apple, :weight, 5]
62
- expect(production.size).to be == 2
63
- production.tokens.each do |token|
64
- expect(token[:X]).to be == 5
65
- expect(token[:Fruit]).to be == :apple
66
- end
58
+ expect(production.size).to be == 1
59
+ token = production.tokens.first
60
+ expect(token[:X]).to be == 5
61
+ expect(token[:Fruit]).to be == :apple
67
62
 
68
63
  engine.retract [:apple, :weight, 5]
69
64
  expect(production.size).to be == 1
70
- production.tokens.each do |token|
71
- expect(token[:X]).to be == 2
72
- expect(token[:Fruit]).to be == :pea
73
- end
65
+ token = production.tokens.first
66
+ expect(token[:X]).to be == 2
67
+ expect(token[:Fruit]).to be == :pea
74
68
  end
75
69
  end
76
70
 
@@ -85,27 +79,23 @@ describe 'aggregate' do
85
79
 
86
80
  engine << [:pea, :weight, 1]
87
81
  expect(production.size).to be == 1
88
- production.tokens.each do |token|
89
- expect(token[:Count]).to be == 1
90
- end
82
+ token = production.tokens.first
83
+ expect(token[:Count]).to be == 1
91
84
 
92
85
  engine << [:apple, :weight, 5]
93
- expect(production.size).to be == 2
94
- production.tokens.each do |token|
95
- expect(token[:Count]).to be == 2
96
- end
86
+ expect(production.size).to be == 1
87
+ token = production.tokens.first
88
+ expect(token[:Count]).to be == 2
97
89
 
98
90
  engine << [:watermelon, :weight, 15]
99
- expect(production.size).to be == 3
100
- production.tokens.each do |token|
101
- expect(token[:Count]).to be == 3
102
- end
91
+ expect(production.size).to be == 1
92
+ token = production.tokens.first
93
+ expect(token[:Count]).to be == 3
103
94
 
104
95
  engine.retract [:apple, :weight, 5]
105
- expect(production.size).to be == 2
106
- production.tokens.each do |token|
107
- expect(token[:Count]).to be == 2
108
- end
96
+ expect(production.size).to be == 1
97
+ token = production.tokens.first
98
+ expect(token[:Count]).to be == 2
109
99
  end
110
100
 
111
101
  it 'works with a post-filter' do
@@ -124,10 +114,9 @@ describe 'aggregate' do
124
114
  expect(production.size).to be == 0
125
115
 
126
116
  engine << [:watermelon, :weight, 15]
127
- expect(production.size).to be == 3
128
- production.tokens.each do |token|
129
- expect(token[:Count]).to be == 3
130
- end
117
+ expect(production.size).to be == 1
118
+ token = production.tokens.first
119
+ expect(token[:Count]).to be == 3
131
120
 
132
121
  engine.retract [:apple, :weight, 5]
133
122
  expect(production.size).to be == 0
@@ -148,7 +137,7 @@ describe 'aggregate' do
148
137
  engine << [:factor, 12, 3]
149
138
  engine << [:factor, 12, 4]
150
139
 
151
- expect(production).to have(4).tokens
140
+ expect(production).to have(2).tokens
152
141
  production.tokens.each do |token|
153
142
  expect(token[:Product]).to be_a(Integer)
154
143
  expect(token[:Product]).to eq(token[:Number])
@@ -170,11 +159,55 @@ describe 'aggregate' do
170
159
  engine << [:factor, 12, 3]
171
160
  engine << [:factor, 12, 4]
172
161
 
173
- expect(production).to have(4).tokens
162
+ expect(production).to have(2).tokens
174
163
  production.tokens.each do |token|
175
164
  expect(token[:Product]).to be_a(Integer)
176
165
  expect(token[:Product]).to eq(token[:Number])
177
166
  end
178
167
  end
179
168
  end
169
+
170
+ it 'propagates a single token' do
171
+ engine << rule {
172
+ forall {
173
+ has :Item, :price_group, :Group
174
+ has :Group, :name, :Name
175
+ has :Group, :base_price, :Price
176
+ max :MaxPrice, over: :Price, partition: %i[Item Name]
177
+ # equal :Price, :MaxPrice # -- this is necessary for this to work without token converging
178
+ }
179
+ make {
180
+ gen :Group, :price, :MaxPrice
181
+ }
182
+ }
183
+
184
+ engine << rule("sum") {
185
+ forall {
186
+ has :Item, :price_group, :Group
187
+ has :Group, :price, :Price
188
+ sum :TotalPrice, over: :Price, partition: :Item
189
+ }
190
+ make {
191
+ gen :Item, :price, :TotalPrice
192
+ }
193
+ }
194
+
195
+ material1 = Object.new
196
+ engine << [:toy, :price_group, material1]
197
+ engine << [material1, :name, :material]
198
+ engine << [material1, :base_price, 100]
199
+
200
+ material2 = Object.new
201
+ engine << [:toy, :price_group, material2]
202
+ engine << [material2, :name, :material]
203
+ engine << [material2, :base_price, 200]
204
+
205
+ packaging = Object.new
206
+ engine << [:toy, :price_group, packaging]
207
+ engine << [packaging, :name, :packaging]
208
+ engine << [packaging, :base_price, 20]
209
+
210
+ total_price = engine.select(:toy, :price, :_).first
211
+ expect(total_price.object).to eq(220)
212
+ end
180
213
  end
data/spec/dataset_spec.rb CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Wongi::Engine::Network do
4
4
  include Wongi::Engine::DSL
5
5
  it 'exposes compiled productions' do
6
- ds = Wongi::Engine::Network.new
6
+ ds = Wongi::Engine.create
7
7
 
8
8
  ds << rule('test-rule') {
9
9
  forall {
@@ -2,29 +2,23 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe 'Greater Than Or Equal test' do
5
+ describe Wongi::Engine::GreaterThanOrEqualTest do
6
6
  include Wongi::Engine::DSL
7
7
  let(:engine) { Wongi::Engine.create }
8
8
 
9
- attr_reader :production
10
-
11
- def test_rule(&block)
12
- @production = (engine << rule('test-rule', &block))
13
- end
14
-
15
9
  it 'interacts with optional node correctly' do
16
10
  # before the fix, filters would try to piggy-back on optional templates
17
11
 
18
- test_rule do
19
- forall do
12
+ production = engine << rule {
13
+ forall {
20
14
  has :Number, :assign_check, :_
21
15
  gte :Number, 6
22
- end
23
- end
16
+ }
17
+ }
24
18
 
25
19
  engine << [6, :assign_check, nil]
26
20
  engine << [7, :assign_check, nil]
27
21
  engine << [5, :assign_check, nil]
28
- expect(@production.size).to eq(2)
22
+ expect(production.size).to eq(2)
29
23
  end
30
24
  end
@@ -10,7 +10,7 @@ class << self
10
10
  }
11
11
  end
12
12
 
13
- describe 'the engine' do
13
+ describe Wongi::Engine::Network do
14
14
  include Wongi::Engine::DSL
15
15
  let(:engine) { Wongi::Engine.create }
16
16
 
@@ -1,19 +1,13 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "LESS test" do
3
+ describe Wongi::Engine::LessThanTest do
4
4
  include Wongi::Engine::DSL
5
5
  let(:engine) { Wongi::Engine.create }
6
6
 
7
- attr_reader :production
8
-
9
- def test_rule(&block)
10
- @production = (engine << rule('test-rule', &block))
11
- end
12
-
13
7
  it "interacts with optional node correctly" do
14
8
  # before the fix, filters would try to piggy-back on optional templates
15
9
 
16
- test_rule {
10
+ production = engine << rule {
17
11
  forall {
18
12
  maybe "Z", "Z", "Z"
19
13
  less 6, 4 # this should fail
@@ -26,6 +20,6 @@ describe "LESS test" do
26
20
 
27
21
  engine << %w[A B C]
28
22
 
29
- expect(@production.size).to eq(0)
23
+ expect(production.size).to eq(0)
30
24
  end
31
25
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe 'Less Than Or Equal test' do
5
+ describe Wongi::Engine::LessThanOrEqualTest do
6
6
  include Wongi::Engine::DSL
7
7
  let(:engine) { Wongi::Engine.create }
8
8
 
data/spec/network_spec.rb CHANGED
@@ -3,62 +3,60 @@ require 'spec_helper'
3
3
  describe Wongi::Engine::Network do
4
4
  include Wongi::Engine::DSL
5
5
 
6
- subject { engine }
7
-
8
6
  let(:engine) { Wongi::Engine.create }
9
7
 
10
8
  it 'asserts facts' do
11
- subject << [1, 2, 3]
12
- expect(subject.select(:_, 2, :_)).to have(1).item
9
+ engine << [1, 2, 3]
10
+ expect(engine.select(:_, 2, :_)).to have(1).item
13
11
  end
14
12
 
15
13
  it 'retracts facts' do
16
- subject << [1, 2, 3]
17
- subject.retract [1, 2, 3]
18
- expect(subject.select(:_, 2, :_).count).to eq(0)
14
+ engine << [1, 2, 3]
15
+ engine.retract [1, 2, 3]
16
+ expect(engine.select(:_, 2, :_).count).to eq(0)
19
17
  end
20
18
 
21
19
  it 'asserted facts end up in productions' do
22
- prod = subject << rule { forall { has :X, 2, :Z } }
23
- subject << [1, 2, 3]
20
+ prod = engine << rule { forall { has :X, 2, :Z } }
21
+ engine << [1, 2, 3]
24
22
  expect(prod).to have(1).tokens
25
23
  end
26
24
 
27
25
  it 'rules can be removed from engine' do
28
- subject << [1, 2, 3]
29
- subject << [4, 5, 6]
26
+ engine << [1, 2, 3]
27
+ engine << [4, 5, 6]
30
28
 
31
- prod1 = subject << rule { forall { has :X, 2, :Z } }
32
- prod2 = subject << rule { forall { has :X, 5, :Z } }
29
+ prod1 = engine << rule { forall { has :X, 2, :Z } }
30
+ prod2 = engine << rule { forall { has :X, 5, :Z } }
33
31
 
34
32
  expect(prod1).to have(1).tokens
35
33
  expect(prod2).to have(1).tokens
36
34
 
37
- subject.remove_production(prod1)
35
+ engine.remove_production(prod1)
38
36
 
39
37
  expect(prod1).to have(0).tokens
40
38
  expect(prod2).to have(1).tokens
41
39
  end
42
40
 
43
41
  it 'new rules can be added to engine after a rule has been been removed' do
44
- subject << [1, 2, 3]
45
- subject << [4, 5, 6]
42
+ engine << [1, 2, 3]
43
+ engine << [4, 5, 6]
46
44
 
47
- prod1 = subject << rule { forall { has :X, 2, :Z } }
45
+ prod1 = engine << rule { forall { has :X, 2, :Z } }
48
46
 
49
47
  expect(prod1).to have(1).tokens
50
48
 
51
- subject.remove_production(prod1)
49
+ engine.remove_production(prod1)
52
50
  expect(prod1).to have(0).tokens
53
51
 
54
- prod2 = subject << rule { forall { has :X, 5, :Z } }
52
+ prod2 = engine << rule { forall { has :X, 5, :Z } }
55
53
  expect(prod2).to have(1).tokens
56
54
  end
57
55
 
58
56
  it 'retracted facts are removed from productions' do
59
- prod = subject << rule { forall { has :X, 2, :Z } }
60
- subject << [1, 2, 3]
61
- subject.retract [1, 2, 3]
57
+ prod = engine << rule { forall { has :X, 2, :Z } }
58
+ engine << [1, 2, 3]
59
+ engine.retract [1, 2, 3]
62
60
  expect(prod).to have(0).tokens
63
61
  end
64
62
 
@@ -66,17 +64,17 @@ describe Wongi::Engine::Network do
66
64
  activated_z = nil
67
65
  deactivated_z = nil
68
66
 
69
- subject << rule {
67
+ engine << rule {
70
68
  forall { has :X, 2, :Z }
71
69
  make {
72
70
  action activate: ->(token) { activated_z = token[:Z] },
73
71
  deactivate: ->(token) { deactivated_z = token[:Z] }
74
72
  }
75
73
  }
76
- subject << [1, 2, 3]
74
+ engine << [1, 2, 3]
77
75
  expect(activated_z).to be == 3
78
76
 
79
- subject.retract [1, 2, 3]
77
+ engine.retract [1, 2, 3]
80
78
  expect(deactivated_z).to be == 3
81
79
  end
82
80
 
data/spec/ruleset_spec.rb CHANGED
@@ -2,44 +2,44 @@ require 'spec_helper'
2
2
 
3
3
  describe Wongi::Engine::Ruleset do
4
4
  before do
5
- Wongi::Engine::Ruleset.reset
5
+ described_class.reset
6
6
  end
7
7
 
8
8
  context 'initially' do
9
9
  it 'has no rules' do
10
- expect(Wongi::Engine::Ruleset.rulesets).to be_empty
10
+ expect(described_class.rulesets).to be_empty
11
11
  end
12
12
  end
13
13
 
14
14
  context 'when creating' do
15
15
  it 'does not register itself when not given a name' do
16
- ruleset = Wongi::Engine::Ruleset.new
16
+ ruleset = described_class.new
17
17
  expect(ruleset.name).to be_nil
18
- expect(Wongi::Engine::Ruleset.rulesets).to be_empty
18
+ expect(described_class.rulesets).to be_empty
19
19
  end
20
20
 
21
21
  it 'has a name' do
22
- ruleset = Wongi::Engine::Ruleset.new 'testing-ruleset'
22
+ ruleset = described_class.new 'testing-ruleset'
23
23
  expect(ruleset.name).to be == 'testing-ruleset'
24
24
  end
25
25
 
26
26
  it 'registers itself when given a name' do
27
- ruleset = Wongi::Engine::Ruleset.new 'testing-ruleset'
28
- expect(Wongi::Engine::Ruleset.rulesets).not_to be_empty
29
- expect(Wongi::Engine::Ruleset[ruleset.name]).to be == ruleset
27
+ ruleset = described_class.new 'testing-ruleset'
28
+ expect(described_class.rulesets).not_to be_empty
29
+ expect(described_class[ruleset.name]).to be == ruleset
30
30
  end
31
31
  end
32
32
 
33
33
  it 'is able to clear registered rulesets' do
34
- _ = Wongi::Engine::Ruleset.new 'testing-ruleset'
35
- Wongi::Engine::Ruleset.reset
36
- expect(Wongi::Engine::Ruleset.rulesets).to be_empty
34
+ _ = described_class.new 'testing-ruleset'
35
+ described_class.reset
36
+ expect(described_class.rulesets).to be_empty
37
37
  end
38
38
 
39
39
  it 'installs creating rules into a rete' do
40
40
  rete = double 'rete'
41
41
 
42
- ruleset = Wongi::Engine::Ruleset.new
42
+ ruleset = described_class.new
43
43
  rule = ruleset.rule('test-rule') {}
44
44
 
45
45
  expect(rete).to receive(:<<).with(rule).once
data/spec/wme_spec.rb CHANGED
@@ -9,36 +9,36 @@ describe Wongi::Engine::WME do
9
9
  # rete
10
10
  # end
11
11
 
12
- subject {
13
- Wongi::Engine::WME.new "a", "b", "c"
12
+ let(:wme) {
13
+ described_class.new "a", "b", "c"
14
14
  }
15
15
 
16
16
  context 'a new WME' do
17
17
  it 'initializes and expose members' do
18
- expect(subject.subject).to be == "a"
19
- expect(subject.predicate).to be == "b"
20
- expect(subject.object).to be == "c"
18
+ expect(wme.subject).to be == "a"
19
+ expect(wme.predicate).to be == "b"
20
+ expect(wme.object).to be == "c"
21
21
  end
22
22
  end
23
23
 
24
24
  it 'compares instances' do
25
- wme1 = Wongi::Engine::WME.new "a", "b", "c"
26
- wme2 = Wongi::Engine::WME.new "a", "b", "c"
27
- wme3 = Wongi::Engine::WME.new "a", "b", "d"
25
+ wme1 = described_class.new "a", "b", "c"
26
+ wme2 = described_class.new "a", "b", "c"
27
+ wme3 = described_class.new "a", "b", "d"
28
28
 
29
29
  expect(wme1).to be == wme2
30
30
  expect(wme1).not_to be == wme3
31
31
  end
32
32
 
33
33
  it 'does not match against non-templates' do
34
- expect { subject =~ [1, 2, 3] }.to raise_error(Wongi::Engine::Error)
34
+ expect { wme =~ [1, 2, 3] }.to raise_error(Wongi::Engine::Error)
35
35
  end
36
36
 
37
37
  it 'matches against templates' do
38
38
  t1 = Wongi::Engine::Template.new "a", :_, :_
39
39
  t2 = Wongi::Engine::Template.new "b", :_, :_
40
40
 
41
- expect(subject).to be =~ t1
42
- expect(subject).not_to be =~ t2
41
+ expect(wme).to be =~ t1
42
+ expect(wme).not_to be =~ t2
43
43
  end
44
44
  end
data/wongi-engine.gemspec CHANGED
@@ -5,26 +5,29 @@ module GemHelper
5
5
  def self.git?
6
6
  File.exist?('.git')
7
7
  end
8
-
9
- def self.hg?
10
- File.exist?('.hg')
11
- end
12
8
  end
13
9
 
14
10
  Gem::Specification.new do |gem|
15
- gem.authors = ['Valeri Sokolov']
16
- gem.email = ['ulfurinn@ulfurinn.net']
17
- gem.description = 'A rule engine.'
18
- gem.summary = 'A forward-chaining rule engine in pure Ruby.'
19
- gem.homepage = 'https://github.com/ulfurinn/wongi-engine'
20
- gem.licenses = %w[MIT]
11
+ gem.authors = ['Valeri Sokolov']
12
+ gem.email = ['ulfurinn@ulfurinn.net']
13
+
14
+ gem.description = 'A forward-chaining rule engine in pure Ruby.'
15
+ gem.summary = 'A forward-chaining rule engine in pure Ruby.'
16
+
17
+ gem.required_ruby_version = '>= 2.7.0'
18
+
19
+ gem.licenses = %w[MIT]
20
+
21
+ gem.homepage = 'https://github.com/ulfurinn/wongi-engine'
22
+ gem.metadata = {
23
+ "documentation_uri" => 'https://ulfurinn.github.io/wongi-engine/',
24
+ "rubygems_mfa_required" => 'true',
25
+ }
21
26
 
22
27
  gem.files = if GemHelper.git?
23
28
  `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
24
- elsif GemHelper.hg?
25
- `hg st -cn`.split($OUTPUT_RECORD_SEPARATOR)
26
29
  else
27
- raise 'cannot enumerate files: not a git or hg repository'
30
+ raise 'cannot enumerate files: not a git repository'
28
31
  end
29
32
  gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
30
33
  gem.name = 'wongi-engine'
@@ -36,5 +39,4 @@ Gem::Specification.new do |gem|
36
39
  # gem.add_development_dependency 'pry-byebug', '~> 2.0'
37
40
  gem.add_development_dependency 'rspec', '~> 3.1'
38
41
  gem.add_development_dependency 'rspec-collection_matchers', '~> 1.1'
39
- gem.metadata['rubygems_mfa_required'] = 'true'
40
42
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wongi-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0.pre.alpha5
4
+ version: 0.4.0.pre.alpha7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Valeri Sokolov
@@ -66,7 +66,7 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.1'
69
- description: A rule engine.
69
+ description: A forward-chaining rule engine in pure Ruby.
70
70
  email:
71
71
  - ulfurinn@ulfurinn.net
72
72
  executables: []
@@ -180,6 +180,7 @@ homepage: https://github.com/ulfurinn/wongi-engine
180
180
  licenses:
181
181
  - MIT
182
182
  metadata:
183
+ documentation_uri: https://ulfurinn.github.io/wongi-engine/
183
184
  rubygems_mfa_required: 'true'
184
185
  post_install_message:
185
186
  rdoc_options: []
@@ -189,7 +190,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
189
190
  requirements:
190
191
  - - ">="
191
192
  - !ruby/object:Gem::Version
192
- version: '0'
193
+ version: 2.7.0
193
194
  required_rubygems_version: !ruby/object:Gem::Requirement
194
195
  requirements:
195
196
  - - ">"