ActiveRecord Gleichheit - beschrieben

ActiveRecord Gleichheit - nicht offensichtlich

In Ruby besteht ein deutlicher Unterschied beim Vergleichen von Objekten zwischen Objektgleichheit (== und eql?) und Objektidentität (equal?). Beides ist an der Klasse Object definiert. Für ableitende Subklassen gilt: Objektgleicheit kann überschrieben werden, Objektidentität allerdings nicht.
Das gilt auch für ActiveRecord.
Ein einfaches Modell Beispiel:

class Language < ActiveRecord::Base
end

Ein neues Objekt und sein Duplikat sehen zunächst einmal gleich aus:

ruby = Language.new name: 'Ruby'
# => #<Language id: nil, name: "Ruby">
ruby.dup
# => #<Language id: nil, name: "Ruby">

Es handelt sich auch wie erwartet nicht um identische Objekte:

ruby.equal? ruby.dup
# => false

Die Objektgleichheit soll in einem RSpec Test nachgewiesen werden.

ActiveRecord Gleichheit - getestet

Es wird erwartet, dass eine Sprache und dessen Duplikat nicht nur gleich aussehen, sondern auch gleich sind:

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

Der Test bestätigt dies allerdings nicht:

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

Der Grund für dieses Verhalten ist in der Implementierung von ActiveRecord selber zu finden.

ActiveRecord Gleichheit - implementiert

Die Methode == wurde in ActiveRecord überschrieben:

# 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

Laut ActiveRecord sind Modell Objekte erst gleich, wenn sie eine definierte ID haben und diese auch gleich ist.

ActiveRecord Gleichheit - neu definiert

Dieses Standardverhalten läßt sich sicherlich ändern. Eine entsprechende Implementierung der Objektgleichheit:

class Language < ActiveRecord::Base
  def == comparison_object
    self.attributes == comparison_object.attributes
  end
end

Demnach sollen Sprachen dann gleich sein, wenn deren Attribute (name) gleich sind.
Der RSpec Test bestätigt dann das Verhalten:

Finished in 0.00517 seconds (files took 0.75063 seconds to load)
1 example, 0 failures