5.11 RESTful Rails 

RESTful Rails? Keine Sorge. In diesem Abschnitt geht es darum, Ihnen zu zeigen, wie RESTful Rails in der Praxis funktioniert - und das ist einfacher, als der Begriff es vermuten lässt. Wofür RESTful steht und was sich dahinter im Detail verbirgt, erfahren Sie in Kapitel 14 .
Wir konzentrieren uns im Folgenden mehr auf die Sache als auf den Begriff.
CRUD
Im letzten Abschnitt haben wir gelernt, dass, wenn wir benannte Routing-Einträge vornehmen, uns automatisch die Methoden »name«_url und »name«_path zur Verfügung stehen. Das hätten wir für unseren Bookmarks-Controller auch gerne. Da unsere Bookmarks dem CRUD-Prinzip folgen, können wir eine Funktionalität in Rails nutzen, die uns ähnliche Methoden zur Verfügung stellt, die wir für die Verlinkung im Controller und in den Views verwenden können. Wir müssen dazu folgenden Eintrag in der Datei routes.rb vornehmen:
map.resources :bookmarks
und die beiden Einträge
map.connect ':controller/:action/:id' map.connect ':controller/:action/:id.:format'
auskommentieren, da der Eintrag map.resources :bookmarks bewirkt, dass das Routing nach einem völlig anderen Konzept funktioniert:
Listing config/routes.rb
ActionController::Routing::Routes.draw do |map|
# ...
map.root :controller => "bookmarks"
map.resources :bookmarks
map.login 'login', :controller => "authentication",
:action => "login"
map.logout 'logout', :controller => "authentication",
:action => "logout"
map.check 'check', :controller => "authentication",
:action => "check"
# Install the default routes as the lowest priority.
# map.connect ':controller/:action/:id'
# map.connect ':controller/:action/:id.:format'
Lokalen Server neu starten
Nach dem Neustart des lokalen Rails-Servers stehen Ihnen u. a. folgende Methoden, die Sie zur Generierung der Links in den Views und Controllern einsetzen können, zur Verfügung:
- bookmarks_path
Generiert den Pfad zum Anzeigen aller Bookmarks:
- bookmarksbookmark_path(id)
Generiert den Pfad zum Anzeigen eines bestimmten Bookmarks: /bookmarks/id
- new_bookmark_path
Generiert den Pfad zum Anzeigen des Formulars, um einen neuen Bookmark anzulegen: /bookmarks/new
- edit_bookmark_path(id)
Generiert den Pfad zum Anzeigen des Ändern-Formulars eines bestimmten Bookmarks: /bookmarks/1/edit
Es wurden auch folgende Methoden erzeugt, die im Unterschied zu den anderen die vollständige URL inklusive des Hosts generieren:
- bookmarks_url
- bookmark_url(id)
- new_bookmark_url
- edit_bookmark_url(id)
In unserem Fall reichen die Pfade ohne den Host aus. Deshalb setzen wir die Methoden *_path in unseren Views und unserem Bookmarks-Controller ein.
In der index.html.erb ersetzen wir den Pfad zu dem Formular, um einen neuen Bookmark anzulegen, durch die Methode new_bookmark_path:
Listing app/views/bookmarks/index.html.erb
<% @title = "liste" %> <h2>Liste der Favoriten</h2> <ul> <%= render :partial => "bookmark", :collection => @bookmarks %> </ul> <% if admin? %> <p> <%= link_to "Neuen Favorit erstellen", new_bookmark_path %> </p> <% end %>
Die weiteren Links befinden sich in dem Partial _bookmark.html.erb . Die Pfade in den Links zum Anzeigen und Ändern eines Bookmarks können wir durch die Methoden
bookmark_path(id) und edit_bookmark_path(id)
ersetzen:
Listing app/views/bookmarks/_bookmark.html.erb
<li> <%= link_to h(bookmark.title), h(bookmark.url) %> (<%= link_to "Details", bookmark_path(bookmark.id) %> <% if admin? %> | <%= link_to "ändern", edit_bookmark_path(bookmark.id) %> | <%= link_to "löschen", {:action => "destroy", :id => bookmark.id}, :confirm => "Wollen Sie diesen Datensatz wirklich löschen?" %> <% end %> ) </li>
Zugriff auf ID
Da per Konvention immer auf die ID eines Objektes zugegriffen wird, wenn das ganze Objekt übergeben wird, müssen wir nicht explizit die ID in die Methoden übergeben, sondern es reicht, wenn wir das Objekt bookmark übergeben:
bookmark_path(bookmark) edit_bookmark_path(bookmark)
Ihnen ist sicherlich aufgefallen, dass uns keine Methode zum Löschen eines Bookmarks zur Verfügung steht. Das liegt an einem weiteren Prinzip, das dem REST-Standard zugrunde liegt. Es geht dabei darum, dass eine URL, die dem Muster http://host:port/controller/action/id folgt, zu technisch und zu sehr railsspezifisch ist. Es ist doch vielmehr so, dass es beim Anzeigen, Ändern und Löschen eines Datensatzes immer um den gleichen Datensatz geht. Das heißt, eigentlich sollte doch ein Datensatz auch durch eine URL gekennzeichnet sein. Aber wie weiß das System dann, welche Operation durchgeführt werden soll? -- Anhand der eingesetzten HTTP-Methode!
GET, PUT, DELETE
Das Anzeigen eines Datensatzes erfolgt über die Methode GET, das Ändern über die Methode PUT und das Löschen über die Methode DELETE . Wie wir die GET -Methode im Browser nutzen, ist uns klar. Das geht über die Eingabe in der Adresszeile, den Aufruf eines Links, das Absenden eines Formulars mit der Methode GET oder natürlich auch über JavaScript. Aber bei den Methoden PUT und DELETE ist uns unter Umständen nicht so ganz klar, wie das im Browser funktionieren soll. Diese Methoden können nur über JavaScript im Browser aufgerufen werden, und die Generierung dieser Skripte übernimmt Rails für uns. Das Einzige, was wir tun müssen, ist, die eingesetzte Methode über den Parameter :method zu übergeben. Im Falle von GET entfällt dieser Parameter, weil ein Link immer über die Methode GET aufgerufen wird.
POST
Wir müssen nicht nur einen Bookmark anzeigen, ändern und löschen können, sondern wir müssen auch alle Bookmarks anzeigen können und der Menge aller Bookmarks einen Bookmark hinzufügen können. Zum Anzeigen aller Bookmarks wird die URL /bookmarks mit der Methode GET aufgerufen und zum Anlegen eines Bookmarks mit der Methode POST.
In Bezug auf unsere Methoden, die automatisch generiert wurden, bedeutet das:
- GET bookmarks_path
Generiert den Pfad zum Anzeigen aller Bookmarks: /bookmarks und ruft im Controller die Action index auf.
- POST bookmarks_path
Legt einen neuen Bookmark an: /bookmarks und ruft im Controller die Action create auf.
- GET bookmark_path(id)
Zeigt einen bestimmten Bookmark an: /bookmarks/id und ruft im Controller die Action show auf.
- PUT bookmark_path(id)
Ändert einen bestimmten Bookmark: /bookmarks/id und ruft im Controller die Action update auf.
- DELETE bookmark_path(id)
Löscht einen bestimmten Bookmark: /bookmarks/id und ruft im Controller die Action destroy auf.
- GET new_bookmark_path
Generiert den Pfad zum Anzeigen des Formulars, um einen neuen Bookmark anzulegen: /bookmarks/new und ruft im Controller die Action new auf.
- GET edit_bookmark_path(id)
Generiert den Pfad zum Anzeigen des Ändern-Formulars eines bestimmten Bookmarks: /bookmarks/1/edit und ruft im Controller die Action edit auf.
Sieben Methoden
Das heißt, eine Grundvoraussetzung ist, dass der Controller dem CRUD-Prinzip folgt und über diese sieben Methoden verfügt:
- index
- show
- new
- create
- edit
- update
- destroy
In unserem Beispiel müssen wir noch den Pfad zum Löschen in der _bookmark.html.erb und den Aufruf des Helpers form_for in den Formularen zum Anlegen und Ändern eines Bookmarks anpassen:
Listing app/views/bookmarks/_bookmark.html.erb
<%= link_to "löschen", bookmark_path(bookmark), :method => :delete, :confirm => "Wollen Sie diesen Datensatz wirklich löschen?" %>
Wir nutzen zum Anlegen und Ändern eines Bookmarks das gleiche Formular (_form.html.erb). Da wir mit Hilfe dieses Formulars jetzt eine Ressource pflegen, muss es mit unterschiedlichen HTTP-Methoden abgeschickt werden (POST bei der Neuanlage und PUT beim Ändern).
Automatisches form-for
Rails 2.0
Neu in Rails 2.0 ist, dass der Methode form_for »nur« noch das Objekt, das in den Actions new und edit erzeugt wurde, übergeben werden muss. Die Methode erkennt automatisch, ob es sich dabei um ein neues Objekt oder ein Objekt, das editiert werden soll, handelt und setzt das Attribut action und die HTTP-Methode innerhalb des von ihr generierten form -Tags in Abhängigkeit davon. Wurde ein neues Objekt übergeben, werden die Formulardaten mit der HTTP-Methode POST an die Action create innerhalb des BookmarksController gesendet. Die Formulardaten eines zu editierenden Objektes werden mit der HTTP-Methode PUT an die Action update gesendet. Das heißt, in Rails 2.0 ist der Aufruf der Helper-Methode form_for innerhalb der beiden Views new.html.erb und edit.html.erb identisch. Der daraus generierte HTML-Code ist aber unterschiedlich.
Listing app/views/bookmarks/_form.html.erb
<% form_for(@bookmark) do |f| %> ... <% end %>
Listing app/views/bookmarks/new.html.erb
<% @title = "neu" %> <h2>Neuen Favorit erstellen</h2> <%= render :partial => "form", :locals => { :submit_text => "Neuen Favorit erstellen" } %> <p><%= back_to_list %></p>
Listing app/views/bookmarks/edit.html.erb
<% @title = "bearbeiten" %> <h2>Favorit bearbeiten</h2> <%= render :partial => "form", :locals => { :submit_text => "ändern" } %> <p><%= back_to_list %></p>
Pfade anpassen
Im Bookmarks-Controller müssen wir noch die Pfade in den Redirect-Aufrufen anpassen:
Listing app/controllers/bookmarks_controller.rb
... def create @bookmark = Bookmark.new(params[:bookmark]) if @bookmark.save flash[:notice] = "Favorit wurde erfolgreich angelegt." redirect_to bookmarks_path else render :action => "new" end end def update @bookmark = Bookmark.find(params[:id]) if @bookmark.update_attributes(params[:bookmark]) flash[:notice] = "Favorit wurde erfolgreich geändert." redirect_to bookmarks_path else render :action => "edit" end end def destroy @bookmark = Bookmark.find(params[:id]) @bookmark.destroy flash[:notice] = "Favorit wurde erfolgreich gelöscht." redirect_to bookmarks_path end
Die render :action -Aufrufe werden nicht ersetzt, da hier kein neuer Aufruf der Seite erfolgt, sondern nur der View zu der Action angezeigt wird.
Damit haben wir nicht nur den Standard RESTful Rails in unserer Bookmarkverwaltung definiert, sondern wir haben auch vereinfachte URLs in unserem Programm-Quelltext.
Der Authentication-Controller ist noch nicht RESTful. Deshalb können wir hier das in diesem Abschnitt Gezeigte nicht anwenden. In Kapitel 14 gehen wir genauer auf die Details hinter RESTful ein.
scaffold
Wir haben bis jetzt einen relativ hohen Aufwand betreiben müssen, damit der Bookmarks-Controller dem CRUD-Prinzip folgt, die Applikation über REST standardisierte Aufrufe verfügt und die Views diese Aufrufe nutzen. Für all das hat Rails einen Generator, der das, was wir bis hierher manuell erzeugt haben, automatisch generiert:
ruby script/generate scaffold Modelname Feldname:Feldtyp ...
Dieser Generator erzeugt automatisch:
- den Controller mit den erforderlichen sieben Methoden für CRUD und mit der Möglichkeit, die Daten in HTML und XML auszugeben
- die zum Controller gehörenden Views inklusive der Formulare
- das Model
- die Migration-Datei
- Eintrag map.resources in routes.rb
- Tests
Wenn man mit dem Generator scaffold arbeitet, schafft man eine sehr gute Grundlage für das Projekt. Man muss zwar noch das eine oder andere anpassen und erweitern, aber das Grundgerüst ist sehr gut. Wir haben den Generator in diesem Beispiel nicht eingesetzt, damit wir Ihnen zeigen konnten, was hinter den Kulissen von Rails passiert.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.