Webentwicklung ist oft von wüsten Hacks beherrscht, in denen Programmcode und HTML bunt gemischt sind. Dabei gibt es mittlerweile Ansätze, die auch in diesem Bereich strukturierte Entwicklung ermöglichen. So implementiert etwa Rails[1] in Ruby ein klassisches Model-View-Controller-Framework (MVC), wie es beispielsweise die Smalltalk-Welt schon lange verwendet. Es isoliert die eigentliche Datenverarbeitung im Modell weitgehend vom GUI- bezogenen Manipulationscode (Controller) und dem reinen Darstellungscode (View), siehe Abbildung 1.
Abbildung 1: Schema der Model-View-Controller-Idee. Die Datenflüsse sind dabei nicht für alle Anwendungen gleich, sondern können auch etwas anders aussehen als hier.
Das Modell kapselt die Daten und die Methoden, die sie bearbeiteten. Das kann vom Arbeiten mit einer Datenbanktabelle bis zu einer Modellierung des Steuerungsprozesses eines Großkonzerns reichen. Im View wird das Modell ganz oder teilweise visualisiert. Die Controller zeichnen dafür verantwortlich, die Zustandsänderungen am Modell auszulösen, indem sie zum Beispiel auf Benutzereingaben reagieren.
Durch diese Aufteilung wird die Applikationslogik von der äußeren Form der Darstellung und Art der Benutzerführung unabhängig. Einige Pakete unterstützen zwar angeblich MVC, was sich bei näherer Betrachtung aber oft nur als Drittelwahrheit entpuppt, siehe Kasten "Rails und Java-Struts ...".
|
Obwohl das Framework Struts [3] hauptsächlich für Controller vorgesehen ist, ziehen es Java-Fans gerne als Paradebeispiel für MVC-Programmierung heran. Rails dagegen bietet etwas für alle drei Begriffe: Model, View und Controller.
In Struts sind alle Aktionen in einer XML-Datei zu notieren (und damit auch indirekt die Rückgabewerte). Rails erledigt das selbstständig durch Reflexion. Daneben versucht der ActionController von Rails lesbare URLs wie »/customers/show/154« zu erzeugen. Alle Aktionen in Struts müssen Klassen sein, bei Rails genügen Methoden, die in der erwähnten Basecamp-Applikation durchschnittlich fünf Zeilen lang sind. Der »ActionController« in Rails unterstützt Layouts direkt, während Struts dafür weitere XML-Dateien braucht.
Der »ActionController« ist überhaupt hilfreich dafür, mit Scaffolding eine Modellklasse online zu bringen. Er unterstützt außerdem Filter und Interzeptoren, für Struts gibt es in dieser Richtung lediglich ein Forschungsprojekt namens SAIF [4]. Wer umsteigen will, findet in Tabelle 1 die wichtigen Java-Bibliotheken entsprechenden Ruby-Projekte.
|
Software aus der Praxis
Rails bringt fürs Modell das Paket Active Record mit, das objektrelationales Mapping realisiert. Das Modul Action Pack teilt Webrequests in Controller- und View-spezifische Teile auf. Damit ist Rails bestens geeignet für die Entwicklung datenbankbasierter Webapplikationen wie der Projektmanagement-Software Basecamp [2], die mit Rails programmiert wurde, siehe Kasten "Interview mit dem Rails-Autor". Diese Geschichte aus der Praxis merkt man Rails beim Arbeiten an, da es viele Probleme vermeidet, die bei Frameworks vom grünen Tisch unschön auffallen.
Rails baut auf das DRY-Konzept (Don\'t repeat yourself - Wiederhol dich nicht, auch bekannt als Once and only once). Denn schreiben Entwickler den mehr oder weniger gleichen Code innerhalb eines Programms immer wieder, ist das ein Zeichen schlechten Designs.
Konfigurationsdateien ersetzt Rails weitgehend durch Reflexion und Laufzeit-Erweiterungen, weswegen die sonst übliche XML-Konfigurationsorgie entfällt. Ein weiterer Effekt ist, dass die Auswirkungen von Änderungen sofort sichtbar werden, da zeitaufwändigen Kompiliervorgänge wegfallen.
Rails verfügt auch über eingebaute Funktionalität für allgemeine und Unit-Tests, zur Dokumentation dient RubyDoc. Das größte Plus ist, dass alle drei Bestandteile des MVC-Entwurfsmusters in Ruby geschrieben sind.
Wenn sich Entwickler an die Rails-Konventionen halten (Zuordnung zwischen Datenbank- und Objektstrukturen), bringt das so genannte Scaffolding (Gerüst bauen) schnell ein Projekt online. Auf Knopfdruck stehen die nötigen Funktionen zur Verfügung, beispielsweise das Hinzufügen, Bearbeiten und Löschen von Objekten.
Objekte in die Datenbank
Die Beispielanwendung orientiert sich am Tutorial der Ruby-Homepage, um eine einfache Webapplikation im Ordner »/var/www/railsdemo« zu erstellen. Zunächst legt Rails das Grundgerüst von Dateien an:
rails /var/www/railsdemo
Dieser Befehl überschreibt eine bereits existierende Rails-Anwendung in dem genannten Verzeichnis, will also mit Vorsicht verwendet sein. Eine MySQL-Datenbank nimmt die Beispieltabelle auf, siehe Listing 1.
01 CREATE DATABASE rails_production;
02 USE rails_production;
03
04 CREATE TABLE people (
05 id int(10) unsigned NOT NULL auto_increment,
06 name varchar(50) NOT NULL default '',
07 street1 varchar(70) NOT NULL default '',
08 street2 varchar(70) NOT NULL default '',
09 city varchar(70) NOT NULL default '',
10 state char(2) NOT NULL default '',
11 zip varchar(10) NOT NULL default '',
12 PRIMARY KEY (id),
13 KEY name (name)
14 ) TYPE=MyISAM AUTO_INCREMENT=2 ;
15
16 INSERT INTO people VALUES (1, 'Superman', '123 Somewhere', '', 'Smallville', 'KS', '123456')
|
Den Zugriff auf die Datenbank legt die Yaml-Datei »config/database.yml« fest, die in einem sprachunabhängigen Format Daten serialisiert:
production:
adapter: mysql
database: rails_production
host: localhost
username: root
password:
Der Eintrag für die Testdatenbank sieht analog aus. Im wirklichen Leben sollte man allerdings weder als Administrator noch ohne Kennwort arbeiten.
Das Skript »new_controller« erzeugt einen neuen Controller, dessen Name mit einem Großbuchstaben beginnen soll. Nach dem Controller-Namen folgen die gewünschten Views:
./script/new_controller Friends list display new edit
Der View-Name »process« ist dabei zu vermeiden, da Rails ihn für interne Zwecke verwendet. Auf ähnliche Weise entsteht ein neues Modell:
./script/new_model Person
In der Regel ermittelt Rails die zugrunde liegende Datenbanktabelle selbst. Bei Bedarf akzeptiert das Skript den Tabellennamen hinter dem Modell:
./script/new_model Person people
Um die Konfiguration der Rails-Anwendung zu testen, führt der Programmierer im Applikationsverzeichnis »rake« aus, das Ruby-Make. Dabei können je nach Systemkonfiguration verschiedene Probleme auftreten:
-
Wenn sich »mysql.sock« an einer unüblichen
Position befindet, kann man entweder »mysql.rb«
anpassen (in einem Verzeichnis der Art »/usr/lib
/ruby/gems/1.8/gems/active-record-0.9.5/lib/active_record/vendor«)
oder die Umgebungsvariable »MYSQL_UNIX_PORT«
setzen.
-
Die Zugangsberechtigungen für die Datenbank reichen
möglicherweise nicht aus.
-
Die Skripte erwarten als Ruby-Pfad
»/usr/local/bin/ruby«. Befindet es sich an einem
anderen Ort, legt man entweder einen Symlink an oder ersetzt den
Pfadnamen in den Skripten.
Um die Webapplikation fertig zu stellen, ist noch »app/views/friends/display .rhtml« entsprechend auszufüllen:
<html>
<body>
<h1>Friends#display</h1>
<p>Zeigt einen Personen-Eintrag an</p>
<p>
<%= @person.name %><br />
<%= @person.street1 %><br />
<%= @person.street2 %><br />
<%= @person.city %><br />
<%= @person.state %><br />
<%= @person.zip %><br />
</p>
</body>
</html>
Das sieht ähnlich aus wie in Erb, der in HTML eingebetteten Ruby-Variante. Die HTML-Seite zeigt die Daten an, die das Modell zur Verfügung stellt. Das bezeichnete Objekt »@person« muss der Controller definieren und erzeugen. Dies geschieht in der Datei »app/controllers/friends_controller.rb«, die schon die Zugriffsmethoden enthält. Das Modell selbst bindet man mit der Anweisung »require« ein:
require 'person'
Nun kann die Webapplikation über die Active-Record-Klasse auf die Datenbank zugreifen. Wird die Methode »display« durch folgenden Code ersetzt, holt sie den ersten Eintrag aus der Tabelle und schreibt ihn in die Instanzvariable »@person« der View-Klasse:
def display
@person = Person.find(1)
end
Auch die generierte Definition der Model-Klasse in »app/models/person.rb« ist einen Blick wert:
require 'active_record'
class Person < ActiveRecord::Base
end
Die Oberklasse »ActiveRecord::Base« übernimmt die meiste Arbeit, indem sie einfache Zugriffsmethoden auf die Datenbank definiert. Der Tabellenname ergibt sich dabei als die Pluralform des Klassennamens, in englischer Sprache natürlich. Das ist ganz ernst zu nehmen und bezieht sich auch auf unregelmäßige Pluralformen, zum Beispiel Men zur Einzahl Man oder eben People zu Person! Die Klassendokumentation geht ausführlich darauf ein [8].
Nun ist unter »http://localhost/rails/friends/display« das Ergebnis der Bemühungen zu sehen. Diese URL enthält sowohl den Controller (»friends«) als auch die Action (»display«).
|
Für Benutzer des Mac OS X (ab 10.3) gibt es zwei Videos[5], die den Installationsprozess vorführen und auch für Linux-User sehenswert sind. Sehr bequem ist die Installation über Rubygems[6] (mindestens Version 0.7), einem Paketmanagement-Tool für Ruby:
gem install rails
Wenn eine neue Version von Action Pack oder Active Record herauskommt, reicht für die Aktualisierung ein einfaches »gem update« aus. Zu beachten ist in jedem Fall, dass für das Backend die Datenbankanbindungen vorhanden sind, wobei MySQL und PostgreSQL in Frage kommen. Für die Installation unter Debian sind wegen der fein unterteilten Pakete viele Voraussetzungen zu erfüllen (siehe auch[7]): irb1.8, libbigdecimal-ruby1.8, libdbm-ruby1.8, libdl-ruby1.8, libdrb-ruby1.8, liberb-ruby1.8, libgdbm-ruby1.8, libiconv-ruby1.8, libopenssl-ruby1.8, libpty-ruby1.8, libracc- runtime-ruby1.8, libreadline-ruby1.8, librexml-ruby1.8, libruby1.8, libruby1.8-dbg, libsdbm-ruby1.8, libsoap-ruby1.8, libstrscan-ruby1.8, libsyslog-ruby1.8, libtest-unit-ruby1.8, libwebrick-ruby1.8, libxmlrpc-ruby1.8, libyaml-ruby1.8, libzlib-ruby1.8, rdoc1., ri1.8 ruby1.8, ruby1.8-dev, ruby1.8-elisp und zu guter Letzt ruby1.8-examples. Apache einstellen
Falls noch nicht geschehen, ist das Rewrite-Modul für Apache zu aktivieren:
# a2enmod
Which module would you like to enable?
Your choices are: ... rewrite ...
Module name? rewrite
Module rewrite installed; run /etc/init.d/apache2 force-reload to enable.
Um eine Rails-Applikation in »/var/www/ myapp« laufen zu lassen, fehlt in der Apache-Konfigurationsdatei ein Eintrag für den virtuellen Host, der ungefähr so aussieht:
<VirtualHost *:80>
ServerName rails
DocumentRoot /var/www/railsdemo/public
ErrorLog /var/www/railsdemo/log/apache.log
<Directory /var/www/railsdemo/public/>
Options ExecCGI FollowSymLinks
AllowOverride all
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
Weitere Einstellungen finden sich in der Htaccess-Datei der Webapplikation, im Beispiel »/var/www/railsdemo/public/.htaccess«. Nun muss Root noch Apache aktualisieren und die Konfiguration damit neu laden:
/etc/init.d/apache2 force-reload
|