If you want to compare two model instances based on their attributes, you will probably want to exclude certain irrelevant attributes from your comparison, such as: id
, created_at
, and updated_at
. (I would consider those to be more metadata about the record than part of the record's data itself.)
This might not matter when you are comparing two new (unsaved) records (since id
, created_at
, and updated_at
will all be nil
until saved), but I sometimes find it necessary to compare a saved object with an unsaved one (in which case == would give you false since nil != 5). Or I want to compare two saved objects to find out if they contain the same data (so the ActiveRecord ==
operator doesn't work, because it returns false if they have different id
's, even if they are otherwise identical).
My solution to this problem is to add something like this in the models that you want to be comparable using attributes:
def self.attributes_to_ignore_when_comparing
[:id, :created_at, :updated_at]
end
def identical?(other)
self. attributes.except(*self.class.attributes_to_ignore_when_comparing.map(&:to_s)) ==
other.attributes.except(*self.class.attributes_to_ignore_when_comparing.map(&:to_s))
end
Then in my specs I can write such readable and succinct things as this:
Address.last.should be_identical(Address.new({city: 'City', country: 'USA'}))
I'm planning on forking the active_record_attributes_equality
gem and changing it to use this behavior so that this can be more easily reused.
Some questions I have, though, include:
- Does such a gem already exist??
- What should the method be called? I don't think overriding the existing
==
operator is a good idea, so for now I'm calling it identical?
. But maybe something like practically_identical?
or attributes_eql?
would be more accurate, since it's not checking if they're strictly identical (some of the attributes are allowed to be different.)...
attributes_to_ignore_when_comparing
is too verbose. Not that this will need to be explicitly added to each model if they want to use the gem's defaults. Maybe allow the default to be overridden with a class macro like ignore_for_attributes_eql :last_signed_in_at, :updated_at
Comments are welcome...
Update: Instead of forking the active_record_attributes_equality
, I wrote a brand-new gem, active_record_ignored_attributes, available at http://github.com/TylerRick/active_record_ignored_attributes and http://rubygems.org/gems/active_record_ignored_attributes