Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

ActiveRecord::Base#== is non-commutative when comparing a loaded versus initialized object

#3120

If you compare a fully loaded model object with an initialized object of the same type and with the same id, the result of the == operator depends on order of operands. For example:

>> loaded_object = MyModel.last
=> #< MyModel id: 842437485, user_id: 11, ...>
>> initialized_object = MyModel.new
=> #< MyModel id: nil, user_id: nil, ...>
>> initialized_object.id = loaded_object.id
=> 842437485
>> initialized_object == loaded_object
=> true
>> loaded_object == initialized_object   # (!)
**=> false**

It's clear from the implementation why this is the case:

2811:       def ==(comparison_object)
2812:         comparison_object.equal?(self) ||
2813:           (comparison_object.instance_of?(self.class) &&
2814:             comparison_object.id == id &&
2815:             ***!comparison_object.new_record?***)
2816:       end

I can't imagine that the intent was a non-commutative equality operator.

This could be resolved either by ensuring that new records either never or always compare equal to records with the same id. However I'm not sure what the intent is. My instinct is "always" (i.e., removing the "&& !comparison_object.new_record?"), but I'm not totally sure of the intent.

I'm happy to submit a patch and the requisite tests if I can get some input on which way the fix should go...

Reported by fritz · August 31st, 2009 @ 07:02 AM

State: resolved
Milestone: none
Assigned to: nobody
Importance: none

Activity

  1. fritz
    fritz

    Followup: we've been using the following monkeypatch locally and it seems to work well. Note the restriction on nil ids.

    module ActiveRecord
      class Base
        def ==(comparison_object)
          comparison_object.equal?(self) ||
            (comparison_object.instance_of?(self.class) &&
             comparison_object.id == id &&
             !id.nil?)
        end
      end
    end
    

    September 1st, 2009 @ 05:53 AM

  2. CancelProfileIsBroken
    CancelProfileIsBroken
    • Tag changed from active_record, base, comparison, equality to active_record, base, bugmash, comparison, equality

    September 25th, 2009 @ 12:38 PM

  3. Rizwan Reza
    Rizwan Reza
    • Tag changed from active_record, base, bugmash, comparison, equality to active_record, base, comparison, equality
    • State changed from new to resolved

    This is fixed in 2-3-stable or 2.3.5.

    January 21st, 2010 @ 07:05 AM