mort666-wongi-engine 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.hgignore +6 -0
  4. data/.hgtags +13 -0
  5. data/.ruby-gemset +1 -0
  6. data/.travis.yml +19 -0
  7. data/CHANGELOG.md +106 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE +22 -0
  10. data/README.md +27 -0
  11. data/Rakefile +9 -0
  12. data/examples/ex01.rb +23 -0
  13. data/examples/ex02.rb +37 -0
  14. data/examples/graphviz.rb +16 -0
  15. data/examples/rdf.n3 +6 -0
  16. data/examples/rdf.rb +14 -0
  17. data/examples/timeline.rb +48 -0
  18. data/lib/wongi-engine.rb +36 -0
  19. data/lib/wongi-engine/alpha_memory.rb +60 -0
  20. data/lib/wongi-engine/beta.rb +11 -0
  21. data/lib/wongi-engine/beta/assignment_node.rb +40 -0
  22. data/lib/wongi-engine/beta/beta_memory.rb +49 -0
  23. data/lib/wongi-engine/beta/beta_node.rb +94 -0
  24. data/lib/wongi-engine/beta/filter_node.rb +48 -0
  25. data/lib/wongi-engine/beta/join_node.rb +140 -0
  26. data/lib/wongi-engine/beta/ncc_node.rb +67 -0
  27. data/lib/wongi-engine/beta/ncc_partner.rb +40 -0
  28. data/lib/wongi-engine/beta/neg_node.rb +115 -0
  29. data/lib/wongi-engine/beta/optional_node.rb +142 -0
  30. data/lib/wongi-engine/beta/or_node.rb +37 -0
  31. data/lib/wongi-engine/beta/production_node.rb +31 -0
  32. data/lib/wongi-engine/compiler.rb +115 -0
  33. data/lib/wongi-engine/core_ext.rb +63 -0
  34. data/lib/wongi-engine/data_overlay.rb +144 -0
  35. data/lib/wongi-engine/dsl.rb +132 -0
  36. data/lib/wongi-engine/dsl/action/base.rb +11 -0
  37. data/lib/wongi-engine/dsl/action/error_generator.rb +31 -0
  38. data/lib/wongi-engine/dsl/action/simple_action.rb +60 -0
  39. data/lib/wongi-engine/dsl/action/simple_collector.rb +52 -0
  40. data/lib/wongi-engine/dsl/action/statement_generator.rb +46 -0
  41. data/lib/wongi-engine/dsl/action/trace_action.rb +49 -0
  42. data/lib/wongi-engine/dsl/any_rule.rb +33 -0
  43. data/lib/wongi-engine/dsl/assuming.rb +31 -0
  44. data/lib/wongi-engine/dsl/builder.rb +44 -0
  45. data/lib/wongi-engine/dsl/clause/assign.rb +15 -0
  46. data/lib/wongi-engine/dsl/clause/fact.rb +71 -0
  47. data/lib/wongi-engine/dsl/clause/gen.rb +17 -0
  48. data/lib/wongi-engine/dsl/clause/generic.rb +38 -0
  49. data/lib/wongi-engine/dsl/generated.rb +43 -0
  50. data/lib/wongi-engine/dsl/ncc_subrule.rb +17 -0
  51. data/lib/wongi-engine/dsl/query.rb +24 -0
  52. data/lib/wongi-engine/dsl/rule.rb +84 -0
  53. data/lib/wongi-engine/enumerators.rb +21 -0
  54. data/lib/wongi-engine/error.rb +22 -0
  55. data/lib/wongi-engine/filter.rb +6 -0
  56. data/lib/wongi-engine/filter/asserting_test.rb +20 -0
  57. data/lib/wongi-engine/filter/equality_test.rb +36 -0
  58. data/lib/wongi-engine/filter/filter_test.rb +18 -0
  59. data/lib/wongi-engine/filter/greater_than_test.rb +36 -0
  60. data/lib/wongi-engine/filter/inequality_test.rb +36 -0
  61. data/lib/wongi-engine/filter/less_than_test.rb +36 -0
  62. data/lib/wongi-engine/graph.rb +73 -0
  63. data/lib/wongi-engine/network.rb +416 -0
  64. data/lib/wongi-engine/network/collectable.rb +42 -0
  65. data/lib/wongi-engine/network/debug.rb +85 -0
  66. data/lib/wongi-engine/ruleset.rb +74 -0
  67. data/lib/wongi-engine/template.rb +78 -0
  68. data/lib/wongi-engine/token.rb +114 -0
  69. data/lib/wongi-engine/version.rb +5 -0
  70. data/lib/wongi-engine/wme.rb +89 -0
  71. data/lib/wongi-engine/wme_match_data.rb +34 -0
  72. data/spec/beta_node_spec.rb +29 -0
  73. data/spec/bug_specs/issue_4_spec.rb +141 -0
  74. data/spec/dataset_spec.rb +27 -0
  75. data/spec/dsl_spec.rb +9 -0
  76. data/spec/filter_specs/assert_test_spec.rb +102 -0
  77. data/spec/filter_specs/less_test_spec.rb +41 -0
  78. data/spec/generation_spec.rb +116 -0
  79. data/spec/high_level_spec.rb +378 -0
  80. data/spec/network_spec.rb +182 -0
  81. data/spec/overlay_spec.rb +61 -0
  82. data/spec/rule_specs/any_rule_spec.rb +75 -0
  83. data/spec/rule_specs/assign_spec.rb +88 -0
  84. data/spec/rule_specs/assuming_spec.rb +66 -0
  85. data/spec/rule_specs/maybe_rule_spec.rb +101 -0
  86. data/spec/rule_specs/ncc_spec.rb +258 -0
  87. data/spec/rule_specs/negative_rule_spec.rb +105 -0
  88. data/spec/ruleset_spec.rb +54 -0
  89. data/spec/simple_action_spec.rb +40 -0
  90. data/spec/spec_helper.rb +3 -0
  91. data/spec/wme_spec.rb +83 -0
  92. data/wongi-engine.gemspec +40 -0
  93. metadata +212 -0
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe Wongi::Engine::DataOverlay do
4
+ include Wongi::Engine::DSL
5
+
6
+ let(:engine) { Wongi::Engine.create }
7
+
8
+ it 'should be disposable' do
9
+ production = engine << rule {
10
+ forall {
11
+ has 1, 2, :X
12
+ }
13
+ }
14
+ engine.with_overlay { |overlay|
15
+ overlay << [1,2,3]
16
+ expect(production).to have(1).token
17
+ }
18
+ expect(production).to have(0).tokens
19
+ end
20
+
21
+ it 'should generate into correct overlays' do
22
+ production = engine << rule {
23
+ forall {
24
+ has 1, 2, :X
25
+ }
26
+ make {
27
+ gen :X, 4, 5
28
+ }
29
+ }
30
+ engine.with_overlay { |overlay|
31
+ overlay << [1,2,3]
32
+ expect(production).to have(1).token
33
+ expect(engine.find(3, 4, 5)).not_to be_nil
34
+ }
35
+ expect(production).to have(0).tokens
36
+ expect(engine.find(3, 4, 5)).to be_nil
37
+ end
38
+
39
+ it 'works with assignments' do
40
+ production = engine << rule {
41
+ forall {
42
+ has 1, 2, :X
43
+ assign(:Something) { 6 }
44
+ }
45
+ make {
46
+ collect :Something, :stuff
47
+ gen :person, 'stuff', :Something
48
+ }
49
+ }
50
+
51
+ engine.with_overlay { |overlay|
52
+ overlay << [1,2,3]
53
+ expect(production).to have(1).token
54
+ expect(engine.find(:_, :_, :_)).not_to be_nil
55
+ }
56
+
57
+ expect(production).to have(0).tokens
58
+ expect(engine.find(:_, :_, :_)).to be_nil
59
+ end
60
+
61
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe "ANY rule" do
4
+
5
+ before :each do
6
+ @engine = Wongi::Engine.create
7
+ end
8
+
9
+ def engine
10
+ @engine
11
+ end
12
+
13
+ context "with just one option" do
14
+
15
+ it "should act like a positive matcher" do
16
+
17
+ engine << rule('one-option') {
18
+ forall {
19
+ any {
20
+ option {
21
+ has 1, 2, :X
22
+ has :X, 4, 5
23
+ }
24
+ }
25
+ }
26
+ }
27
+
28
+ production = engine.productions['one-option']
29
+
30
+ engine << [1, 2, 3]
31
+ engine << [3, 4, 5]
32
+
33
+ expect(production.size).to eq(1)
34
+
35
+ end
36
+
37
+ end
38
+
39
+ context "with several options" do
40
+
41
+ specify "all matching branches must pass" do
42
+
43
+ engine << rule('two-options') {
44
+ forall {
45
+ has 1, 2, :X
46
+ any {
47
+ option {
48
+ has :X, 4, 5
49
+ }
50
+ option {
51
+ has :X, "four", "five"
52
+ }
53
+ }
54
+ }
55
+ make {
56
+ collect :X, :threes
57
+ }
58
+ }
59
+
60
+ production = engine.productions['two-options']
61
+
62
+ engine << [1, 2, 3]
63
+ engine << [3, 4, 5]
64
+ engine << [1, 2, "three"]
65
+ engine << ["three", "four", "five"]
66
+
67
+ expect(production.size).to eq(2)
68
+ expect( engine.collection(:threes) ).to include(3)
69
+ expect( engine.collection(:threes) ).to include("three")
70
+
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+
3
+ describe "ASSIGN rule" do
4
+
5
+ let( :engine ) { Wongi::Engine.create }
6
+
7
+ it "should assign simple expressions" do
8
+
9
+ production = engine << rule {
10
+ forall {
11
+ assign :X do
12
+ 42
13
+ end
14
+ }
15
+ }
16
+ expect(production.size).to eq(1)
17
+ expect(production.tokens.first[:X]).to eq(42)
18
+
19
+ end
20
+
21
+ it "should be able to access previous assignments" do
22
+
23
+ production = engine << rule {
24
+ forall {
25
+ has 1, 2, :X
26
+ assign :Y do |token|
27
+ token[:X] * 2
28
+ end
29
+ }
30
+ }
31
+
32
+ engine << [1, 2, 5]
33
+ expect(production.tokens.first[:Y]).to eq(10)
34
+
35
+ end
36
+
37
+ it 'should be deactivatable' do
38
+
39
+ prod = engine << rule {
40
+ forall {
41
+ has 1, 2, :X
42
+ assign :Y do |token|
43
+ token[:X] * 2
44
+ end
45
+ }
46
+ }
47
+
48
+ engine << [1, 2, 5]
49
+ engine.retract [1, 2, 5]
50
+
51
+ expect( prod ).to have(0).tokens
52
+
53
+ end
54
+
55
+ it 'should be evaluated once' do
56
+ x = 0
57
+ prod = engine << rule {
58
+ forall {
59
+ has :a, :b, :c
60
+ assign :T do
61
+ x += 1
62
+ end
63
+ }
64
+ make {
65
+ gen :d, :e, :T
66
+ gen :f, :g, :T
67
+ }
68
+ }
69
+ engine << [:a, :b, :c]
70
+ expect(x).to be == 1
71
+ end
72
+
73
+ it 'should handle booleans' do
74
+ engine << rule do
75
+ for_all {
76
+ has :a, :b, :c
77
+ assign :X do |token|
78
+ false
79
+ end
80
+ }
81
+ make {
82
+ gen :d, :e, :X
83
+ }
84
+ end
85
+ engine << [:a, :b, :c]
86
+ end
87
+
88
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe Wongi::Engine::AssumingClause do
4
+
5
+ let( :engine ) { Wongi::Engine.create }
6
+
7
+ it 'should include base rules' do
8
+
9
+ engine << rule( :base ) {
10
+ forall {
11
+ has :x, :y, :Z
12
+ }
13
+ }
14
+
15
+ extended = engine << rule {
16
+ forall {
17
+ assuming :base
18
+ has :Z, :u, :W
19
+ }
20
+ }
21
+
22
+ engine << [:x, :y, 1]
23
+ engine << [:x, :y, 2]
24
+ engine << [1, :u, :a]
25
+ engine << [2, :u, :b]
26
+ result = Hash[ extended.tokens.map { |token| [ token[:Z], token[:W] ] } ]
27
+ expect( result ).to eq(1 => :a, 2 => :b)
28
+
29
+ end
30
+
31
+ it 'should check for base rule\'s existence' do
32
+
33
+ f = -> {
34
+ engine << rule {
35
+ forall {
36
+ assuming :base
37
+ }
38
+ }
39
+ }
40
+
41
+ expect( &f ).to raise_error Wongi::Engine::UndefinedBaseRule
42
+
43
+ end
44
+
45
+ it 'should come first in a rule' do
46
+
47
+ f = -> {
48
+ engine << rule( :base ) {
49
+ forall {
50
+ has :x, :y, :Z
51
+ }
52
+ }
53
+
54
+ engine << rule {
55
+ forall {
56
+ has :Z, :u, :W
57
+ assuming :base
58
+ }
59
+ }
60
+ }
61
+
62
+ expect( &f ).to raise_error Wongi::Engine::DefinitionError
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,101 @@
1
+ require "spec_helper"
2
+
3
+ describe "MAYBE rule" do
4
+
5
+ let( :engine ) { Wongi::Engine.create }
6
+ let( :maybe_rule ) {
7
+ rule {
8
+ forall {
9
+ has 1, 2, :X
10
+ maybe :X, 4, :Y
11
+ }
12
+ }
13
+ }
14
+
15
+ it "should pass with existing facts" do
16
+
17
+ production = engine << maybe_rule
18
+
19
+ engine << [1, 2, 3]
20
+ engine << [3, 4, 5]
21
+
22
+ expect(production.size).to eq(1)
23
+
24
+ expect(production.tokens.first[:X]).to eq(3)
25
+ expect(production.tokens.first[:Y]).to eq(5)
26
+
27
+ end
28
+
29
+ it "should pass with missing facts" do
30
+
31
+ production = engine << maybe_rule
32
+
33
+ engine << [1, 2, 3]
34
+
35
+ expect(production.size).to eq(1)
36
+
37
+ expect(production.tokens.first[:X]).to eq(3)
38
+ expect(production.tokens.first[:Y]).to be_nil
39
+
40
+ end
41
+
42
+ it "should pass with pre-added missing facts" do
43
+
44
+ engine << [1, 2, 3]
45
+
46
+ production = engine << maybe_rule
47
+
48
+ expect(production.size).to eq(1)
49
+
50
+ expect(production.tokens.first[:X]).to eq(3)
51
+ expect(production.tokens.first[:Y]).to be_nil
52
+
53
+ end
54
+
55
+ it 'should pass with retracted facts' do
56
+
57
+ prod = engine << maybe_rule
58
+
59
+ engine << [1, 2, 3]
60
+ engine << [3, 4, 5]
61
+ engine.retract [3, 4, 5]
62
+
63
+ expect(prod.size).to eq(1)
64
+
65
+ expect(prod.tokens.first[:X]).to eq(3)
66
+ expect(prod.tokens.first[:Y]).to be_nil
67
+
68
+ end
69
+
70
+ it 'should work with repeated activations' do
71
+
72
+ prod = engine << maybe_rule
73
+
74
+ engine << [1, 2, 3]
75
+ engine << [3, 4, 5]
76
+ engine.retract [3, 4, 5]
77
+
78
+ 10.times {
79
+ engine << [3, 4, 5]
80
+ expect(prod.size).to eq(1)
81
+ expect(prod.tokens.first[:Y]).to be == 5
82
+
83
+ engine.retract [3, 4, 5]
84
+ expect(prod.size).to eq(1)
85
+ expect(prod.tokens.first[:Y]).to be_nil
86
+ }
87
+
88
+ end
89
+
90
+ it 'should handle retracted parent tokens' do
91
+ prod = engine << maybe_rule
92
+
93
+ engine << [1, 2, 3]
94
+ engine << [3, 4, 5]
95
+ engine.retract [1, 2, 3]
96
+
97
+ expect(prod).to have(0).tokens
98
+ expect(engine.find(3,4,5).opt_join_results).to be_empty
99
+ end
100
+
101
+ end
@@ -0,0 +1,258 @@
1
+ require 'spec_helper'
2
+
3
+ describe Wongi::Engine::NccNode do
4
+
5
+ before :each do
6
+ @engine = Wongi::Engine.create
7
+ end
8
+
9
+ def engine
10
+ @engine
11
+ end
12
+
13
+ def ncc_rule
14
+ rule('ncc') {
15
+ forall {
16
+ has "base", "is", :Base
17
+ none {
18
+ has :Base, 2, :X
19
+ has :X, 4, 5
20
+ }
21
+ }
22
+ }
23
+ end
24
+
25
+ def ncc_rule_post_has
26
+ rule('ncc post has') {
27
+ forall {
28
+ has "base", "is", :Base
29
+ none {
30
+ has :Base, 2, :X
31
+ has :X, 4, 5
32
+ }
33
+ has "base", "is", :Base2
34
+ }
35
+ }
36
+ end
37
+
38
+ it 'should pass with a mismatching subchain' do
39
+
40
+ engine << ncc_rule
41
+ production = engine.productions['ncc']
42
+
43
+ engine << ["base", "is", 1]
44
+
45
+ expect(production).to have(1).token
46
+
47
+ engine << [1, 2, 3]
48
+
49
+ expect(production).to have(1).token
50
+
51
+ engine << [3, 4, 5]
52
+
53
+ expect(production).to have(0).token
54
+
55
+ end
56
+
57
+ it 'should remain consistent after retraction' do
58
+
59
+ engine << ncc_rule
60
+ production = engine.productions['ncc']
61
+
62
+ engine << ["base", "is", 1]
63
+ engine << [1, 2, 3]
64
+ engine << [3, 4, 5]
65
+
66
+ expect(production).to have(0).tokens
67
+
68
+ engine.retract [3, 4, 5]
69
+ expect( production ).to have(1).token
70
+
71
+ engine.retract ["base", "is", 1]
72
+ expect(production).to have(0).tokens
73
+
74
+ end
75
+
76
+ it 'can handle an alpha node template introduced after the negative-conjunctive-condition' do
77
+
78
+ engine << ncc_rule_post_has
79
+
80
+ production = engine.productions['ncc post has']
81
+
82
+ engine << ["base", "is", 1]
83
+ engine << [1, 2, 3]
84
+ engine << [3, 4, 5]
85
+
86
+ expect( production ).to have(0).tokens
87
+
88
+ engine.retract [3, 4, 5]
89
+ expect( production ).to have(1).tokens
90
+
91
+ engine.retract ["base", "is", 1]
92
+ expect( production ).to have(0).tokens
93
+
94
+ end
95
+
96
+ it 'should clean up correctly' do
97
+
98
+ engine.rule :rule1 do
99
+ forall {
100
+ has :light_kitchen, :value, :on
101
+ }
102
+ make {
103
+ #trace values: true, generation: true
104
+ gen self.name, :light_bathroom, :on
105
+ gen self.name, :want_action_for, :light_bathroom
106
+ }
107
+ end
108
+
109
+ prod = engine.rule "action" do
110
+ forall {
111
+ has :Requestor, :want_action_for, :Actor
112
+ has :Requestor, :Actor, :Value
113
+ has :Requestor, :priority, :Priority
114
+ ncc {
115
+ has :OtherRequestor, :want_action_for, :Actor
116
+ diff :OtherRequestor, :Requestor
117
+ has :OtherRequestor, :priority, :OtherPriority
118
+ greater :OtherPriority, :Priority
119
+ }
120
+ }
121
+ make {
122
+ #trace values: true, generation: true
123
+ gen :Actor, :value, :Value
124
+ gen :Actor, :last_user, :Requestor
125
+ }
126
+ end
127
+
128
+ engine << [:user, :priority, 1]
129
+ engine << [:rule1, :priority, 2]
130
+ engine << [:poweruser, :priority, 3]
131
+ engine << [:god, :priority, 4]
132
+
133
+ engine << [:user, :want_action_for, :light_bathroom]
134
+ engine << [:user, :light_bathroom, :off]
135
+ expect( engine.select(:light_bathroom, :value, :_) ).to be == [ Wongi::Engine::WME.new(:light_bathroom, :value, :off) ]
136
+ expect( engine.select(:light_bathroom, :last_user, :_) ).to be == [ Wongi::Engine::WME.new(:light_bathroom, :last_user, :user) ]
137
+
138
+ engine << [:light_kitchen, :value, :on]
139
+ expect( engine.select(:light_bathroom, :value, :_) ).to be == [ Wongi::Engine::WME.new(:light_bathroom, :value, :on) ]
140
+ expect( engine.select(:light_bathroom, :last_user, :_) ).to be == [ Wongi::Engine::WME.new(:light_bathroom, :last_user, :rule1) ]
141
+
142
+ engine << [:poweruser, :want_action_for, :light_bathroom]
143
+ engine << [:poweruser, :light_bathroom, :super_on]
144
+ expect( engine.select(:light_bathroom, :value, :_) ).to be == [ Wongi::Engine::WME.new(:light_bathroom, :value, :super_on) ]
145
+ expect( engine.select(:light_bathroom, :last_user, :_) ).to be == [ Wongi::Engine::WME.new(:light_bathroom, :last_user, :poweruser) ]
146
+
147
+ engine << [:god, :want_action_for, :light_bathroom]
148
+ engine << [:god, :light_bathroom, :let_there_be_light]
149
+ expect( engine.select(:light_bathroom, :value, :_) ).to be == [ Wongi::Engine::WME.new(:light_bathroom, :value, :let_there_be_light) ]
150
+ expect( engine.select(:light_bathroom, :last_user, :_) ).to be == [ Wongi::Engine::WME.new(:light_bathroom, :last_user, :god) ]
151
+
152
+ end
153
+
154
+ it 'should clean up correctly with a different activation order' do
155
+
156
+ engine.rule :rule1 do
157
+ forall {
158
+ has :light_kitchen, :value, :on
159
+ }
160
+ make {
161
+ #trace values: true, generation: true
162
+ gen self.name, :light_bathroom, :on
163
+ gen self.name, :want_action_for, :light_bathroom
164
+ }
165
+ end
166
+
167
+ prod = engine.rule "action" do
168
+ forall {
169
+ has :Requestor, :want_action_for, :Actor
170
+ has :Requestor, :Actor, :Value
171
+ has :Requestor, :priority, :Priority
172
+ ncc {
173
+ has :OtherRequestor, :want_action_for, :Actor
174
+ diff :OtherRequestor, :Requestor
175
+ has :OtherRequestor, :priority, :OtherPriority
176
+ greater :OtherPriority, :Priority
177
+ }
178
+ }
179
+ make {
180
+ #trace values: true, generation: true
181
+ gen :Actor, :value, :Value
182
+ gen :Actor, :last_user, :Requestor
183
+ }
184
+ end
185
+
186
+ engine << [:user, :priority, 2]
187
+ engine << [:rule1, :priority, 1]
188
+ engine << [:poweruser, :priority, 3]
189
+ engine << [:god, :priority, 4]
190
+
191
+ engine << [:user, :want_action_for, :light_bathroom]
192
+ engine << [:user, :light_bathroom, :off]
193
+ expect( engine.select(:light_bathroom, :value, :_) ).to be == [ Wongi::Engine::WME.new(:light_bathroom, :value, :off) ]
194
+ expect( engine.select(:light_bathroom, :last_user, :_) ).to be == [ Wongi::Engine::WME.new(:light_bathroom, :last_user, :user) ]
195
+
196
+ engine << [:light_kitchen, :value, :on]
197
+ expect( engine.select(:light_bathroom, :value, :_) ).to be == [ Wongi::Engine::WME.new(:light_bathroom, :value, :off) ]
198
+ expect( engine.select(:light_bathroom, :last_user, :_) ).to be == [ Wongi::Engine::WME.new(:light_bathroom, :last_user, :user) ]
199
+
200
+ engine << [:poweruser, :want_action_for, :light_bathroom]
201
+ engine << [:poweruser, :light_bathroom, :super_on]
202
+ expect( engine.select(:light_bathroom, :value, :_) ).to be == [ Wongi::Engine::WME.new(:light_bathroom, :value, :super_on) ]
203
+ expect( engine.select(:light_bathroom, :last_user, :_) ).to be == [ Wongi::Engine::WME.new(:light_bathroom, :last_user, :poweruser) ]
204
+
205
+ engine << [:god, :want_action_for, :light_bathroom]
206
+ engine << [:god, :light_bathroom, :let_there_be_light]
207
+ expect( engine.select(:light_bathroom, :value, :_) ).to be == [ Wongi::Engine::WME.new(:light_bathroom, :value, :let_there_be_light) ]
208
+ expect( engine.select(:light_bathroom, :last_user, :_) ).to be == [ Wongi::Engine::WME.new(:light_bathroom, :last_user, :god) ]
209
+
210
+ end
211
+
212
+ it 'should ncc-deactivate without destroying tokens' do
213
+ engine << rule {
214
+ forall {
215
+ has :Student, :is_a, :student
216
+ has :Course, :is_a, :course
217
+ none {
218
+ has :Requirement, :is_a, :requirement
219
+ has :Course, :Requirement, :RequiredGrade
220
+ any {
221
+ option {
222
+ neg :Student, :Requirement, :_
223
+ }
224
+ option {
225
+ has :Student, :Requirement, :ReceivedGrade
226
+ less :ReceivedGrade, :RequiredGrade
227
+ }
228
+ }
229
+ }
230
+ }
231
+ make {
232
+ gen :Student, :passes_for, :Course
233
+ }
234
+ }
235
+
236
+ %w( math science english bio ).each { |req| engine << [ req, :is_a, :requirement ] }
237
+ %w( CourseA CourseB CourseC ).each { |course| engine << [ course, :is_a, :course ] }
238
+ engine << [ "StudentA", :is_a, :student ]
239
+
240
+ engine << ["CourseA", "math", 50]
241
+ engine << ["CourseA", "science", 50]
242
+
243
+ engine << ["CourseB", "math", 50]
244
+ engine << ["CourseB", "english", 50]
245
+
246
+ engine << ["CourseC", "math", 50]
247
+ engine << ["CourseC", "bio", 50]
248
+
249
+ engine << ["StudentA", "math", 60]
250
+ engine << ["StudentA", "science", 60]
251
+ engine << ["StudentA", "bio", 40]
252
+
253
+ expect(engine.find "StudentA", :passes_for, "CourseA").not_to be_nil
254
+ expect(engine.find "StudentA", :passes_for, "CourseB").to be_nil
255
+ expect(engine.find "StudentA", :passes_for, "CourseC").to be_nil
256
+ end
257
+
258
+ end