Diskussion über Enumerations in Ruby on Rails

29.03.2016 17:17

Seit der Version 4.1 bringt Ruby on Rails die Möglichkeit mit Enumerations in Modellen zu definieren. Noch gibt es einige Tücken und andere Dinge die es bei der Anwendung zu beachten gibt.

Enumerations oder auf Deutsch Aufzählungstypen sind ein praktisches Werkzeug, um einen Zustand mit vordefinierten Werten zu beschreiben. Dazu ein Minimalbeispiel, um das Thema zu veranschaulichen:

class Article < ActiveRecord::Base
  enum status: { drafted: 0, published: 1, protected: 2 }
end

Der stellvertretende Wert, beispielsweise für den Status published die Zahl 1, wird von Rails in der Datenbank in einer Spalte des Modells gespeichert. Über eine Migration wird diese dem Schema hinzugefügt:

class AddStatusToArticles < ActiveRecord::Migration
  def change
    add_column :articles, :status, :integer, default: 0, null: false
  end
end

Eine allgemeine Anleitung für die Anwendung von Enumerations gibt es in der API-Dokumentation.

Diskussion der Implementation

Dadurch, dass nur der stellvertretende Wert in der Datenbank gespeichert wird, fehlt eine Definition des Inhalts durch einen passenden Datentyp. Die Datenbank kann keine vollständige Validierung der Daten durchführen und man läuft dabei Gefahr, dass die Daten inkonsistent werden.

Im Minimalbeispiel werden in der Applikation drei Zustände definiert. Es gibt keinen Hinderungsgrund um in der Spalte status einen Zustand zu definieren, der die Applikation nicht kennt. Hier könnten Fehler entstehen.

Dieses Problem lässt sich durchaus beheben:

  1. In vielen Datenbanksystemen lassen sich Enumerations als Datentypen definieren. Beispielsweise PostgreSQL und MySQL haben diese Fähigkeit. Rails interpretiert diesen Datentyp als String, womit einer Verwendung nichts im Wege steht. Leider ist die Funktionalität nicht so in Rails integriert, wie man sich das vielleicht wünscht.

  2. Die Enumeration könnte in der Datenbank als zusätzliche Tabelle abgebildet werden. In Rails wäre dies auch mit den verfügbaren Mitteln einfach umsetzbar.
    ERD

  3. Es könnte auch definiert werden, dass nur eine Applikation auf die Datenbank zugreift. Diese spielt den Gate Keeper und lässt fremde Applikationen nur über die API auf die Daten der Datenbank zugreifen. Die Applikation müsste für sich selbst sicherstellen, dass die Datenbank konsistent bleibt.

Übersetzung der Zustände (i18n)

Werden die Zustände irgendwo in der Applikation dem Benutzer angezeigt, wird oftmals eine Übersetzung dieser notwendig. Dazu kann im Model folgende Methode definiert, welche die Namen der Zustände mittles i18n-API von Rails übersetzt:

def self.translated_statuses
  hash = {}
  statuses.map do |key, _|
    hash.store(I18n.t("activerecord.attributes.#{model_name.i18n_key}.statuses.#{key}"), key)
  end
  hash
end

Mit article_form.select :status, Article.translated_statuses wird mit Hilfe der definierten Methode ein Dropdown-Menü befüllt.

Fazit

Ganz so sauber ist die Implementierung von Enumerations in Ruby on Rails noch nicht. Vor allem eine eingebaute Möglichkeit die Zustände zu übersetzt fehlt.