ActiveRecord Text Repräsentation

ActiveRecord Objekte bzw. deren Attribute müssen oft als Text dargestellt werden. Dabei gibt es oft ein bestimmtes Attribut, dass das Model ganz besonders repäsentiert oder eben häufiger als andere Attribute ausgegeben werden muss. Zum Beispiel die Bezeichnung eines Produktes oder der Name des Nutzers.

ActiveRecord Standardtextausgabe

Da in Ruby alle Klasseninstanzen auch alle Methoden von Object erben, hat jedes Objekt auch eine Stringinterpretation. Das gilt genauso für ActiveRecord.
Das Model Person:

rails g model Person name:string && rake db:migrate

hat durch Object#to_s eine Stringrepäsentation:

puts "Hello #{Person.first}"
# => "Hello #<Person:0x000000028bcf70>"

Die Ausgabe des Namens der Person:

puts "Hello #{Person.first.name}"
# => "Hello Christian"

kommt relativ häufig vor, verglichen mit anderen Attributen von Person, da jede Person sich vor allem über den Namen angesprochen/ identifiziert fühlt.

ActiveRecord#to_s überschreiben

Die Standardimplementierung von ActiveRecord#to_s bringt wenig Mehrwert. Daher ist es absolut sinnvoll, im Bedarfsfall #to_s zu überschreiben:

class Person < ApplicationRecord
  def to_s
    name
  end
end

Der Nachricht to_s wird bei der Stringinterpolation immer implizit gesendet:

puts "Hello #{Person.first}"
# => "Hello Christian"

Und auch in den Ruby on Rails templates kann die implizite Stringinterpolation ausgenutzt werden:

# people/show.html.slim
.person = @person
// <div class="person">Christian</div>

Der dazugehörige Test:

require 'spec_helper.rb'
RSpec.describe Person, type: :model do
  subject { Person.new name: 'Christian' }

  describe '#to_s' do
    it 'returns the name' do
      expect(subject.to_s).to eq(subject.name)
    end
  end
end

Textausgabe durch Delegation

Wenn allerdings keine Logik bei der Stringrepräsentation des Objektes enthalten ist, dann ist Überschreiben durch Delegation sogar die elegantere Variante:

class Person < ApplicationRecord
  delegate :to_s, to: :name
end

Zumal es dafür einen Shoulda Matcher gibt, der den Test auf lesbare Weise verkürzt:

require 'spec_helper.rb'
RSpec.describe Person, type: :model do
  subject { Person.new name: 'Christian' }

  it { is_expected.to delegate_method(:to_s).to(:name) }
end