I have generalized the
recursion-safe routine used to compare two arrays,
so that it now determines whether two objects are "structurally equivalent".
It is fairly powerful, being able to track recursion across instance
variables, arrays...
What's this good for?
The obvious application would be checking whether some objects returned by
by the code under test have the desired structure.
Examples
Here's a taste of what it can do... I'll be using the following class to
illustrate the semantics of #struct_eql?.
class A
attr_accessor :a, :b
def initialize(a,b)
@a, @b = a, b
end
end
Let's perform some simple tests:
a = A.new(1, 2)
a.struct_eql?(A.new(2,3)) # => true
A.new(2,3) is considered "structurally equivalent" to A.new(1,2)
because both objects have two instance variables named @a and @b, and they
both refer to Fixnum objects. That's simple, and explains why the following
don't match:
a.struct_eql?(A.new("",1)) # => false
Object identity
We've seen the basics of the definition of "structural equivalence"
proposed here, but there's more. In the above examples, we had
another implicit condition:
a.struct_eql?(A.new(1,1)) # => false
The "structural equivalence" conditions expressed by A.new(1,2).struct_eql?(b)
could be rephrased as:
b must be an object of class A (or descendants)
b must have two instance variables named @a and @b
@a and @b must refer to two Fixnum objects
@a must be different from @b
The last condition can be reversed to express things like "both instance
variables must be equal":