Ruby Set with custom class to equal basic strings
Set
is built upon Hash
, and Hash
considers two objects the same if:
[...] their
hash
value is identical and the two objects areeql?
to each other.
What you are missing is that eql?
isn't necessarily commutative. Making Rank#eql?
recognize strings doesn't change the way String#eql?
works:
one.eql?('one') #=> true
'one'.eql?(one) #=> false
Therefore it depends on which object is the hash key and which is the argument to include?
:
Set['one'].include?(one) #=> true
Set[one].include?('one') #=> false
In order to make two objects a
and b
interchangeable hash keys, 3 conditions have to be met:
a.hash == b.hash
a.eql?(b) == true
b.eql?(a) == true
But don't try to modify String#eql?
– fiddling with Ruby's core classes isn't recommended and monkey-patching probably won't work anyway because Ruby usually calls the C methods directly for performance reasons.
In fact, making both hash
and eql?
mimic name
doesn't seem like a good idea in the first place. It makes the object's identity ambiguous which can lead to very strange behavior and hard to find bugs:
h = { one => 1, 'one' => 1 }
#=> {#<struct Rank name="one">=>1, "one"=>1}
# vs
h = { 'one' => 1, one => 1 }
#=> {"one"=>1}