mini_kraken 0.1.04 → 0.1.09
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 +4 -4
- data/.travis.yml +5 -1
- data/CHANGELOG.md +54 -0
- data/Gemfile +3 -1
- data/README.md +8 -6
- data/Rakefile +5 -3
- data/lib/mini_kraken.rb +3 -1
- data/lib/mini_kraken/core/any_value.rb +9 -7
- data/lib/mini_kraken/core/association.rb +20 -7
- data/lib/mini_kraken/core/association_walker.rb +5 -1
- data/lib/mini_kraken/core/atomic_term.rb +5 -3
- data/lib/mini_kraken/core/binary_relation.rb +8 -6
- data/lib/mini_kraken/core/composite_goal.rb +46 -0
- data/lib/mini_kraken/core/composite_term.rb +7 -20
- data/lib/mini_kraken/core/conj2.rb +76 -0
- data/lib/mini_kraken/core/cons_cell.rb +51 -41
- data/lib/mini_kraken/core/designation.rb +55 -0
- data/lib/mini_kraken/core/disj2.rb +71 -0
- data/lib/mini_kraken/core/duck_fiber.rb +4 -2
- data/lib/mini_kraken/core/environment.rb +25 -11
- data/lib/mini_kraken/core/equals.rb +127 -188
- data/lib/mini_kraken/core/fail.rb +20 -14
- data/lib/mini_kraken/core/freshness.rb +11 -8
- data/lib/mini_kraken/core/goal.rb +8 -4
- data/lib/mini_kraken/core/goal_arg.rb +10 -0
- data/lib/mini_kraken/core/goal_relation.rb +28 -0
- data/lib/mini_kraken/core/k_integer.rb +4 -3
- data/lib/mini_kraken/core/k_symbol.rb +4 -3
- data/lib/mini_kraken/core/nullary_relation.rb +3 -1
- data/lib/mini_kraken/core/outcome.rb +29 -25
- data/lib/mini_kraken/core/relation.rb +4 -18
- data/lib/mini_kraken/core/succeed.rb +20 -14
- data/lib/mini_kraken/core/term.rb +7 -2
- data/lib/mini_kraken/core/variable.rb +11 -25
- data/lib/mini_kraken/core/variable_ref.rb +12 -59
- data/lib/mini_kraken/core/vocabulary.rb +267 -48
- data/lib/mini_kraken/glue/fresh_env.rb +44 -6
- data/lib/mini_kraken/glue/run_star_expression.rb +49 -23
- data/lib/mini_kraken/version.rb +3 -1
- data/mini_kraken.gemspec +15 -13
- data/spec/core/association_spec.rb +4 -4
- data/spec/core/association_walker_spec.rb +25 -24
- data/spec/core/conj2_spec.rb +114 -0
- data/spec/core/cons_cell_spec.rb +12 -3
- data/spec/core/disj2_spec.rb +99 -0
- data/spec/core/duck_fiber_spec.rb +22 -12
- data/spec/core/environment_spec.rb +16 -28
- data/spec/core/equals_spec.rb +7 -7
- data/spec/core/fail_spec.rb +7 -7
- data/spec/core/goal_spec.rb +10 -10
- data/spec/core/k_symbol_spec.rb +5 -6
- data/spec/core/succeed_spec.rb +4 -4
- data/spec/core/variable_ref_spec.rb +0 -4
- data/spec/core/vocabulary_spec.rb +33 -27
- data/spec/glue/fresh_env_spec.rb +28 -2
- data/spec/glue/run_star_expression_spec.rb +370 -59
- data/spec/mini_kraken_spec.rb +4 -0
- data/spec/spec_helper.rb +3 -2
- data/spec/support/factory_methods.rb +20 -2
- metadata +12 -2
@@ -0,0 +1,99 @@
|
|
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/fail'
|
6
|
+
require_relative '../../lib/mini_kraken/core/succeed'
|
7
|
+
require_relative '../../lib/mini_kraken/core/equals'
|
8
|
+
require_relative '../../lib/mini_kraken/core/environment'
|
9
|
+
require_relative '../../lib/mini_kraken/core/variable'
|
10
|
+
require_relative '../../lib/mini_kraken/core/variable_ref'
|
11
|
+
|
12
|
+
# Load the class under test
|
13
|
+
require_relative '../../lib/mini_kraken/core/disj2'
|
14
|
+
|
15
|
+
module MiniKraken
|
16
|
+
module Core
|
17
|
+
describe Disj2 do
|
18
|
+
subject { Disj2.instance }
|
19
|
+
|
20
|
+
context 'Initialization:' do
|
21
|
+
it 'should be initialized without argument' do
|
22
|
+
expect { Disj2.instance }.not_to raise_error
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should know its name' do
|
26
|
+
expect(subject.name).to eq('disj2')
|
27
|
+
end
|
28
|
+
end # context
|
29
|
+
|
30
|
+
context 'Provided services:' do
|
31
|
+
let(:corn) { KSymbol.new(:corn) }
|
32
|
+
let(:meal) { KSymbol.new(:meal) }
|
33
|
+
let(:oil) { KSymbol.new(:oil) }
|
34
|
+
let(:olive) { KSymbol.new(:olive) }
|
35
|
+
let(:pea) { KSymbol.new(:pea) }
|
36
|
+
let(:fails) { Goal.new(Fail.instance, []) }
|
37
|
+
let(:succeeds) { Goal.new(Succeed.instance, []) }
|
38
|
+
let(:var_q) { Variable.new('q') }
|
39
|
+
let(:ref_q) { VariableRef.new('q') }
|
40
|
+
let(:env) do
|
41
|
+
e = Environment.new
|
42
|
+
e.add_var(var_q)
|
43
|
+
e
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should complain when one of its argument is not a goal' do
|
47
|
+
err = StandardError
|
48
|
+
expect { subject.solver_for([succeeds, pea], env) }.to raise_error(err)
|
49
|
+
expect { subject.solver_for([pea, succeeds], env) }.to raise_error(err)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should fails if both arguments fail' do
|
53
|
+
# Covers frame 1:55
|
54
|
+
solver = subject.solver_for([fails, fails], env)
|
55
|
+
expect(solver.resume).not_to be_successful
|
56
|
+
expect(solver.resume).to be_nil
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'yield success if first argument succeeds' do
|
60
|
+
# Covers frame 1:56
|
61
|
+
subgoal = Goal.new(Equals.instance, [olive, ref_q])
|
62
|
+
solver = subject.solver_for([subgoal, fails], env)
|
63
|
+
outcome = solver.resume
|
64
|
+
expect(outcome).to be_successful
|
65
|
+
expect(outcome.associations['q'].first.value).to eq(olive)
|
66
|
+
expect(solver.resume).to be_nil
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'yield success if second argument succeeds' do
|
70
|
+
# Covers frame 1:57
|
71
|
+
subgoal = Goal.new(Equals.instance, [oil, ref_q])
|
72
|
+
solver = subject.solver_for([fails, subgoal], env)
|
73
|
+
outcome = solver.resume
|
74
|
+
expect(outcome).to be_successful
|
75
|
+
expect(outcome.associations['q'].first.value).to eq(oil)
|
76
|
+
expect(solver.resume).to be_nil
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'yield two solutions if both arguments succeed' do
|
80
|
+
# Covers frame 1:58
|
81
|
+
subgoal1 = Goal.new(Equals.instance, [olive, ref_q])
|
82
|
+
subgoal2 = Goal.new(Equals.instance, [oil, ref_q])
|
83
|
+
solver = subject.solver_for([subgoal1, subgoal2], env)
|
84
|
+
|
85
|
+
# First solution
|
86
|
+
outcome1 = solver.resume
|
87
|
+
expect(outcome1).to be_successful
|
88
|
+
expect(outcome1.associations['q'].first.value).to eq(olive)
|
89
|
+
|
90
|
+
# Second solution
|
91
|
+
outcome2 = solver.resume
|
92
|
+
expect(outcome2).to be_successful
|
93
|
+
expect(outcome2.associations['q'].first.value).to eq(oil)
|
94
|
+
expect(solver.resume).to be_nil
|
95
|
+
end
|
96
|
+
end # context
|
97
|
+
end # describe
|
98
|
+
end # module
|
99
|
+
end # module
|
@@ -25,37 +25,47 @@ module MiniKraken
|
|
25
25
|
|
26
26
|
context 'Provided services:' do
|
27
27
|
let(:parent) { Environment.new }
|
28
|
-
|
28
|
+
|
29
29
|
it 'should behave like a Fiber yielding a failure' do
|
30
30
|
failing = DuckFiber.new(:failure)
|
31
31
|
outcome = nil
|
32
32
|
expect { outcome = failing.resume }.not_to raise_error
|
33
33
|
expect(outcome).to eq(Failure)
|
34
|
-
|
34
|
+
|
35
35
|
# Only one result should be yielded
|
36
|
-
expect(failing.resume).to be_nil
|
36
|
+
expect(failing.resume).to be_nil
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
it 'should behave like a Fiber yielding a basic success' do
|
40
40
|
succeeding = DuckFiber.new(:success)
|
41
41
|
outcome = nil
|
42
42
|
expect { outcome = succeeding.resume }.not_to raise_error
|
43
|
-
expect(outcome).to
|
44
|
-
|
43
|
+
expect(outcome).to be_successful
|
44
|
+
expect(outcome.parent).to be_nil
|
45
|
+
|
45
46
|
# Only one result should be yielded
|
46
47
|
expect(succeeding.resume).to be_nil
|
47
|
-
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should yield a distinct success object' do
|
51
|
+
instance1 = DuckFiber.new(:success)
|
52
|
+
outcome1 = instance1.resume
|
53
|
+
|
54
|
+
instance2 = DuckFiber.new(:success)
|
55
|
+
outcome2 = instance1.resume
|
56
|
+
|
57
|
+
expect(outcome1).not_to be_equal(outcome2)
|
58
|
+
end
|
48
59
|
|
49
60
|
it 'should behave like a Fiber yielding a custom outcome' do
|
50
|
-
|
51
|
-
tailored = DuckFiber.new(:custom) { Outcome.new(:"#s", parent) }
|
61
|
+
tailored = DuckFiber.new(:custom) { Outcome.new(:"#s", parent) }
|
52
62
|
outcome = nil
|
53
63
|
expect { outcome = tailored.resume }.not_to raise_error
|
54
64
|
expect(outcome).to eq(Outcome.new(:"#s", parent))
|
55
|
-
|
65
|
+
|
56
66
|
# Only one result should be yielded
|
57
|
-
expect(tailored.resume).to be_nil
|
58
|
-
end
|
67
|
+
expect(tailored.resume).to be_nil
|
68
|
+
end
|
59
69
|
end # context
|
60
70
|
end # describe
|
61
71
|
end # module
|
@@ -4,7 +4,6 @@ require_relative '../spec_helper' # Use the RSpec framework
|
|
4
4
|
require_relative '../../lib/mini_kraken/core/k_symbol'
|
5
5
|
require_relative '../../lib/mini_kraken/core/variable'
|
6
6
|
require_relative '../../lib/mini_kraken/core/variable_ref'
|
7
|
-
require_relative '../../lib/mini_kraken/core/association'
|
8
7
|
require_relative '../../lib/mini_kraken/core/outcome'
|
9
8
|
|
10
9
|
# Load the class under test
|
@@ -38,7 +37,6 @@ module MiniKraken
|
|
38
37
|
child = Environment.new(subject)
|
39
38
|
expect(child.parent).to eq(subject)
|
40
39
|
end
|
41
|
-
|
42
40
|
end # context
|
43
41
|
|
44
42
|
context 'Provided services:' do
|
@@ -66,8 +64,7 @@ module MiniKraken
|
|
66
64
|
|
67
65
|
it 'should accept the addition of an association' do
|
68
66
|
subject.add_var(var_a)
|
69
|
-
assoc =
|
70
|
-
subject.add_assoc(assoc)
|
67
|
+
assoc = subject.add_assoc('a', pea)
|
71
68
|
expect(subject.associations.size).to eq(1)
|
72
69
|
expect(subject.associations['a']).to eq([assoc])
|
73
70
|
end
|
@@ -83,25 +80,21 @@ module MiniKraken
|
|
83
80
|
subject.add_var(var_a)
|
84
81
|
|
85
82
|
# Let's associate an atomic term...
|
86
|
-
|
87
|
-
subject.add_assoc(assoc)
|
83
|
+
subject.add_assoc('a', pea)
|
88
84
|
|
89
85
|
expect(subject.fresh?(var_a)).to be_falsey
|
90
86
|
end
|
91
87
|
|
92
|
-
it
|
88
|
+
it 'should cope with a variable associated with another variable' do
|
93
89
|
subject.add_var(var_a)
|
94
90
|
subject.add_var(var_b)
|
95
91
|
|
96
92
|
# Let's associate a with (fresh) b
|
97
|
-
|
98
|
-
subject.add_assoc(assoc)
|
99
|
-
|
93
|
+
subject.add_assoc(var_a, var_b)
|
100
94
|
expect(subject.fresh?(var_a)).to be_truthy
|
101
95
|
|
102
96
|
# Now associate b with something ground...
|
103
|
-
|
104
|
-
subject.add_assoc(assoc_b)
|
97
|
+
subject.add_assoc(var_b, pea)
|
105
98
|
|
106
99
|
# b is no more fresh, so is ... a
|
107
100
|
expect(subject.fresh?(var_b)).to be_falsey
|
@@ -110,12 +103,10 @@ module MiniKraken
|
|
110
103
|
|
111
104
|
it 'should remove all associations' do
|
112
105
|
subject.add_var(var_a)
|
113
|
-
|
114
|
-
subject.add_assoc(assoc)
|
106
|
+
subject.add_assoc(var_a, pea)
|
115
107
|
|
116
108
|
subject.add_var(var_b)
|
117
|
-
|
118
|
-
subject.add_assoc(assoc)
|
109
|
+
subject.add_assoc(var_b, pod)
|
119
110
|
|
120
111
|
subject.clear
|
121
112
|
expect(subject.fresh?(var_a)).to be_truthy
|
@@ -125,30 +116,27 @@ module MiniKraken
|
|
125
116
|
it 'should propagate associations up in the environment hierarchy' do
|
126
117
|
parent = Environment.new
|
127
118
|
parent.add_var(var_a)
|
128
|
-
# parent.add_var(var_c)
|
129
119
|
instance = Environment.new(parent)
|
130
120
|
instance.add_var(var_b)
|
131
|
-
# instance.add_var(var_c_bis) # Should shadow parent's c variable
|
132
121
|
|
133
122
|
outcome = Outcome.new(:"#s", instance)
|
134
|
-
outcome.add_assoc(
|
135
|
-
outcome.add_assoc(
|
136
|
-
|
137
|
-
|
138
|
-
|
123
|
+
outcome.add_assoc(var_a, pea)
|
124
|
+
outcome.add_assoc(var_b, pod)
|
125
|
+
expect(outcome.associations.size).to eq(2)
|
126
|
+
|
139
127
|
# Propagate: outcome -> .. -> instance
|
140
|
-
instance.propagate(outcome)
|
128
|
+
instance.propagate(outcome)
|
141
129
|
expect(outcome.associations.size).to eq(1)
|
142
130
|
expect(instance.associations[var_b.name]).not_to be_nil
|
143
131
|
expect(parent.associations[var_a.name]).to be_nil
|
144
|
-
|
132
|
+
|
145
133
|
# Propagate: outcome -> .. -> parent
|
146
134
|
parent.propagate(outcome)
|
147
|
-
expect(outcome.associations).to be_empty
|
135
|
+
expect(outcome.associations).to be_empty
|
148
136
|
expect(parent.associations[var_b.name]).to be_nil
|
149
|
-
expect(parent.associations[var_a.name]).not_to be_nil
|
137
|
+
expect(parent.associations[var_a.name]).not_to be_nil
|
150
138
|
end
|
151
139
|
end # context
|
152
140
|
end # describe
|
153
141
|
end # module
|
154
|
-
end # module
|
142
|
+
end # module
|
data/spec/core/equals_spec.rb
CHANGED
@@ -49,21 +49,21 @@ module MiniKraken
|
|
49
49
|
let(:env) { Environment.new }
|
50
50
|
let(:var_q) { build_var('q') }
|
51
51
|
let(:ref_q) do
|
52
|
-
|
52
|
+
var_q # Force dependency
|
53
53
|
build_var_ref('q')
|
54
54
|
end
|
55
55
|
let(:ref_q_bis) do
|
56
|
-
|
56
|
+
var_q # Force dependency
|
57
57
|
build_var_ref('q')
|
58
58
|
end
|
59
59
|
let(:var_x) { build_var('x') }
|
60
60
|
let(:ref_x) do
|
61
|
-
|
61
|
+
var_x # Force dependency
|
62
62
|
build_var_ref('x')
|
63
63
|
end
|
64
64
|
let(:var_y) { build_var('y') }
|
65
65
|
let(:ref_y) do
|
66
|
-
|
66
|
+
var_y # Force dependency
|
67
67
|
build_var_ref('y')
|
68
68
|
end
|
69
69
|
|
@@ -246,7 +246,7 @@ module MiniKraken
|
|
246
246
|
result = solve_for(ref_x, ref_y)
|
247
247
|
|
248
248
|
expect(result).to be_successful
|
249
|
-
expect(env.associations
|
249
|
+
expect(env.associations).to be_empty # Symmetric association
|
250
250
|
expect(ref_x.fresh?(result)).to be_truthy
|
251
251
|
expect(ref_y.fresh?(result)).to be_truthy
|
252
252
|
end
|
@@ -285,7 +285,7 @@ module MiniKraken
|
|
285
285
|
expect(ref_x.fresh?(env)).to be_falsey
|
286
286
|
expect(ref_q.fresh?(env)).to be_falsey
|
287
287
|
end
|
288
|
-
|
288
|
+
|
289
289
|
it 'should fail for one right literal and one composite arguments' do
|
290
290
|
# Reasoned S2, frame 1:46
|
291
291
|
# x associated to ('pea 'pod)
|
@@ -297,7 +297,7 @@ module MiniKraken
|
|
297
297
|
|
298
298
|
expect(result.resultant).to eq(:"#u")
|
299
299
|
expect(result.associations).to be_empty
|
300
|
-
end
|
300
|
+
end
|
301
301
|
end # context
|
302
302
|
end # describe
|
303
303
|
end # module
|
data/spec/core/fail_spec.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require_relative '../spec_helper' # Use the RSpec framework
|
4
4
|
|
5
5
|
# Load the class under test
|
6
|
-
require_relative '../../lib/mini_kraken/core/fail'
|
6
|
+
require_relative '../../lib/mini_kraken/core/fail'
|
7
7
|
|
8
8
|
module MiniKraken
|
9
9
|
module Core
|
@@ -14,7 +14,7 @@ module MiniKraken
|
|
14
14
|
it 'should have one instance' do
|
15
15
|
expect { Fail.instance }.not_to raise_error
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
it 'should know its name' do
|
19
19
|
expect(subject.name).to eq('fail')
|
20
20
|
end
|
@@ -24,20 +24,20 @@ module MiniKraken
|
|
24
24
|
it 'should unconditionally return a failure result' do
|
25
25
|
args = double('fake-args')
|
26
26
|
env = double('fake-env')
|
27
|
-
|
27
|
+
|
28
28
|
solver = nil
|
29
29
|
expect { solver = subject.solver_for(args, env) }.not_to raise_error
|
30
|
-
|
30
|
+
|
31
31
|
# Solver should quack like a Fiber
|
32
32
|
dummy_arg = double('dummy-stuff')
|
33
33
|
result = solver.resume(dummy_arg)
|
34
34
|
expect(result).to eq(Failure)
|
35
|
-
|
35
|
+
|
36
36
|
# Only one "solution", next 'resume' call should return nil
|
37
37
|
result = solver.resume(dummy_arg)
|
38
|
-
expect(result).to be_nil
|
38
|
+
expect(result).to be_nil
|
39
39
|
end
|
40
40
|
end # context
|
41
41
|
end # describe
|
42
42
|
end # module
|
43
|
-
end # module
|
43
|
+
end # module
|
data/spec/core/goal_spec.rb
CHANGED
@@ -22,15 +22,15 @@ module MiniKraken
|
|
22
22
|
it 'should accept one nullary relation and empty argument array' do
|
23
23
|
expect { Goal.new(nullary_relation, []) }.not_to raise_error
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
it 'should accept one binary relation and 2-elements array' do
|
27
27
|
expect { Goal.new(binary_relation, [KSymbol.new(:pea), KSymbol.new(:pod)]) }.not_to raise_error
|
28
|
-
end
|
28
|
+
end
|
29
29
|
|
30
30
|
it 'should know its relation' do
|
31
31
|
expect(subject.relation).to eq(binary_relation)
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
it 'should know its actual arguments' do
|
35
35
|
expectations = [KSymbol.new(:pea), KSymbol.new(:pod)]
|
36
36
|
expect(subject.actuals).to eq(expectations)
|
@@ -41,21 +41,21 @@ module MiniKraken
|
|
41
41
|
it 'should fail if relation does not succeed' do
|
42
42
|
solver = subject.attain(env)
|
43
43
|
expect(solver.resume).not_to be_successful
|
44
|
-
|
44
|
+
|
45
45
|
# No more solution...
|
46
46
|
expect(solver.resume).to be_nil
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
it 'should succeed if relation succeeds' do
|
50
|
-
instance = Goal.new(binary_relation, [KSymbol.new(:pea), KSymbol.new(:pea)])
|
51
|
-
|
50
|
+
instance = Goal.new(binary_relation, [KSymbol.new(:pea), KSymbol.new(:pea)])
|
51
|
+
|
52
52
|
solver = instance.attain(env)
|
53
53
|
expect(solver.resume).to be_successful
|
54
|
-
|
54
|
+
|
55
55
|
# No more solution...
|
56
56
|
expect(solver.resume).to be_nil
|
57
|
-
end
|
57
|
+
end
|
58
58
|
end # context
|
59
59
|
end # describe
|
60
60
|
end # module
|
61
|
-
end # module
|
61
|
+
end # module
|
data/spec/core/k_symbol_spec.rb
CHANGED
@@ -20,12 +20,11 @@ module MiniKraken
|
|
20
20
|
it 'should know its value' do
|
21
21
|
expect(subject.value).to eq(a_value)
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
it 'should know that it is a ground term' do
|
25
25
|
env = double('mock-env')
|
26
|
-
visitees = double('fake-visitees')
|
27
26
|
expect(subject.ground?(env)).to be_truthy
|
28
|
-
end
|
27
|
+
end
|
29
28
|
end # context
|
30
29
|
|
31
30
|
context 'Provided services:' do
|
@@ -39,7 +38,7 @@ module MiniKraken
|
|
39
38
|
expect(subject).not_to be_eql(another)
|
40
39
|
|
41
40
|
# Different type, same value
|
42
|
-
yet_another = OpenStruct.new(:
|
41
|
+
yet_another = OpenStruct.new(value: :pea)
|
43
42
|
expect(subject).not_to be_eql(yet_another)
|
44
43
|
end
|
45
44
|
|
@@ -53,11 +52,11 @@ module MiniKraken
|
|
53
52
|
expect(subject == another).to be_falsy
|
54
53
|
|
55
54
|
# Same duck type, same value
|
56
|
-
yet_another = OpenStruct.new(:
|
55
|
+
yet_another = OpenStruct.new(value: :pea)
|
57
56
|
expect(subject == yet_another).to be_truthy
|
58
57
|
|
59
58
|
# Different duck type, different value
|
60
|
-
still_another = OpenStruct.new(:
|
59
|
+
still_another = OpenStruct.new(value: :pod)
|
61
60
|
expect(subject == still_another).to be_falsy
|
62
61
|
|
63
62
|
# Default Ruby representation, same value
|