ActiveRecord equality - unapparent
Comparing objects in Ruby there is distinct difference between object equality (== and eql?) and object identity (equal?). Both are defined at class Object. And for all inheriting sub classes: object equality is meant to be overwritten, though object identity is not.
That also applies to ActiveRecord.
A simple model example:
class Language < ActiveRecord::Base
end
At first a new object and its duplicate look very equal:
ruby = Language.new name: 'Ruby'
# => #<Language id: nil, name: "Ruby">
ruby.dup
# => #<Language id: nil, name: "Ruby">
As expected both objects are not identical:
ruby.equal? ruby.dup
# => false
The object equality should be verified in a RSpec test.
ActiveRecord equality - tested
It is expected that a language and its duplicate not only look equal, but also are equal:
require 'spec_helper'
describe Language, type: :model do
subject { Language.new name: 'Ruby' }
context "when duplicated" do
it { is_expected.to eq(subject.dup) }
end
end
However the test does not verify the assertion:
1) Language when duplicated should eq #<Language id: nil, name: "Ruby">
Failure/Error: it { is_expected.to eq(subject.dup) }
expected: #<Language id: nil, name: "Ruby">
got: #<Language id: nil, name: "Ruby">
(compared using ==)
Diff:
@@ -1,4 +1,4 @@
-#<Language:0x00000003e59248
+#<Language:0x000000056bca10
The reason for that behaviour can be found in the ActiveRecord implementation itself.
ActiveRecord equality - implemented
The method == was overwritten in ActiveRecord:
# active_record/core.rb
module ActiveRecord
module Core
def ==(comparison_object)
super ||
comparison_object.instance_of?(self.class) &&
!id.nil? &&
comparison_object.id == id
end
end
end
ActiveRecord objects only are equal, if they have a defined and equal ID.
ActiveRecord equality - overwritten
The default behaviour can be changed for sure. An appropriate object equality implementation:
class Language < ActiveRecord::Base
def == comparison_object
self.attributes == comparison_object.attributes
end
end
Languages should be equal, if their attributes (name) are equal accordingly.
The RSpec test verifies the behaviour:
Finished in 0.00517 seconds (files took 0.75063 seconds to load)
1 example, 0 failures