18.2 Ruby on Rails
Eine moderne Lösung zur Erstellung von Webanwendungen ist der Einsatz eines spezialisierten Web-Frameworks. Inzwischen stehen zahlreiche Frameworks zur Auswahl, die auf viele verschiedene Sprachen aufsetzen. In diesem Abschnitt wird das Framework Ruby on Rails beschrieben. Es basiert auf der Programmiersprache Ruby, die bereits in Kapitel 9, »Grundlagen der Programmierung«, vorgestellt wurde.
18.2.1 Grundlagen
Das Framework wurde 2004 von dem dänischen Programmierer David Heinemeier Hansson ins Leben gerufen. Es basiert auf dem MVC-Entwurfsmuster (Model, View, Controller), das bereits seit den 70er-Jahren die Grundlage lokaler GUI-Anwendungen bildet. Die drei Aspekte einer MVC-Anwendung bedeuten Folgendes:
- Das Model bildet die zugrunde liegende Datenstruktur: In Rails basiert sie auf einer relationalen Datenbank, wobei jede Tabelle automatisch einer Ruby-Klasse zugeordnet wird.
- Der View ist die Darstellung oder Präsentation der Datenstruktur: Dafür wird eRuby (embedded Ruby) verwendet, das heißt HTML-Code, in den ähnlich wie bei PHP Ruby hineingeschrieben wird. Um den View von komplexer Programmierung freizuhalten, bietet Rails die Möglichkeit, zusätzlich sogenannte Helpers (Helfermethoden) dafür zu erstellen. Rails kann dabei nicht nur statisches HTML rendern, sondern enthält auch eingebaute Unterstützung für Ajax (siehe Kapitel 19, »JavaScript und Ajax«).
- Der Controller schließlich enthält die eigentliche Programmlogik: Wenn der Benutzer Formularelemente im View verwendet, bestimmt der Controller-Code, was mit den Daten des Models geschehen soll.
Ruby on Rails besteht aus verschiedenen Komponenten:
- Active Record ist ein sogenannter Object-Relational Mapper (ORM) und bildet die Rails-Datenbankschnittstelle, die Datenbanktabellen automatisch in Ruby-Klassen abbildet und so die Voraussetzungen für das Model schafft.
- Action Pack, bestehend aus Action Controller und Action View, ist die Grundlage für die Steuerlogik des Controllers und die eRuby-Templates des Views.
- Action Web Service ermöglicht sowohl die Entwicklung eigener Webservices als auch die Programmierung von Clients für bestehende Webservices.
- Action Mailer ist ein umfangreiches Paket für E-Mail-Unterstützung aller Art.
- Active Support schließlich ist die Schnittstelle für beliebige eigene Ruby-Klassen zur Rails-Erweiterung.
Zwei wichtige Designprinzipien von Ruby on Rails sind:
- Don’t Repeat Yourself (DRY) – in aller Regel braucht benötigter Code nur ein einziges Mal für die gesamte Anwendung geschrieben zu werden.
- Convention over Configuration – die meisten Aspekte Ihrer Rails-Anwendungen brauchen nicht in Konfigurationsdateien festgelegt zu werden, sondern stehen automatisch bereit, wenn Sie sich an ein paar Namens- und Verzeichniskonventionen halten.
Rails wird über den Ruby-Paketmanager RubyGems installiert, den Sie gegebenenfalls zuerst von http://www.rubygems.org/ herunterladen und nach der dortigen Anleitung installieren müssen. Diese Schnittstelle benötigen Sie übrigens ebenfalls, um das kurze Beispiel in diesem Abschnitt nachzubauen. Alternativ können Sie auch eine andere von Ruby unterstützte Datenbank verwenden.
Geben Sie (unter Unix als User root) folgende Kommandos ein, um Rails zu installieren:
# gem update -y
# gem install -y rails
Zusätzlich benötigen Sie den Ruby-Treiber für die Datenbank, mit der Sie arbeiten möchten. Der Rails-Standard ist inzwischen SQLite, aber für die nachfolgenden Beispiele wird MySQL verwendet. Der zugehörige Treiber wird über RubyGems wie folgt installiert:
# gem install -y mysql
18.2.2 Ein Praxisbeispiel
Ein wichtiger Bestandteil von Rails sind zahlreiche Generatorskripte, die einen großen Teil des Codes Ihrer Anwendung automatisch erzeugen. Erstellen Sie ein Verzeichnis für Ihre Rails-Anwendungen, wechseln Sie mithilfe von cd in dieses, und geben Sie die folgende Anweisung ein, um eine neue Applikation namens buecher zu erstellen:
> rails buecher
In dieser kleinen Anwendung werden Daten über IT-Fachbücher gesammelt, und Benutzer können Bewertungen und Kommentare dazu abgeben.
Der Generator rails hat die Aufgabe, die Verzeichnisstruktur und diverse grundlegende Skripte der Anwendung zu erzeugen. Wechseln Sie in das neu erstellte Verzeichnis, und schauen Sie sich die verschiedenen Unterverzeichnisse und Dateien an. app enthält die Verzeichnisse models, views, controllers und helpers für die besagten MVC-Komponenten und Helfermethoden. Das Verzeichnis script enthält einige Ruby-Skripte, zum Beispiel weitere Generatoren, den einfachen Webserver WEBrick (praktisch zum Testen Ihrer Anwendung) sowie eine Rails-fähige Variante von irb. In public befinden sich CGI- und Fast-CGI-Skripte, die den Betrieb der Rails-Anwendung über einen externen Webserver (zum Beispiel Apache) ermöglichen. Und ein Verzeichnis namens config enthält die wenigen Konfigurationsdateien, die trotz Convention over Configuration erforderlich sind.
Bevor Sie anfangen können zu programmieren, müssen Sie in die Datei config/database.yml die Zugangsdaten der verwendeten Datenbanken eintragen. Das Format dieser Datei ist YAML[Anm.: Rekursives Akronym für »YAML Ain’t Markup Language«, nicht zu verwechseln mit dem
CSS-
Framework, das dieselbe Abkürzung verwendet und im vorigen Kapitel erwähnt wurde.], ein leichtgewichtiges Klartextformat zur Darstellung unterschiedlicher Datentypen.
Den größten Teil der Arbeit hat Rails dabei bereits erledigt. Vorgegeben sind drei
Datenbanken, deren Namen jeweils aus der Bezeichnung Ihrer Applikation und einem Suffix
bestehen. Im vorliegenden Fall buecher_development für die Entwicklungsarbeit, buecher_test für Unit-Tests sowie buecher_production für den späteren Praxiseinsatz.
Die Umgebungsvariable RAILS_ENV, festgelegt in config/environment.rb, bestimmt den aktuellen Modus und damit die verwendete Datenbank. Der Standardwert ist praktischerweise development. Falls Ihr Datenbankserver MySQL ist, müssen Sie dreimal das adapter ändern und die Zeilen username und password auf einen berechtigten User und sein Passwort setzen. Hier ein Beispiel für die fertige Datei (aus Platzgründen ohne die Kommentare des Originals):
development:
adapter: mysql
database: buecher_development
username: buchuser
password: 93h31M
host: localhost
test:
adapter: mysql
database: buecher_test
username: buchuser
password: 93h31M
host: localhost
production:
adapter: mysql
database: buecher_production
username: buchuser
password: 93h31M
host: localhost
Danach müssen die drei Datenbanken selbst und eventuell der zugehörige User angelegt werden, zum Beispiel im mysql-Client oder phpMyAdmin (Details siehe Kapitel 12, »Datenbanken«).
Das Model
Als Nächstes wird das Model erzeugt, das heißt Datenbanktabellen mit dazu passenden Ruby-Klassen. Verwenden Sie grundsätzlich englische Tabellen- und Klassennamen, denn dies sorgt dafür, dass die Tabellennamen fast immer automatisch den Plural der Klassennamen bilden, sogar bei Ausnahmen wie Klasse Shelf und Tabelle shelves, Fish/fish oder Person/people. Geben Sie die folgenden Zeilen ein, um die Model-Klassen Book und Rating zu erzeugen:
> ruby script/generate model Book
> ruby script/generate model Rating
Active Record enthält eine Komponente namens Migration, die die notwendigen Datenbanktabellen aus Ruby-Code erstellt oder gegebenenfalls wieder entfernt. Bearbeiten Sie zunächst das Skript db/migrate/<Zeitstempel>_create_books.rb, das die Tabelle books zur Klasse Book erzeugt. Das Endergebnis sollte so aussehen:
class CreateBooks < ActiveRecord::Migration
def self.up
create_table :books do |t|
t.column(:isbn, :string)
t.column(:authors, :string)
t.column(:title, :string)
t.column(:topic, :string)
t.column(:publisher, :string)
t.column(:price, :float)
end
end
def self.down
drop_table :books
end
end
Wie Sie sehen, sind die Daten nicht vollständig normalisiert – zum Beispiel werden die Autorennamen als einfacher String gespeichert, was effektives Suchen oder Sortieren nach Autoren verhindert. Diese Vereinfachung erfolgt hier aus Platzgründen. Wenn Sie möchten, können Sie selbst mit Ihrem Wissen aus Kapitel 12, »Datenbanken«, und aus diesem Abschnitt eine vollständig relationale Lösung erstellen.
Die Active-Record-Methode create_table nimmt einen Block entgegen, wobei eine Referenz auf das Tabellenobjekt zur Verfügung gestellt wird, das standardmäßig den Variablennamen t erhält. Die Spaltennamen und Spaltendatentypen werden jeweils als Symbole notiert. Hinter den Kulissen erzeugt die Migration noch automatisch einen ganzzahligen Auto-Increment-Primärschlüssel namens id. Falls Ihr Primärschlüssel einen anderen Datentyp haben soll, müssen Sie explizit eine Spalte namens ID mit dem entsprechenden Typ anlegen.
Nehmen Sie sich als Nächstes die Datei db/migrate/<Zeitstempel>_create_ratings.rb vor, die die Buchbewertungen enthalten soll. Die Methode self.up sollte nach der Bearbeitung so aussehen:
def self.up
create_table :ratings do |t|
t.column(:points, :integer)
t.column(:comment, :string)
t.column(:book_id, :integer)
end
end
Die Spalte book_id ist der Fremdschlüssel, der automatisch den Bezug zum jeweiligen Buch herstellt. Vor der Ausführung der Migration-Skripte muss diese Beziehung zwischen den beiden Tabellen auch auf Klassenebene abgebildet werden. Öffnen Sie dazu die beiden Dateien app/models/book.rb und app/models/rating.rb. Ergänzen Sie sie um je eine Zeile:
class Book < ActiveRecord::Base
has_many :ratings, :order => "points DESC"
end
class Rating < ActiveRecord::Base
belongs_to :book
end
Auch hier kommen die erwähnten Pluralisierungsregeln zum Einsatz. In den Klassendefinitionen wird nun die 1:n-Beziehung zwischen den beiden Tabellen beschrieben, und zwar fast umgangssprachlich: »One book has many ratings, and one rating belongs to one book.« Bei der has_many-Angabe wurde auch gleich eine festgelegte Sortierreihenfolge hinzugefügt, nämlich absteigend nach Punkten. Auf diese Weise können Sie später die fertig sortierten Bewertungen eines Buches mithilfe von book_instanz.ratings auslesen.
Wenn Sie m:n-Beziehungen benötigen, lautet die Methode übrigens has_and_ belongs_to_many. Die dafür nötige Verknüpfungstabelle muss gemäß der Rails-Konvention Tabelle1_Tabelle2 (in alphabetischer Reihenfolge) heißen und die beiden Fremdschlüsselfelder Tabelle1_id und Tabelle2_id enthalten.
Nach diesen Vorbereitungen können Sie die folgende Anweisung eingeben:
> rake db:migrate
rake ist ein Ruby-Automatisierungstool, ähnlich wie Ant oder make. Das Target db:migrate führt die self.up-Methoden der Migration-Skripte in der Reihenfolge ihrer Zeitstempel aus, soweit sie noch nicht bei einem früheren Durchlauf abgearbeitet wurden – dieses Verfahren garantiert die problemlose nachträgliche Erweiterbarkeit. Wenn Sie den Parameter VERSION=<Zeitstempel> angeben, werden die self.down-Methoden (die standardmäßig die betreffenden Tabellen wieder löschen) bis zum angegebenen Zeitstempel aufgerufen, um die Anwendung (zum Beispiel nach missglückten Experimenten) auf einen früheren Zustand zurückzuführen.
Wenn Sie ein paar Bücher eingeben und gleichzeitig den Umgang mit der Active-Record-Syntax für den Datenbankzugriff erlernen möchten, können Sie jetzt Folgendes eingeben:
> ruby script/concole
Nach einiger Zeit erscheint der irb-Prompt. Geben Sie Folgendes ein, um einen Datensatz zur Tabelle buecher hinzuzufügen:
>> Book.create(:isbn => "978-3-8362-1102-4",
?> :authors => "Surendorf, Kai",
?> :title => "UNIX fuer Mac OS X-Anwender",
?> :topic => "Betriebssysteme - Mac OS X",
?> :publisher => "Galileo Press",
?> :price => 39.90)
=> #<Book:0x46e30e4
@errors=#<ActiveRecord::Errors:0x46e1c08 @errors={},
@base=#<Book:0x46e30e4 ...>>, @new_record=false,
@attributes={"isbn"=>"978-3-8362-1102-4", "price"=>39.9,
"title"=>"UNIX fuer Mac OS X-Anwender",
"topic"=>"Betriebssysteme - Mac OS X", "id"=>1,
"publisher"=>"Galileo Press", "authors"=>"Surendorf,
Kai"}, @new_record_before_save=true>
Wie Sie sehen, wird die Klassenmethode create der jeweiligen Model-Klasse eingesetzt. Als Argument erhält sie einen Hash aus Spaltentitel-Wert-Paaren.
Die Active-Record-Entsprechung der SQL-Anweisung SELECT heißt dagegen find. Das Mindestargument ist der Wert eines existierenden Primärschlüsselwertes:
>> b = Book.find(1)
=> #<Book:0x46ae81c @attributes={"isbn"=>"978-3-8362-1102-4",
"price"=>"39.9", "title"=>"UNIX fuer Mac OS X-Anwender",
"topic"=>"Betriebssysteme - Mac OS X",
"id"=>"1", "publisher"=>"Galileo Press",
"authors"=>"Surendorf, Kai"}>
Für die einzelnen Felder des erhaltenen Datensatzes stehen Akzessoren mit den Original-Spaltennamen zur Verfügung. Beispiele:
>> b.authors
=> "Surendorf, Kai"
>> b.isbn
=> "978-3-8362-1102-4"
Auch Wertänderungen sind auf diese Weise möglich. Angenommen, die Bücher-Site erweitert ihr Sortiment auf Nicht-IT-Bücher und muss die Kategorie der bisherigen Bücher daher jeweils um das Präfix »EDV« ergänzen. Dies funktioniert so:
>> b.topic = "EDV - Betriebssysteme - Mac OS X"
>> b.save
Statt eines Indexes können Sie für find auch eines der Symbole :all oder :first angeben. Die Angabe :all findet alle infrage kommenden Datensätze und gibt diese als Array zurück (selbst wenn nur ein Datensatz vorhanden ist), während :first den ersten Treffer liefert. Zusätzliche Hash-Schlüssel wie :conditions oder :order bilden die SQL-Klauseln WHERE zur Angabe von Kriterien beziehungsweise ORDER BY zum Sortieren ab.
Eine andere Möglichkeit, ohne Aufwand eine Administrationsoberfläche für eine Tabelle (oder einen erweiterbaren Prototyp für die Anwendung selbst) zu erstellen, ist das sogenannte Scaffolding. Ein Generator erzeugt dabei das Grundgerüst (Scaffold) für die Datenbankaufgaben, die sich mit CRUD (Create, Read, Update, Delete) zusammenfassen lassen. Verlassen Sie irb mit exit, und geben Sie folgende Anweisung ein, um ein Scaffold für die Tabelle books zu erstellen:
> ruby script/generate scaffold book
Um auszuprobieren, welche Teile der Anwendung damit automatisch generiert wurden, können Sie danach zum ersten Mal den Testserver starten:
> ruby script/server
Öffnen Sie anschließend einen Browser, und geben Sie darin die URL http://localhost:3000/ ein. Diese allgemeine Startseite enthält einige Informationen und Links zu Rails. Rufen Sie danach die Adresse http://localhost:3000/books auf. Das bereits vorhandene Buch wird in einer schmucklosen Tabelle angezeigt. Daneben finden Sie die Links Show, Edit und Destroy zum Lesen, Ändern und Löschen des Datensatzes; Letzteres verwendet eine JavaScript-Sicherheitsabfrage. Unter der Tabelle befindet sich ein weiterer Link mit der Beschriftung New Book. Klicken Sie ihn an, geben Sie die Daten eines weiteren Buches ein, und klicken Sie dann auf die Schaltfläche Create. Wiederholen Sie dies mit drei bis vier Büchern.
Ein praktischer Vorteil des Scaffoldings ist, dass Sie sich den erzeugten Quellcode unter app/controller/books beziehungsweise app/view/books anschauen und so einige der Tricks erlernen können, die Ihnen in Rails viel Schreibarbeit ersparen.
Beenden Sie den Server mit + . Nun geht es darum, das Endbenutzer-Frontend der Anwendung zu erstellen, also Seiten zum Erstellen und Lesen der Buchbewertungen.
Controller und Views
Geben Sie zuerst die folgende Konsolenanweisung ein, um die Grundstruktur zweier Controller und Views für das Model Book zu erstellen:
> ruby script/generate controller Book list rate
Öffnen Sie nun die Datei app/controllers/book_controller.rb. Sie enthält zwei leere Methoden namens list beziehungsweise rate. Ergänzen Sie zunächst die Methode list so, dass sie ein nach Titeln sortiertes Array aller Book-Datensätze liefert:
def list
@books = Book.find(:all, :order => "title ASC")
end
Die hier verwendete find-Syntax dürfte Ihnen bereits vertraut sein. Wenn Sie die Ruby-Einführung in Kapitel 9, »Grundlagen der Programmierung«, gelesen haben, werden Sie wissen, dass Variablen, deren Namen mit @ beginnen, Instanzvariablen sind. Genau auf diese können Sie in dem zugehörigen View zugreifen.
Laden Sie als Nächstes die Datei app/views/list.rhtml. Sie enthält bisher nur zwei kurze Infozeilen. Löschen Sie diese, und fügen Sie stattdessen folgenden Code ein:
<html>
<head>
<title>Bücherbewertung</title>
</head>
<body>
<h1>Bücherbewertung</h1>
Wählen Sie hier ein Buch, für das Sie
Bewertungen lesen oder abgeben möchten:
<ul>
<%
@books.each { |b|
%>
<li><%= link_to(b.title,
{ :action => "rate",
:id => b.id }) %> von
<%= b.authors %></li>
<%
}
%>
</ul>
</body>
</html>
Es handelt sich, wie bereits erwähnt, um eine eRuby-Datei. Sie kann zwischen den Zeichen <% und %> Ruby-Programmlogik enthalten. Gehen Sie aber zurückhaltend damit um; die eigentliche Programmierung gehört in den Controller! Ausgabebefehle funktionieren in eRuby nicht. Wenn ein Ausdruck im erzeugten HTML-Code landen soll, muss er stattdessen zwischen <%= und %> gesetzt werden.
Die Liste wird von dem Iterator each umschlossen, der jedes Element des Arrays durchgeht. Ohne die eRuby-Kennzeichen sieht dieser Teil so aus:
@books.each { |b|
# b ist hier jeweils der aktuelle Book-Datensatz
}
Der Buchtitel wird mithilfe der Methode link_to als Link ausgegeben. Ihr erstes Argument ist ein String mit dem Link-Text – hier b.title, also der Titel des aktuellen Buches. Danach folgt ein Hash mit der verlinkten Controller-Action und eventuell weiteren Parametern; im vorliegenden Fall wird die ID des aktuellen Buches mitgeschickt.
Zum Schluss wird noch der Inhalt von b.authors hinzugefügt, um die Autoren des Buches zu nennen.
Falls Sie möchten, können Sie nun noch einmal den Server starten und die URL http://localhost:3000/book/list eingeben.[Anm.: Beachten Sie den Singular (book) im Unterschied zum Plural der Scaffold-Actions.] Es sollte eine Liste der bisher erfassten Bücher erscheinen. Klicken Sie einen der Links an, wird eine URL wie http://localhost:3000/rate/1 aufgerufen, und der Standardinhalt des Views rate erscheint.
Es wäre sicherlich wünschenswert, den View list als Startseite der Anwendung zu definieren, die sich mit http://localhost:3000/book aufrufen lässt. Das geht sehr einfach. Fügen Sie in book_controller.rb die folgende Methodendefinition hinzu:
def index
redirect_to(:action => "list")
end
redirect_to leitet automatisch zur angegebenen Action weiter. Wenn Sie stattdessen eine »richtige« Startseite haben möchten, müssen Sie in views eine Datei namens index.rhtml erstellen.
Als Nächstes kommt die Action rate an die Reihe; sie ist ein bisschen komplexer. Ergänzen Sie zunächst die Controller-Methode wie folgt:
def rate
# Das aktuelle Buch auslesen
@book = Book.find(params[:id])
# Das Array der zugehörigen Ratings auslesen
@ratings = @book.ratings
# Evtl. durchschnittliche Punktzahl berechnen
if @ratings.length > 0
sum = 0
@ratings.each { |r|
sum += r.points
}
@avg = (sum.to_f / @ratings.length).round
end
end
Der find-Aufruf dürfte klar sein. Als Argument dient der von list übergebene Parameter id. Die nächste Zeile zeigt die volle Stärke des ORM in Rails: Die Ratings sind nichts weiter als ein Attribut einer Book-Instanz und lassen sich als solches auslesen. Zum Schluss wird, falls bereits Bewertungen vorhanden sind (Array#length liefert die Anzahl der Elemente), der auf eine Ganzzahl gerundete Durchschnitt aus allen Punktzahlen berechnet und in @avg gespeichert.
Bevor der View erstellt wird, soll ein Helper geschrieben werden. Die Punktzahl soll nämlich nicht numerisch erscheinen, sondern als entsprechende Anzahl von Sternchen. Öffnen Sie dazu die Datei app/helpers/application_helper.rb, deren Methoden allen Actions Ihrer Anwendung zur Verfügung stehen. Ergänzen Sie den Code des Moduls ApplicationHelper wie folgt:
module ApplicationHelper
def show_points(points)
str = ""
points.times {
str += "*"
}
str
end
end
Ersetzen Sie nun den Inhalt von app/views/rate.rhtml durch folgendes eRuby-Dokument:
<html>
<head>
<title>Bücherbewertung:<%= @book.title %></title>
</head>
<body>
<!-- Teil 1: Buchinfos -->
<h1>Bewertungen für das Buch<br />
»<%= @book.title %>«</h1>
<%
if flash[:notice]
%>
<p><i><%= flash[:notice] %></i></p>
<%
end
%>
<h2>Infos zum Buch</h2>
<b><%= @book.title %></b><br />
von <%= @book.authors %><br />
ISBN: <%= @book.isbn %><br />
Verlag: <%= @book.publisher %><br />
Preis: <%= sprintf "%.2f", @book.price %>
<!-- Teil 1: Ausgabe der Bewertungen -->
<%
if @ratings.length > 0
%>
<h2>Bewertungen</h2>
Durchschnittliche Punktzahl:
<%= show_points(@avg) %><br /><br />
<table width="480" border="0" cellpadding="4">
<tr>
<th>Punkte</th>
<th>Kommentar</th>
</tr>
<%
@ratings.each { |r|
%>
<tr>
<td><%= show_points(r.points) %></td>
<td><%= r.comment %></td>
</tr>
<%
}
%>
</table>
<%
end
%>
<!-- Teil 3: Formular für eigene Bewertungen -->
<h2>Ihre Bewertung?</h2>
<%= form_tag(:action => "create_rating", :id => @book.id)
%>
<table border="0" cellpadding="4">
<tr>
<td>Bewertung</td>
<td>
<%
6.downto(1) { |i|
%>
<%= radio_button(:rating, :points, i) %>
<%= i %>
<%
}
%>
</td>
</tr>
<tr>
<td>Kommentar</td>
<td>
<%= text_area(:rating, :comment, :rows => 4, :cols => 50) %>
</td>
</tr>
<tr>
<td colspan="2">
<%= submit_tag("Bewertung abschicken") %>
</table>
<%= link_to("Zur Liste", :action => "list") %>
</body>
</html>
Im ersten Teil werden zunächst die Datenfelder von @book angezeigt. Die Prüfung und eventuelle Anzeige von flash[:notice] betrifft das Rails-eigene Nachrichtensystem: Was Sie in dem Hash flash speichern, können Sie bei der nächsten HTTP-Anfrage wieder auslesen. Die Angabe :notice ist der Standardschlüssel für einfache Meldungen. Wie Sie im Folgenden sehen werden, wird flash[:notice] über den erfolgreichen Eintrag einer neuen Bewertung informieren.
Der zweite Teil des Skripts prüft zunächst, ob Bewertungen vorhanden sind (Kriterium @ratings.length > 0) und zeigt diese gegebenenfalls an. In diesem Bereich befindet sich keine Anweisung, die noch nicht erläutert wurde. Beachten Sie, dass die Helper-Methode show_points zweimal aufgerufen wird – einmal für den Durchschnitt und einmal innerhalb der Schleife für jede einzelne Punktzahl.
Interessant wird es im dritten Teil. Darin wird auf Rails-konforme Art ein Formular zur Eingabe einer neuen Bewertung aufgebaut. Die Methode form_tag erzeugt, wie ihr Name vermuten lässt, das HTML-Tag <form> mit den angegebenen Parametern. Eine Action, die einen neuen Datensatz erzeugt, sollte stets create_klassenname heißen, hier also create_rating.
Die verwendeten Formularelemente, radio_button und text_area, folgen ebenfalls einer Syntaxkonvention: Der erste Parameter ist der Model-Name und der zweite der jeweilige Spaltenname, beide in Symbolschreibweise. Im erzeugten HTML-Code werden daraus automatisch die Parameter id="model_spalte" und name="model[spalte]", zum Beispiel:
<textarea cols="50" id="rating_comment"
name="rating[comment]" rows="4"></textarea>
Beim radio_button kommt naturgemäß noch der Wert für die jeweilige Auswahl hinzu, bei text_area optional :rows und :cols.
Den Abschluss des Formulars bilden ein Submit-Button, erzeugt durch die Methode submit_tag("Beschriftung"), sowie das schließende </form>-Tag.
Als Letztes müssen Sie noch die Controller-Methode create_rating hinzufügen. Es handelt sich um die folgende Methodendefinition in app/controllers/book_controller.rb, sinnvollerweise unmittelbar vor dem abschließenden end:
def create_rating
@rating = Rating.new(params[:rating])
@book = Book.find(params[:id])
@book.ratings << @rating
flash[:notice] = "Bewertung gespeichert."
redirect_to(:action => "rate", :id => params[:id])
end
Zunächst wird aus dem Formulardaten-Array rating eine neue Instanz der Klasse Rating erzeugt. Die nächste Zeile sucht das passende Buch heraus. Der Operator << fügt die Bewertung anschließend als neuen Datensatz ein. Die erwähnte Nachricht wird in flash[:notice] geschrieben, und zum Schluss erfolgt ein Redirect zur Action rate mit derselben Buch-ID, sodass der Kommentar und die Meldung unmittelbar gelesen werden können.
Nun können Sie die fertige Anwendung testen. In der Praxis sollten Sie sich als Nächstes um das Layout in Form passender CSS-Dateien kümmern. Danach erfolgt das Deployment der Applikation in eine echte Produktionsumgebung, die aber über den Schwerpunkt dieser Einführung hinausgeht. Geben Sie in Ihre bevorzugte Suchmaschine die Stichwörter »Mongrel« (leistungsfähiger Ruby-Webserver) und »Capistrano« (Rails-Deployment-Automatisierer) ein. In Anhang C finden Sie zudem weiterführende Literaturtipps.
Ihr Kommentar
Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.