views:

227

answers:

2

Rails' ActiveRecord::Base class defines an == method that returns true if the objects are identical or they have the same ID.

I've overridden == in a couple of my Rails models to allow for more meaningful equality conditions. These work when I compare objects directly (e.g., through script/console), but if I do something like my_array_of_models.include? other_model, include? always returns false. even if the array contains an object that is "equal" (according to my definition).

I've fixed this by doing, e.g., my_array_of_models.any? { |el| el.attr == other_model.attr } (which I think is the way you're encouraged to do comparisons, anyway), but I'm wondering: is it meaningful to override == in ActiveRecord models, or does ActiveRecord do something at a high level that renders such an overridden method useless (or worse, dangerous)?

Source

Here're my implementations of my overridden methods. There are two classes, User and Contact. Users have unique email addresses, so == returns true if the email addresses are the same. Contact is a bridge between Users (like a "friend" relationship in social networking), and should return true if they have the same user_id.

class User < ActiveRecord::Base
  def ==(other)
    other.respond_to?(:email) and self.email == other.email
  end
end

class Contact < ActiveRecord::Base
  def ==(other)
    if other.class == self.class
      self.user == other.user
    elsif other.kind_of? User
      self.user == other
    else
      false
    end
  end
end

As I noted, it works when comparing directly (e.g., one_object == another_object), but my_array_of_objs.include? included_obj always returns false.

+1  A: 

Perhaps its instructive to read this piece of documentation:

http://ruby-doc.org/core/classes/Object.html#M000341

array_of_models.include?(my_object) perhaps doesn't work because == is not used for testing whether collection has the object.It uses equal?.

EDIT

The OP's assertion that even when he overrides == method for his models, array.include?(obj) returns false is not true. Lets see this:

class Event < ActiveRecord::Base
  def ==(that)
    if(that.eventname == self.eventname)
      true
    else
      false
   end
 end
end
>> a << Event.find(1)
=> [#<Event id: 1, eventname: "hemant", foo: nil, created_at: "2009-06-24 21:33:00", updated_at: "2009-06-24 21:33:00">]

>> b = Event.find(2)
=> #<Event id: 2, eventname: "hemant", foo: nil, created_at: "2009-06-24 21:33:04", updated_at: "2009-06-24 21:33:04">

>> a.include?(b)
=> true

Clearly thats not true. But anyways, I think since AR defines == in a particular way, its not a good idea to override it. It may lead to hard to detect bugs.

Hemant Kumar
The documentation for Array.include? says that it uses "==": http://www.ruby-doc.org/core/classes/Array.html#M002226
mipadi
Indeed, it does. I am wondering why then your array of model objects return false (that led to somewhat wrong conclusion on my part).
Hemant Kumar
Array.include? _does_ return false, even when overriding == on the appropriate classes. It's not like I'm lying in my post. ;)
mipadi
Not lying friend. But just some other magic is going on, perhaps one of your plugins or something.
Hemant Kumar
I'm not using any Rails plugins.
mipadi
Its quite possible that the logic that you have put in implementation of `==` method could be cause of the problem. Additionally I wouldn't advise you to override `==` in such a fashion, it *will* cause hard to debug errors.
Hemant Kumar
A: 

Another way to state your question might be "is the == method considered 'final'?". In Java, you can use the 'final' keyword to prevent a method from being overridden in subclasses (you can also apply final to classes, so they can't be subclassed at all). This can be a handy thing in OO design, because some classes or methods are not designed to be overridden. Ruby, of course, does not have 'final', but it's possible that the same concept applies here (maybe you can even implement it using meta-programming).

I remember reading some good articles and/or SO posts on the use of 'final' in OO. I'll edit those in if I find them.

allyourcode