Händler-Anwendung (Ruby-on-Rails-Beispiel): Unterschied zwischen den Versionen
Kowa (Diskussion | Beiträge) |
Kowa (Diskussion | Beiträge) |
||
| Zeile 199: | Zeile 199: | ||
<source lang="bash"> | <source lang="bash"> | ||
cd /web/rails/haendler | cd /web/rails/haendler | ||
ruby script/generate scaffold | ruby script/generate scaffold ein_haendler name:string adresse:string | ||
ruby script/generate scaffold | ruby script/generate scaffold ware typ:string bezeichnung:string | ||
rake db:migrate | rake db:migrate | ||
rake db:migrate RAILS_ENV="production" | rake db:migrate RAILS_ENV="production" | ||
Version vom 10. Dezember 2008, 09:40 Uhr
Dieser Artikel wird derzeit von einem Autor gründlich bearbeitet. Die Inhalte sind daher evtl. noch inkonsistent.
Zweck
Anhand einer einfachen Web-Anwendung sollten die Prinzipien von Ruby on Rails erläutert werden.
Diesem Beispiel liegt die Händler-Datenbank zugrunde, die Wolfgang Kowarschick in der Vorlesung Datenmanagement II (Multimedia-Datenbanksysteme)[1] als Beispiel einsetzt:
Voraussetzungen
Folgende Softwarepakete wurden installiert:
- Apache 2.2.x
- PostgreSQL 8.x (am Besten zusammen mit einem Frontend wie z.B phpPgAdmin)
- Ruby 1.8.7
- Ruby on Rails 2.2
Ruby on Rails wurde zusammen mit einigen Erweiterungs-Modulen mit Hilfe des Ruby-Paketmanagers gem installiert:
RUBY_DIR=...; export RUBY_DIR
POSTGRESQL_DIR=...; export POSTGRESQL_DIR
gem install rails --include-dependencies
gem install postgres -r -- --with-pgsql-dir=${POSTGRESQL_DIR}
gem install passenger
${RUBY_DIR}/bin/passenger-install-apache2-module
Auf dem Test-Server steht in der Datei /etc/hosts ein
Eintrag für den gewünschten virtuellen Server, auf dem die Anwendung laufen soll (die IP-Adresse
und der Rechnernamen können natürlich angepasst werden):
192.168.0.161 haendler.kowa haendler
Diese IP-Adresse ist einem echten oder virtuellen Rechner zugeordnet.
Unter SuSE 11 kann man einem Rechner ganz einfach mehrere IP-Adressen zuordnen.
Man trägt die neue IP-Adresse in die Datei /etc/sysconfig/network/ifcfg-eth0 ein:
IPADDR_haendler='192.168.0.161'
NETMASK_haendler='255.255.255.0'
Danach muss der Netzwerkzugriff neu gestartet werden:
/etc/init.d/network restart
Ein PostgreSQL-Benutzer mit Namen haendler wurde (z.B. mit Hilfe von phpPgAdmin) angelegt.
Für diesen Benutzer wurden drei Datenbanken angelegt:
haendler_development (Zeichenkodierung UTF8)haendler_test (Zeichenkodierung UTF8)haendler_production (Zeichenkodierung UTF8)
Anlegen der Rails-Anwendung haendler
Zunächst werden im Rails-Webverzeichnis die Ordner und Dateien einer neuen Rails-Anwendung namens haendler erzeugt:
cd /web/rails
rails --database postgresql haendler
In der Konfigurations-Datei database.yml,
die den Zugriff auf das DB-System regelt, wird das Passwort für den Benutzer
haendler an drei Stellen eingetragen:
vi /web/rails/haendler/config/database.yml
development:
adapter: postgresql
encoding: unicode
database: haendler_development
pool: 5
username: haendler
password: geHEIM!?
test:
adapter: postgresql
encoding: unicode
database: haendler_test
pool: 5
username: haendler
password: geHEIM!?
production:
adapter: postgresql
encoding: unicode
database: haendler_production
pool: 5
username: haendler
password: geHEIM!?
Mit Hilfe eines rake-Kommandos kann überprüft werden, ob die
Verbindung zur Datenbank hergestellt werden kann:
cd /web/rails/haendler
rake db:migrate
Wenn das rake-Kommando keinen Fehler ausgibt, ist alles in Ordnung.
Zugang zur Web-Anwendung haendler mit Hilfe von Apache
Unter der Annahme, dass Rails-Anwendungen im Verzeichnis /web/rails
angelegt werden, kann der virtuelle Server in der Apache-Konfigurationsdatei httpd.conf
eingetragen werden:
<VirtualHost 192.168.0.161:80>
ServerName haendler.kowa
DocumentRoot /web/rails/haendler/public
<Directory /web/rails/haendler/public>
AllowOverride All
Order allow,deny
Allow from all
</Directory>
CustomLog logs/haendler-access combined
ErrorLog logs/haendler-error
</VirtualHost>
Nach einem Neustart von Apache mit /etc/rc.d/apache restart oder, schneller,
kill -1 <Prozessnummer des Apache-Root-Prozesses> sollte man die Applikation
im Browser ansehen können:
http://haendler.kowa/
Anmerkung
Wann immer an der Rails-Anwendung eine Änderung vorgenommen wurde, sollte man im tmp-Verzeichnis
der Anwendung eine Datei namens restart.txt (mit beliebigem Inhalt) angelegt werden. Dadurch wird
Apache informiert, dass der die Anwendung neu einlesen soll. Und somit werden alle Änderungen direkt im Browser sichtbar.
touch /web/rails/haendler/tmp/restart.txt
Konvention: Klassennamen stehen im Singular und Datenbanktabellen-Namen im Plural
Für Ruby on Rails wurde die Konvention eingeführt, dass Klassennamen stets in Singular stehen und mit einem Großbuchstaben beginnen: Haendler,
Liefert, Ware. Die zugehörigen Tabellennamen sollen dagegen im Plural stehen und kleingeschrieben werden.
Die zweite Regel führt allerdings bei deutschen Tabellennamen sowie bei englischnamigen Beziehungstabellen zu unschönen Ergebnissen, wenn die Tabellen automatisch gemäß Konvention z.B. mittels
ruby script/generate erzeugt werden:
haendlers, lieferts, wares bzw. traders, supplies, items (hier
wäre supply besser).
Wenn man die Ruby-Konvention beachten will, sollte man die falsche automatische Plural-Bildung verhindern, indem man Ausnahmen in die Datei inflections.rb einfügt:
vi /web/rails/haendler/config/initializers/inflections.rb
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'ein_haendler', 'haendler'
inflect.irregular 'liefert', 'liefern'
inflect.irregular 'ware', 'waren'
end
Anmerkung 1
Klassennamen für die Singular und Plural übereinstimmen,
bereiten beim automatischen Erzeugen von Zugriffsmethoden („Scaffolding“) Probleme, da in den URLs nicht zwischen
der Menge aller zugehörigen Objekten und einem einzelen Objekt unterschieden werden kann. Ich verwende als Workaround die Unterscheidung zwischen ein_haendler (Singular) und haendler (Plural).
Im Blog „HowFlow“ beschreibt aroedl eine etwas aufwändigere
Methode[2], bei der man
haendler als uncountable deklarieren kann, so dass Singular und Plural sich
wirklich nicht unterscheiden.
Anmerkung 2
In allen meinen Programmen und Vorlesungsbeispielen halte ich mich normalerweise an die Konvention, dass sowohl Klassen-, als auch Tabellennamen im Singular geschrieben werden sollten. Dies könnte man in Ruby on Rails erreichen, indem man eine Initialisierungs-Datei pluralize_false.rb
mit folgenden Inhalt anlegt:
# vi /web/rails/haendler/config/initializers/pluralize_false.rb
ActiveRecord::Base.pluralize_table_names = false
Das es nun einmal Ruby-on-Rails-Konvention ist, Tabellennamen im Plural zu schreiben, greife ich in diesem Beispiel nicht auf diese Möglichkeit zurück.
Anlegen des Controllers
Die Scaffold-Controller
cd /web/rails/haendler
ruby script/generate scaffold ein_haendler name:string adresse:string
ruby script/generate scaffold ware typ:string bezeichnung:string
rake db:migrate
rake db:migrate RAILS_ENV="production"
rake db:migrate RAILS_ENV="test"
Anlegen des Modells
Anlegen der Modell-Dateien
Mit Hilfe des Ruby-Skriptes ruby script/generate model kann nun die Dateien des Modells, d.h. die Dateien für die Klassen- und Tabellen-Definitionen des Modells angelegt werden. Man beachte, dass die Beziehungsklasse Liefert bzw. die Beziehungstabelle liefern explizit angelegt wird, da sie über eigene Attribute verfügt.
cd /web/rails/haendler/
ruby script/generate model haendler
ruby script/generate model ware
ruby script/generate model liefert
Ergebnis:
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/haendler.rb
create test/unit/haendler_test.rb
create test/fixtures/haendlers.yml
create db/migrate
create db/migrate/20081208135556_create_haendlers.rb
...
Primär- und Fremdschlüssel
Laut Ruby-on-Rails-Philosophie sollte jede Tabelle ein Primärschlüssel-Attribut namens id enthalten.
Dieses wird, wenn man in den zugehörigen Migrate-Dateien nichts anderes angibt, mittels rake db:migrate automatisch erzeugt.
Nun benötigen Beziehungstabellen kein künstliches Schlüssel-Attribut. Eine übliche Methode ist es daher, in Rails-Anwendungen
bei Beziehungstabellen ganz auf Primärschlüssel zu verzichten und anstatt dessen einen Index für die Beziehungsattribute zu definieren
(siehe z.B. den Abschnitt "Migration" in der sehr schönen Rails-Kurz-Referenz von InVisible[3]). Sollte eine Beziehungstabelle weitere Attribute haben,
so müsste für die Beziehungsattribute zusätzlich noch eine gemeinsame Unique-Bedingung angegeben werden. Dies wird für mehr als ein Attribut von Rails
leider auch nicht direkt unterstüzt (vgl. z.B. die Dokumentation zur Klasse
ActiveRecord::Migration[4], Abschnitt „More examples“).
In einem sehr schönen Blog-Beitrag[5] beschreibt Jeff Smith, warum im Falle von Beziehungstabellen „Composite Primary Keys“, wie Multi-Attribut-Primärschlüssel im Englischen genannt werden, so wichtig sind. Er widerlegt auch sehr schön die Argumente von Lee Richardson[6], der gegen „Composite Primary Keys“ argumentiert.
Ruby on Rails unterstützt nicht nur die Angabe von Multi-Attribut-Primärschlüsseln, auch die Angabe von Fremdschlüsseln wird nicht direkt unterstützt.
Glücklicherweise gibt es zwei Erweiterungspakete, die diese Probleme beheben.
Das Multi-Primär-Schlüssel-Problem lässt sich mit dem Rails-Paket composite_primary_keys beheben.
Und mit Hilfe des Pakets foreign_key_migrations können auch Foreign Keys innerhalb von Migration-Dateien definiert werde:
gem install composite_primary_keys
gem install foreign_key_migrations
Um diese Pakete verwenden zu können, müssen sie in der Händler-Anwendung noch aktiviert werden:
vi /web/rails/haendler/config/initializers/db_extensions.rb
require 'composite_primary_keys'
require 'composite_primary_keys/migration'
require 'foreign_key_migrations'
Bearbeiten der Migrate-Dateien
Nun werden die Attribute, Schlüssel, Fremdschlüssel und Indexe der Einzelenen Klassen des Modells
in den zugehörigen migrate-Dateien eingetragen.
vi /web/rails/haendler/db/migrate/*_create_haendler.rb
class CreateHaendler < ActiveRecord::Migration
def self.up
create_table :haendler do |t|
t.string :name, :null => false
t.string :adresse
t.timestamps
end
end
def self.down
drop_table :haendler
end
end
vi /web/rails/haendler/db/migrate/*_create_waren.rb
class CreateWaren < ActiveRecord::Migration
def self.up
create_table :waren do |t|
t.string :typ, :null => false
t.string :bezeichnung, :null => false
t.timestamps
end
end
def self.down
drop_table :waren
end
end
vi /web/rails/haendler/db/migrate/*_create_liefern.rb
class CreateLiefern < ActiveRecord::Migration
def self.up
create_table :liefern, :primary_key => [:haendler_id, :ware_id] do |t|
t.integer :haendler_id, :null => false, :references => :haendler
t.integer :ware_id, :null => false, :references => :waren
t.decimal :preis, :null => false, :precision => 6, :scale => 2
t.integer :lieferzeit
t.timestamps
end
add_index :liefern, [:ware_id]
end
def self.down
drop_table :liefern
end
end
Anschließend werden die Tabellen mit dem Ruby-Make-Befehl rake erzeugt:
cd /web/rails/haendler
rake db:migrate
Anmerkung
Mit
rake db:migrate VERSION=0
können alle Tabellen wieder gelöscht werden.
Bearbeitung der Klassendateien des Modells
vi /web/rails/haendler/app/models/haendler.rb
class Haendler < ActiveRecord::Base
has_many :waren, :through => :liefern
has_many :liefern
end
vi /web/rails/haendler/app/models/ware.rb
class Ware < ActiveRecord::Base
has_many :haendler, :through => :liefern
has_many :liefern
end
vi /web/rails/haendler/app/models/liefert.rb
class Liefert < ActiveRecord::Base
set_primary_keys [:haendler_id, :ware_id]
belongs_to :haendler, :foreign_key => 'haendler_id'
belongs_to :waren, :foreign_key => 'ware_id'
end
Anschließend sollte mit einem Browser Ihrer Wahl überprüft werden, ob irgendwelche Fehlermeldungen ausgegeben werden:
touch /web/rails/haendler/tmp/restart.txt
lynx http://haendler.kowa/test
Wenn der Server mit der Fehlermeldung 404 anzeigt, dass es diese Seite nicht gibt, ist alles OK. Views wurden ja noch nicht definiert. Wenn der Server allerdings mit der Fehlermeldung 500 anzeigt, dass ein interner Server-Fehler vorliegt, wurden die Modell-Dateien nicht korrekt definiert. Der Server gibt dann auch eine genauere Fehlermeldung aus.
Quellen
- ↑ Kowarschick, W. (2008): Skriptum zur Vorlesung Multimedia-Datenbanksysteme
- ↑ http://howflow.com/tricks/stoffnamen_uncountable_nouns_als_resourcen
- ↑ http://blog.invisible.ch/files/rails-reference-1.1.html#migration
- ↑ http://api.rubyonrails.org/classes/ActiveRecord/Migration.html
- ↑ http://weblogs.sqlteam.com/jeffs/archive/2007/08/23/composite_primary_keys.aspx
- ↑ http://rapidapplicationdevelopment.blogspot.com/2007/08/in-case-youre-new-to-series-ive.html
- Kowarschick, W.: Content-Management
- Agiles Web Development with Rails
- Learn all about Ruby ob Rails
- [1]
