mini_kraken 0.1.01 → 0.1.02

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/README.md +0 -3
  4. data/lib/mini_kraken/core/any_value.rb +29 -0
  5. data/lib/mini_kraken/core/association.rb +21 -0
  6. data/lib/mini_kraken/core/association_walker.rb +179 -0
  7. data/lib/mini_kraken/core/atomic_term.rb +64 -0
  8. data/lib/mini_kraken/core/binary_relation.rb +61 -0
  9. data/lib/mini_kraken/core/composite_term.rb +54 -0
  10. data/lib/mini_kraken/core/cons_cell.rb +44 -0
  11. data/lib/mini_kraken/core/duck_fiber.rb +44 -0
  12. data/lib/mini_kraken/core/environment.rb +59 -0
  13. data/lib/mini_kraken/core/equals.rb +216 -0
  14. data/lib/mini_kraken/core/fail.rb +8 -5
  15. data/lib/mini_kraken/core/freshness.rb +42 -0
  16. data/lib/mini_kraken/core/goal.rb +31 -6
  17. data/lib/mini_kraken/core/k_integer.rb +15 -0
  18. data/lib/mini_kraken/core/k_symbol.rb +15 -0
  19. data/lib/mini_kraken/core/nullary_relation.rb +11 -3
  20. data/lib/mini_kraken/core/outcome.rb +35 -0
  21. data/lib/mini_kraken/core/relation.rb +31 -4
  22. data/lib/mini_kraken/core/succeed.rb +13 -1
  23. data/lib/mini_kraken/core/term.rb +7 -0
  24. data/lib/mini_kraken/core/variable.rb +45 -3
  25. data/lib/mini_kraken/core/variable_ref.rb +76 -0
  26. data/lib/mini_kraken/core/vocabulary.rb +161 -0
  27. data/lib/mini_kraken/glue/fresh_env.rb +31 -0
  28. data/lib/mini_kraken/glue/run_star_expression.rb +43 -0
  29. data/lib/mini_kraken/version.rb +1 -1
  30. data/spec/core/association_spec.rb +38 -0
  31. data/spec/core/association_walker_spec.rb +191 -0
  32. data/spec/core/cons_cell_spec.rb +63 -0
  33. data/spec/core/duck_fiber_spec.rb +62 -0
  34. data/spec/core/environment_spec.rb +154 -0
  35. data/spec/core/equals_spec.rb +289 -0
  36. data/spec/core/fail_spec.rb +16 -0
  37. data/spec/core/goal_spec.rb +36 -10
  38. data/spec/core/k_symbol_spec.rb +72 -0
  39. data/spec/core/succeed_spec.rb +43 -0
  40. data/spec/core/variable_ref_spec.rb +31 -0
  41. data/spec/core/variable_spec.rb +11 -3
  42. data/spec/core/vocabulary_spec.rb +188 -0
  43. data/spec/glue/fresh_env_spec.rb +36 -0
  44. data/spec/glue/run_star_expression_spec.rb +247 -0
  45. data/spec/support/factory_methods.rb +54 -0
  46. metadata +46 -13
  47. data/lib/mini_kraken/core/facade.rb +0 -45
  48. data/lib/mini_kraken/core/formal_arg.rb +0 -6
  49. data/lib/mini_kraken/core/publisher.rb +0 -27
  50. data/lib/mini_kraken/core/run_star_expression.rb +0 -34
  51. data/lib/mini_kraken/dsl/kraken_dsl.rb +0 -12
  52. data/spec/core/facade_spec.rb +0 -38
  53. data/spec/core/run_star_expression_spec.rb +0 -43
  54. data/spec/dsl/kraken_dsl_spec.rb +0 -31
