Skip to content

Commit da5b844

Browse files
committed
Fix enum hash equality
Protobuf::Enum delegates methods to Fixnum, which has a custom hash equality method (`eql?`). This causes enum values to be equivalent when using `==`, `===`, `equals?`, but not `eql?`**: 2.3.7 :002 > Test::EnumTestType::ZERO.eql?(::Test::EnumTestType::ZERO) => false However, they are equilvalent to their tag value: 2.3.7 :002 > Test::EnumTestType::ZERO.eql?(::Test::EnumTestType::ZERO.tag) => true Use the hash equality implementation from Object#eql?, which is equivalent to == instead. **The implementation changed in Ruby 2.5, so this only affects Ruby versions less than v2.5.
1 parent ac12151 commit da5b844

2 files changed

Lines changed: 33 additions & 0 deletions

File tree

lib/protobuf/enum.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,25 @@ def class
271271
tag.class
272272
end
273273

274+
# Protobuf::Enum delegates methods to Fixnum, which has a custom hash equality method (`eql?`)
275+
# This causes enum values to be equivalent when using `==`, `===`, `equals?`, but not `eql?`**:
276+
#
277+
# 2.3.7 :002 > ::Test::EnumTestType::ZERO.eql?(::Test::EnumTestType::ZERO)
278+
# => false
279+
#
280+
# However, they are equilvalent to their tag value:
281+
#
282+
# 2.3.7 :002 > ::Test::EnumTestType::ZERO.eql?(::Test::EnumTestType::ZERO.tag)
283+
# => true
284+
#
285+
# **The implementation changed in Ruby 2.5, so this only affects Ruby versions less than v2.4.
286+
#
287+
# Use the hash equality implementation from Object#eql?, which is equivalent to == instead.
288+
#
289+
def eql?(other)
290+
self == other
291+
end
292+
274293
def inspect
275294
"\#<Protobuf::Enum(#{parent_class})::#{name}=#{tag}>"
276295
end

spec/lib/protobuf/enum_spec.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,20 @@
268268
specify { subject.try { |yielded| expect(yielded).to eq(subject) } }
269269
end
270270

271+
describe '#eql?' do
272+
it "is equal to itself" do
273+
expect(::Test::EnumTestType::ZERO.eql?(::Test::EnumTestType::ZERO)).to be(true)
274+
end
275+
276+
it "is equal to it's tag" do
277+
expect(::Test::EnumTestType::ZERO.eql?(::Test::EnumTestType::ZERO.tag)).to be(true)
278+
end
279+
280+
it "is not equal to it's name" do
281+
expect(::Test::EnumTestType::ZERO.eql?(::Test::EnumTestType::ZERO.name)).to be(false)
282+
end
283+
end
284+
271285
context 'when coercing from enum' do
272286
subject { Test::StatusType::PENDING }
273287
it { is_expected.to eq(0) }

0 commit comments

Comments
 (0)