Aliasieren auf Rails Art

Ein Methodenalias kann aus unterschiedlichen Gründen sinnvoll oder sogar notwendig sein:

  1. Um für eine Klasse duck typing zu ermöglichen
  2. Die Bedeutung/ Lesbarkeit einer abgeleiteten Methode zu erhöhen
  3. Wenn das Verhalten einer abgeleiteten Methode erweitert werden soll (im Sinne von Aspektorientierter Programmierung)

In Ruby kann dies auf verschiedene Weise erreicht werden, z. Bsp. durch Module#alias_method.
Zunächst ein Beispiel anhand des Company Models:

rail$ rails g model Company name:string && rake db:migrate

und das dazugehörige Model und einem Alias label auf die Attributmethode name:

class Company < ActiveRecord::Base
  alias_method :label, :name
end

Nur leider funktioniert das so nicht:

Company.new # => NameError: undefined method `name' for class 'Company'

Der Grund ist ganz einfach. Ruby on Rails erstellt Attributzugriffsmethoden dynamisch und bedient sich dazu des method-missing-Ansatzes. Zum Zeitpunkt der Klasseninstantiierung wurden die Zugriffsmethoden allerdings noch nicht erstellt. Wenn allerdings alias_method genau dann versucht, eine Kopie (Alias) der originalen Method zu erstellen, löst das folgerichtig den NameError aus.
Die Lösung ist Module#alias_attribute. Ruby on Rails hat Module erweitert und ermöglicht damit Aliasierung in ActiveRecord. Das Prinzip ist simpel. Es erstellt mittels Metaprogrammierung eine Methode, die die originale Methode aufruft, intern also auf die originale Methode zeigt.
Das Model muss also so aussehen:

class Company < ActiveRecord::Base
  alias_attribute :label, :name
end

und dann:

company = Company.new(name: 'Google')
company.name # => "Google"
company.to_s # => "Google"

Zusätzlich erstellt alias_attribute auch die entsprechenden setter-Methoden:

company = Company.new(name: 'Google')
company.label = 'Apple'
company.name # => "Apple"
company.label # => "Apple"

Da alias_attribute an Module hängt, funktioniert es natürlich für jede Klasse:

class Article
  attr_accessor :name
  alias_attribute :title, :name

  def initialize name
    @name = name
  end
end

Article hat nun zwei weitere Methoden title und title=:

article = Article.new 'Aliasieren auf Rails Art'
article.title # => "Aliasieren auf Rails Art"
article.title = 'ActiveRecord Attribute aliasieren'
article.title # => "ActiveRecord Attribute aliasieren"
article.name # => "ActiveRecord Attribute aliasieren"