@@ -0,0 +1,31 @@
1
+ require_relative '../core/environment'
2
+ require_relative '../core/variable'
3
+
4
+ module MiniKraken
5
+ module Glue
6
+ # (fresh (x) (== 'pea q))
7
+ # Introduces the new variable 'x'
8
+ # Takes a list of names and a goal-like object
9
+ # Must respond to message attain(aPublisher, vars) and must return an Outcome
10
+ class FreshEnv < Core::Environment
11
+ # @return [Goal]
12
+ attr_reader :goal
13
+
14
+ # @param theNames [Array<String>]
15
+ # @param aGoal [Goal]
16
+ def initialize(theNames, aGoal)
17
+ super()
18
+ @goal = aGoal
19
+ theNames.each { |nm| add_var(Core::Variable.new(nm)) }
20
+ end
21
+
22
+ # Attempt to achieve the goal given this environment
23
+ # @param aParent [Environment]
24
+ # @return [Fiber<Outcome>] A Fiber object that will generate the results.
25
+ def attain(aParent)
26
+ self.parent = aParent
27
+ goal.attain(self)
28
+ end
29
+ end # class
30
+ end # module
31
+ end # module
@@ -0,0 +1,43 @@
1
+ require_relative '../core/any_value'
2
+ require_relative '../core/cons_cell'
3
+ require_relative 'fresh_env'
4
+
5
+ module MiniKraken
6
+ module Glue
7
+ class RunStarExpression
8
+ attr_reader :env
9
+
10
+ # @param var_name [String]
11
+ # @param goal [Core::Goal]
12
+ def initialize(var_name, goal)
13
+ @env = FreshEnv.new([var_name], goal)
14
+ end
15
+
16
+ def var
17
+ env.vars.values.first
18
+ end
19
+
20
+ def run
21
+ result = nil
22
+ solver = env.goal.attain(env)
23
+ # require 'debug'
24
+ loop do
25
+ outcome = solver.resume
26
+ break if outcome.nil?
27
+ env.clear
28
+ if result # ... more than one result...
29
+ else
30
+ if outcome.successful?
31
+ env.propagate(outcome)
32
+ result = Core::ConsCell.new(var.quote(outcome))
33
+ else
34
+ result = Core::NullList
35
+ end
36
+ end
37
+ end
38
+
39
+ result
40
+ end
41
+ end # class
42
+ end # module
43
+ end # module
@@ -1,3 +1,3 @@
1
1
  module MiniKraken
