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