homeASCIIcasts

149: Rails Engines 

(view original Railscast)

Other translations: En

Other formats:

Written by Jan Lelis

Eine Rails Engine ("Schienenmotor") ist eine Möglichkeit, eine Rails-Applikation in eine andere einzubetten. Engines gibt es schon eine Weile als Plugin, aber viel der Funktionalität ist in Rails 2.3 schon eingebaut. In dieser Episode zeigen wir, wie es funktioniert.

Wir starten mit zwei separaten Applikationen. Die eine ist eine einfache e-commerce-Applikation, welche Produkte verkauft.

Our shop application.

Unsere Shop-Applikation läuft auf Port 3000.

Die andere ist eine Blog-Applikation, welche Artikel beinhaltet und Benutzern erlaubt, diese zu kommentieren.

Our blog application.

Unsere Blog-Applikation auf Port 3001.

Was wir erreichen möchten ist, die Blog-Funktionalität zu dem Shop hinzufügen, sodass dieser sein eigenen Blog hat. Wir könnten diese zwei Applikationen zusammenführen, aber wir wollen beide Applikationen so getrennt wie möglich halten, damit wir den Blog auch in anderen Applikationen nutzen können. Um dies zu erreichen, betten wir die Blog-Applikation mittels Engines in unseren Shop ein.

Ein Plugin erstellen

Beide Applikationen sitzen derzeit in verschiedenen Verzeichnissen. Um die Blog-App einzustöpseln, müssen wir zuerst ein Plugin in der Shop-App generieren.

script/generate plugin blogify

Nachdem wir das Plugin generiert haben, erstellt es einige Dateien im /vendor/plugins/blogify Verzeichnis und es ist dieses Verzeichnis, in welches wir die Dateien unserer Blog-App kopieren. Das erste Verzeichnis, welches wir rüberkopieren müssen, ist /app.

cp -R ~/rails/blogify/app .

Jetzt haben wir alle unsere Blog-Model, -Controller und Views in dem Plugin-Verzeichnis. Diese Verzeichnisse werden automatisch von der Applikation eingebunden, sodass wir nun in der Lage sind, die Artikel- und Comments-Controller zu verwenden.

Wir müssen auch die routes-Datei der Blog-App kopieren. Wir können ein config-Verzeichnis in dem Plugin-Verzeichnis erzeugen und die Dateien rüberkopieren.

  mkdir config
  cp ~/rails/blogify/config/routes.rb config
  

Nachdem wir die Blog-Routen kopiert haben, müssen wir die map.root-Zeile aus der Datei entfernen, damit es keinen Konflikt mit dem root-Controller der Shop-App gibt.

Die Migrationen migrieren

Wenn wir den Server nun starten und versuchen, den Artikel-Controller zu benutzen, bekommen wir ein Response, was bedeutet, dass Rails zwar den Controller im Plugin-Verzeichnis findet. Auf der Artikel-Seite tritt aber ein Fehler auf. Der Grund dafür sind die Datenbank-Tabellen, welche im Blog existieren, aber noch nicht in der Shop-Applikation erstellt wurden. Wir werden die Migrationen der Blog-Applikation auf der Shop-Datenbank ausführen müssen. Dazu müssen wir ein db-Verzeichnis in dem Plugin-Verzeichnis anlegen und unsere Migrationen rüberkopieren.

  mkdir db
  cp -R ~/rails/blogify/db/migrate db
  

Obwohl die Dateien nun im richtigen Verzeichnis des Plugins sind, weiß Rails 2.3 nicht, dass es in diesem Verzeichnis danach suchen soll. Es gibt keine einfache Methode, Rails zu zeigen, wo es nachschauen soll. Also werden wir ein Rake-Task anlegen, welcher die Plugin-Migrationen mit den Migrationen der Haupt-Applikation synchronisiert.

Den rake-Task erstellen

Im Plugin-Verzeichnis gibt es ein tasks-Verzeichnis und darin sollte eine Datei mit dem Namen blogify_tasks.rake liegen. Wir werden unseren rake-Task in dieser Datei erzeugen.

  namespace :blogify do
    desc "Sync extra files from blogify plugin"
    task :sync do
      system "rsync -ruv vendor/plugins/blogify/db/migrate db"
    end
  end
  