2
- VERSION = "0.1.01"
2
+ VERSION = '0.1.02'.freeze
3
3
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../../lib/mini_kraken/core/k_symbol'
5
+ require_relative '../../lib/mini_kraken/core/variable'
6
+
7
+ # Load the class under test
8
+ require_relative '../../lib/mini_kraken/core/association'
9
+
10
+ module MiniKraken
11
+ module Core
12
+ describe Association do
13
+ let(:pea) {KSymbol.new(:pea) }
14
+ subject { Association.new('q', pea) }
15
+
16
+ context 'Initialization:' do
17
+ it 'should be initialized with a name and a value' do
18
+ expect { Association.new('q', pea) }.not_to raise_error
19
+ end
20
+
21
+ it 'should be initialized with a variable and a value' do
22
+ expect { Association.new(Variable.new('p'), pea) }.not_to raise_error
23
+ end
24
+
25
+ it 'should know the variable name' do
26
+ expect(subject.var_name).to eq('q')
27
+ end
28
+
29
+ it 'should know the associated value' do
30
+ expect(subject.value).to eq(pea)
31
+ end
32
+ end # context
33
+
34
+ context 'Provided services:' do
35
+ end # context
36
+ end # describe
37
+ end # module
38
+ end # module
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../../lib/mini_kraken/core/environment'
5
+ require_relative '../support/factory_methods'
6
+
7
+ # Load the class under test
8
+ require_relative '../../lib/mini_kraken/core/association_walker'
9
+
10
+
11
+ module MiniKraken
12
+ module Core
13
+ describe AssociationWalker do
14
+ include FactoryMethods
15
+
16
+ subject { AssociationWalker.new }
17
+
18
+ context 'Initialization:' do
19
+ it 'should be initialized without argument' do
20
+ expect { AssociationWalker.new }.not_to raise_error
21
+ end
22
+
23
+ it "shouldn't have any visitee at initialization" do
24
+ expect(subject.visitees.size).to eq(0)
25
+ end
26
+ end # context
27
+
28
+ context 'Provided services:' do
29
+ let(:pea) { KSymbol.new(:pea) }
30
+ let(:pod) { KSymbol.new(:pod) }
31
+ let(:var_q) { Variable.new('q') }
32
+ let(:ref_q) { VariableRef.new('q') }
33
+ let(:var_x) { Variable.new('x') }
34
+ let(:ref_x) { VariableRef.new('x') }
35
+ let(:env) { Environment.new }
36
+
37
+ it 'should return composite when it has only atomic term(s)' do
38
+ expr1 = cons(pea)
39
+ expect(subject.walk_value(expr1, env)).to eq(expr1)
40
+
41
+ expr2 = cons(pea, pod)
42
+ expect(subject.walk_value(expr2, env)).to eq(expr2)
43
+ end
44
+
45
+ it 'should return composite when it has ground composite term(s)' do
46
+ expr1 = cons(pea, cons(pod))
47
+ expect(subject.walk_value(expr1, env)).to eq(expr1)
48
+
49
+ expr2 = cons(pea, cons(pod, cons(pea, cons(pod))))
50
+ expect(subject.walk_value(expr2, env)).to eq(expr2)
51
+ end
52
+
53
+ it 'should return nil when there is one fresh variable' do
54
+ env.add_var(var_q)
55
+ expr1 = cons(pea, cons(pod, ref_q))
56
+ expect(subject.walk_value(expr1, env)).to be_nil
57
+ end
58
+
59
+ it 'should return composite when it has ground composite term(s)' do
60
+ env.add_var(var_q)
61
+ env.add_assoc(Association.new('q', pea))
62
+ expr1 = cons(pea, cons(pod, cons(ref_q)))
63
+ expect(subject.walk_value(expr1, env)).to eq(expr1)
64
+ end
65
+
66
+ it 'should return nil when no assocation exists' do
67
+ env.add_var(var_q)
68
+ env.add_assoc(Association.new('q', pea))
69
+ env.add_var(var_x)
70
+
71
+ expect(subject.find_ground(var_x.name, env)).to be_nil
72
+ end
73
+
74
+ it 'should find an atomic term directly associated' do
75
+ env.add_var(var_q)
76
+ env.add_assoc(Association.new('q', pea))
77
+
78
+ result = subject.find_ground(var_q.name, env)
79
+ expect(result).to eq(pea)
80
+ end
81
+
82
+ it 'should find an atomic term directly associated' do
83
+ env.add_var(var_q)
84
+ env.add_var(var_x)
85
+ env.add_assoc(Association.new('q', ref_x))
86
+ env.add_assoc(Association.new('x', pea))
87
+ expect(env['x']).not_to be_nil
88
+
89
+ result = subject.find_ground(var_q.name, env)
90
+ expect(result).to eq(pea)
91
+ end
92
+
93
+ it 'should cope with cyclic structures' do
94
+ env.add_var(var_q)
95
+ env.add_var(var_x)
96
+ env.add_assoc(Association.new('q', ref_x))
97
+ env.add_assoc(Association.new('x', pea))
98
+ env.add_assoc(Association.new('x', ref_q))
99
+
100
+ result = subject.find_ground(var_q.name, env)
101
+ expect(result).to eq(pea)
102
+
103
+ result = subject.find_ground(var_x.name, env)
104
+ expect(result).to eq(pea)
105
+ end
106
+
107
+ it 'should cope with a composite with atomic terms only' do
108
+ env.add_var(var_q)
109
+ expr = cons(pea, cons(pod, cons(pea)))
110
+ env.add_assoc(Association.new('q', expr))
111
+
112
+ result = subject.find_ground(var_q.name, env)
113
+ expect(result).to eq(expr)
114
+ end
115
+
116
+ it 'should cope with a composite with one fresh variable' do
117
+ env.add_var(var_q)
118
+ env.add_var(var_x)
119
+ expr = cons(pea, cons(pod, cons(ref_x)))
120
+ env.add_assoc(Association.new('q', expr))
121
+
122
+ result = subject.find_ground(var_q.name, env)
123
+ expect(result).to be_nil
124
+ end
125
+
126
+ it 'should cope with a composite with one ground variable' do
127
+ env.add_var(var_q)
128
+ env.add_var(var_x)
129
+ expr = cons(pea, cons(pod, cons(ref_x)))
130
+ env.add_assoc(Association.new('q', expr))
131
+ env.add_assoc(Association.new('x', pod))
132
+
133
+ result = subject.find_ground(var_q.name, env)
134
+ expect(result).to eq(expr)
135
+ end
136
+ =begin
137
+ =end
138
+ it 'should categorize a variable without association as free' do
139
+ env.add_var(var_q)
140
+ result = subject.determine_freshness(ref_q, env)
141
+ expect(result).to be_fresh
142
+ expect(result.associated).to be_nil
143
+ end
144
+
145
+ it 'should categorize a variable related to fresh variable as bound' do
146
+ env.add_var(var_q)
147
+ env.add_var(var_x)
148
+ env.add_assoc(Association.new('q', ref_x))
149
+
150
+ result = subject.determine_freshness(ref_q, env)
151
+ expect(result).to be_bound
152
+ expect(result.associated).to eq(ref_x)
153
+ end
154
+
155
+ it 'should categorize a variable even in presence of cycle(s)' do
156
+ env.add_var(var_q)
157
+ env.add_var(var_x)
158
+ env.add_assoc(Association.new('q', ref_x))
159
+ env.add_assoc(Association.new('x', ref_q))
160
+
161
+ result = subject.determine_freshness(ref_q, env)
162
+ expect(result).to be_bound
163
+ expect(result.associated).to eq(ref_x)
164
+ end
165
+
166
+ it 'should categorize an atomic term as ground term' do
167
+ result = subject.determine_freshness(pea, env)
168
+ expect(result).to be_ground
169
+ expect(result.associated).to eq(pea)
170
+ end
171
+
172
+ it 'should categorize a composite term as ground term' do
173
+ # Ground composite: a composite where all members are ground
174
+ composite = cons(pea, cons(pod))
175
+ result = subject.determine_freshness(composite, env)
176
+ expect(result).to be_ground
177
+ expect(result.associated).to eq(composite)
178
+ end
179
+
180
+ it 'should categorize a composite term as bound term' do
181
+ # Bound composite: a composite where at least one member is fresh
182
+ env.add_var(var_q)
183
+ composite = cons(pea, cons(ref_q))
184
+ result = subject.determine_freshness(composite, env)
185
+ expect(result).to be_bound
186
+ expect(result.associated).to eq(composite)
187
+ end
188
+ end # context
189
+ end # describe
190
+ end # module
191
+ end # module
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../../lib/mini_kraken/core/k_symbol'
5
+
6
+ # Load the class under test
7
+ require_relative '../../lib/mini_kraken/core/cons_cell'
8
+
9
+ module MiniKraken
10
+ module Core
11
+ describe ConsCell do
12
+ let(:pea) {KSymbol.new(:pea) }
13
+ let(:pod) {KSymbol.new(:pod) }
14
+ subject { ConsCell.new(pea, pod) }
15
+
16
+ context 'Initialization:' do
17
+ it 'could be initialized with one argument' do
18
+ expect { ConsCell.new(pea) }.not_to raise_error
19
+ end
20
+
21
+ it 'could be initialized with a second optional argument' do
22
+ expect { ConsCell.new(pea, pod) }.not_to raise_error
23
+ end
24
+
25
+ it 'should know its car child' do
26
+ expect(subject.car).to eq(pea)
27
+ end
28
+
29
+ it 'should know its cdr child' do
30
+ expect(subject.cdr).to eq(pod)
31
+ end
32
+
33
+ it 'should know its children' do
34
+ expect(subject.children).to eq([pea, pod])
35
+ end
36
+
37
+ it 'should know if it is empty (null)' do
38
+ expect(subject).not_to be_null
39
+ expect(ConsCell.new(nil, nil)).to be_null
40
+ expect(NullList).to be_null
41
+ end
42
+ end # context
43
+
44
+ context 'Provided services:' do
45
+ it 'should compare to itself' do
46
+ expect(subject.eql?(subject)).to be_truthy
47
+ expect(subject == subject).to be_truthy
48
+ end
49
+
50
+ it 'should compare to another instance' do
51
+ same = ConsCell.new(pea, pod)
52
+ expect(subject.eql?(same)).to be_truthy
53
+
54
+ different = ConsCell.new(pod, pea)
55
+ expect(subject.eql?(different)).to be_falsey
56
+
57
+ different = ConsCell.new(pea)
58
+ expect(subject.eql?(different)).to be_falsey
59
+ end
60
+ end # context
61
+ end # describe
62
+ end # module
63
+ end # module
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../../lib/mini_kraken/core/environment'
5
+
6
+ # Load the class under test
7
+ require_relative '../../lib/mini_kraken/core/duck_fiber'
8
+
9
+ module MiniKraken
10
+ module Core
11
+ describe DuckFiber do
12
+ subject { DuckFiber.new(:failure) }
13
+
14
+ context 'Initialization:' do
15
+ it 'should be initialized with a symbol and an optional block' do
16
+ expect { DuckFiber.new(:failure) }.not_to raise_error
17
+
18
+ expect { DuckFiber.new(:custom) { Outcome.new(:"#s") } }.not_to raise_error
19
+ end
20
+
21
+ it 'should know its outcome' do
22
+ expect(subject.outcome).to eq(Failure)
23
+ end
24
+ end # context
25
+
26
+ context 'Provided services:' do
27
+ let(:parent) { Environment.new }
28
+
29
+ it 'should behave like a Fiber yielding a failure' do
30
+ failing = DuckFiber.new(:failure)
31
+ outcome = nil
32
+ expect { outcome = failing.resume }.not_to raise_error
33
+ expect(outcome).to eq(Failure)
34
+
35
+ # Only one result should be yielded
36
+ expect(failing.resume).to be_nil
37
+ end
38
+
39
+ it 'should behave like a Fiber yielding a basic success' do
40
+ succeeding = DuckFiber.new(:success)
41
+ outcome = nil
42
+ expect { outcome = succeeding.resume }.not_to raise_error
43
+ expect(outcome).to eq(BasicSuccess)
44
+
45
+ # Only one result should be yielded
46
+ expect(succeeding.resume).to be_nil
47
+ end
48
+
49
+ it 'should behave like a Fiber yielding a custom outcome' do
50
+
51
+ tailored = DuckFiber.new(:custom) { Outcome.new(:"#s", parent) }
52
+ outcome = nil
53
+ expect { outcome = tailored.resume }.not_to raise_error
54
+ expect(outcome).to eq(Outcome.new(:"#s", parent))
55
+
56
+ # Only one result should be yielded
57
+ expect(tailored.resume).to be_nil
58
+ end
59
+ end # context
60
+ end # describe
61
+ end # module
62
+ end # module
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../../lib/mini_kraken/core/k_symbol'
5
+ require_relative '../../lib/mini_kraken/core/variable'
6
+ require_relative '../../lib/mini_kraken/core/variable_ref'
7
+ require_relative '../../lib/mini_kraken/core/association'
8
+ require_relative '../../lib/mini_kraken/core/outcome'
9
+
10
+ # Load the class under test
11
+ require_relative '../../lib/mini_kraken/core/environment'
12
+
13
+ module MiniKraken
14
+ module Core
15
+ describe Environment do
16
+ subject { Environment.new }
17
+
18
+ context 'Initialization:' do
19
+ it 'should be initialized with an optional parent' do
20
+ expect { Environment.new }.not_to raise_error
21
+ parent = Environment.new
22
+ expect { Environment.new(parent) }.not_to raise_error
23
+ end
24
+
25
+ it "shouldn't have variable by default" do
26
+ expect(subject.vars).to be_empty
27
+ end
28
+
29
+ it "shouldn't have associations by default" do
30
+ expect(subject.associations).to be_empty
31
+ end
32
+
33
+ it 'shold know its parent (if any)' do
34
+ # Case: no parent
35
+ expect(subject.parent).to be_nil
36
+
37
+ # Case: there is a parent
38
+ child = Environment.new(subject)
39
+ expect(child.parent).to eq(subject)
40
+ end
41
+
42
+ end # context
43
+
44
+ context 'Provided services:' do
45
+ let(:var_a) { Variable.new('a') }
46
+ let(:var_b) { Variable.new('b') }
47
+ let(:var_c) { Variable.new('c') }
48
+ let(:var_c_bis) { Variable.new('c') }
49
+ let(:pea) { KSymbol.new(:pea) }
50
+ let(:pod) { KSymbol.new(:pod) }
51
+ let(:pad) { KSymbol.new(:pad) }
52
+
53
+ it 'should accept the addition of a variable' do
54
+ subject.add_var(var_a)
55
+ expect(subject.vars).not_to be_empty
56
+ expect(subject.vars['a']).to eq(var_a)
57
+ end
58
+
59
+ it 'should accept the addition of multiple variables' do
60
+ subject.add_var(var_a)
61
+ expect(subject.vars).not_to be_empty
62
+ subject.add_var(var_b)
63
+ expect(subject.vars['a']).to eq(var_a)
64
+ expect(subject.vars['b']).to eq(var_b)
65
+ end
66
+
67
+ it 'should accept the addition of an association' do
68
+ subject.add_var(var_a)
69
+ assoc = Association.new('a', pea)
70
+ subject.add_assoc(assoc)
71
+ expect(subject.associations.size).to eq(1)
72
+ expect(subject.associations['a']).to eq([assoc])
73
+ end
74
+
75
+ it 'should tell that a newborn variable is fresh' do
76
+ subject.add_var(var_a)
77
+
78
+ # By default, a variable is fresh...
79
+ expect(subject.fresh?(var_a)).to be_truthy
80
+ end
81
+
82
+ it "should tell variable associated with a literal value isn't fresh" do
83
+ subject.add_var(var_a)
84
+
85
+ # Let's associate an atomic term...
86
+ assoc = Association.new('a', pea)
87
+ subject.add_assoc(assoc)
88
+
89
+ expect(subject.fresh?(var_a)).to be_falsey
90
+ end
91
+
92
+ it "should cope with a variable associated with another variable" do
93
+ subject.add_var(var_a)
94
+ subject.add_var(var_b)
95
+
96
+ # Let's associate a with (fresh) b
97
+ assoc = Association.new(var_a, var_b)
98
+ subject.add_assoc(assoc)
99
+
100
+ expect(subject.fresh?(var_a)).to be_truthy
101
+
102
+ # Now associate b with something ground...
103
+ assoc_b = Association.new(var_b, pea)
104
+ subject.add_assoc(assoc_b)
105
+
106
+ # b is no more fresh, so is ... a
107
+ expect(subject.fresh?(var_b)).to be_falsey
108
+ expect(subject.fresh?(var_a)).to be_falsey
109
+ end
110
+
111
+ it 'should remove all associations' do
112
+ subject.add_var(var_a)
113
+ assoc = Association.new(var_a, pea)
114
+ subject.add_assoc(assoc)
115
+
116
+ subject.add_var(var_b)
117
+ assoc = Association.new(var_b, pod)
118
+ subject.add_assoc(assoc)
119
+
120
+ subject.clear
121
+ expect(subject.fresh?(var_a)).to be_truthy
122
+ expect(subject.fresh?(var_a)).to be_truthy
123
+ end
124
+
125
+ it 'should propagate associations up in the environment hierarchy' do
126
+ parent = Environment.new
127
+ parent.add_var(var_a)
128
+ # parent.add_var(var_c)
129
+ instance = Environment.new(parent)
130
+ instance.add_var(var_b)
131
+ # instance.add_var(var_c_bis) # Should shadow parent's c variable
132
+
133
+ outcome = Outcome.new(:"#s", instance)
134
+ outcome.add_assoc(Association.new(var_a, pea))
135
+ outcome.add_assoc(Association.new(var_b, pod))
136
+ # outcome.add_assoc(Association.new(var_c_bis, pad))
137
+ expect(outcome.associations.size).to eq(2)
138
+
139
+ # Propagate: outcome -> .. -> instance
140
+ instance.propagate(outcome)
141
+ expect(outcome.associations.size).to eq(1)
142
+ expect(instance.associations[var_b.name]).not_to be_nil
143
+ expect(parent.associations[var_a.name]).to be_nil
144
+
145
+ # Propagate: outcome -> .. -> parent
146
+ parent.propagate(outcome)
147
+ expect(outcome.associations).to be_empty
148
+ expect(parent.associations[var_b.name]).to be_nil
149
+ expect(parent.associations[var_a.name]).not_to be_nil
150
+ end
151
+ end # context
152
+ end # describe
153
+ end # module
154
+ end # module