Unser neuer rake-Task wird rsync benutzen, um die Migration-Dateien zu syncen (Leider funktioniert rsync nicht unter Windows)

Unseren neuen rake-Task können wir nun im Shop-Verzeichnis ausführen und die Migrationen synchronisieren

  rake blogify:sync
  (in /Users/eifion/rails/apps_for_asciicasts/ep149)
  building file list ... done
  migrate/20090226195405_create_articles.rb
  migrate/20090226200008_create_comments.rb
  migrate/20090226202713_add_article_id_to_comments.rb

  sent 944 bytes  received 86 bytes  2060.00 bytes/sec
  total size is 646  speedup is 0.63
  

Jedes mal, wenn wir nun unser plugin modifizieren, können wir unseren rake-Task ausführen. Natürlich müssen wir danach rake db:migrate ausführen um die Datenbank upzugraden.

Erfolg!

Jetzt, da die Datenbank migriert ist, können wir den Server der Applikation wieder starten. Mit den neuen Tabellen an der richtigen Stelle funktioniert der Artikel-Index jetzt, obwohl natürlich keine Artikel da sind, da wir gerade neue, leere Tabellen erstellt haben.

Der Blog funktioniert als Plugin.

Stylesheets und Layouts

Der Blog-Bereich unserer Applikation funktioniert jetzt, aber er hat die Layout-Datei und die Stylesheets unserer Shop-App angenommen. Der Grund dafür ist, dass wir zwei Layout-Dateien mit den gleichen Namen application.html.erb haben, und eine Layout-Datei in der Haupt-Applikation hat Vorrang vor einer mit dem gleichen Namen in einem Plugin. Deshalb wird ein Template des Blog-Plugins mit der Layoutdatei des Shops angezeigt. Dieses Verhalten wirkt auch bei Models und Controllern, was uns erlaubt, beliebige Teile eines Plugins zu überschreiben. Da wir für unseren Blog das gleiche Aussehen wie für den Shop haben wollen, bleiben wir bei dieser Variante.

Was aber, wenn wir eine separate Layoutedatei und separate Stylesheets für unseren Blog haben wollen? Episode 7 behandelte verschiedene Typen von verfügbaren Layouts. Eines davon ist ein Controller-spezifisches Layout und dies ist für uns eine ideale Möglichkeit ein separates Layout für unseren Blog zu behalten, da unsere Blog-Applikation nur ein Controller hat.

Man benötigt nur zwei Schritte ein separates Layout anzulegen. Als Erstes müssen wir die Layout-Datei des Plugins von application.html.erb zu articles.html.erb umbenennen. Controller-Layouts haben Vorrang vor Applikations-Layouts, also müsste unser Blog dieses Layout nun verwenden. Als Zweites müssen wir den Blog-Stylesheet von der originalen Blog-App zum Stylesheet-Ordner der Shop-App kopieren. (Wir können es nicht in ein public/stylesheets-Verzeichnis im Plugin-Ordner kopieren, da Rails dort nicht danach suchen wird).

Bilder und JavaScript

Egal ob wir eine separates Layout verwenden oder nicht, wollen wir normalerweise auch andere statische Inhalte rüberkopieren, zum Beispiel Bilder und JavaScript-Dateien. Dies können wir machen, indem wir diese Dateien von dem public-Verzeichnis unserer Blog-App in ein public-Verzeichnis des Plugins kopieren. Um diese dann aktuell zu halten, können wir den rake-Task erweitern, den wir früher angelegt hatten. Wenn wir, wie oben beschrieben, einen separates Stylesheet benutzen, kann diese Technik benutzt werden, um diesen auch zu syncen.

  namespace :blogify do
    desc "Sync extra files from blogify plugin"
    task :sync do
      system "rsync -ruv vendor/plugins/blogify/db/migrate db"
      system "rsync -ruv vendor/plugins/blogify/public ."
    end
  end
  

Der verbesserte rake-Task kopiert nun auch den public-Ordner.

Das ist erstmal alles wichtige über Rails-Engines. Auch wenn sie nicht passend für jede Rails-Applikation sind, bieten sie einen mächtigen Weg, eine Rails-Applikation in eine andere einzubetten.