Neues in Rails 2.2 Teil 3: ActionController und ActionView
Im ActionController und im ActionView hat sich in Rails 2.2 sehr viel getan. Im folgenden Artikel gehen wir ausführlich auf die neuen Features ein.
Helper-Methoden
Helper-Methoden mit Blöcken
In Rails 2.2 kann man Helper nun auch sehr einfach mit einem Block verwenden. Angenommen wir möchten in der Seitenleiste mehre Kästen ausgeben, die z.B. wie folgt im HTML aussehen:
<div class="kasten">
<h2>Anzahl Benutzer</h2>
21 User sind Online
</div>
<div class="kasten">
<h2>News des Tages</h2>
<a href="http://www.rubyonrails.com">Rails 3.0</a> kommt ....
</div>
Mit dem folgenden Helper kann man die Wiederholung (Don’t Repeat Yourself) einsparen:
<% kasten("Anzahl Benutzer") do %>
21 User sind Online
<% end %>
<% kasten("Meldung des Tages") do %>
<%= link_to "Rails 3.0", "http://www.rubyonrails.com" %> kommt ....
<% end %>
Der Helper wird wie folgt implementiert (z.B. in der Datei app/helpers/application_helper.rb):
def kasten(title) do
concat("<div class='kasten'><h2>#{titel}</h2>")
yield
concat("</div>")
end
Vor Rails 2.2 musste noch umständlich ein sogenannter binding-Parameter angegeben werden. Der Helper link_to nutzt bereits diese neue Funktion:
link_to mit Blöcken
Der Methode link_to kann ab Rails 2.2 ein Block übergeben werden, was sich positiv bemerkbar macht, wenn längere Zeichenketten übergeben werden sollen.
Vor Rails 2.2
<%= link_to "<strong>#{@bookmark.title}</strong> angelgt am #{@bookmark.created_at}", @bookmark %>
Ab Rails 2.2
<% link_to(@bookmark) do %> <strong><%= @bookmark.title %></strong> angelegt am <%= @bookmark.created_at %> <% end %>
Helper country_select nicht mehr verfügbar
Die Methode country_select wurde ab Rails 2.2 entfernt. Sie steht aber weiterhin als Plugin zur Verfügung. Zur Installation des Plugins bitte folgendes ausführen:
./script/plugin install git://github.com/rails/country_select.git
Helper mit Hash-Optionen
Um z.B. die Zahl 2334.05 im deutschen Format als 2.334,05 zu formatieren, kann der Helper number_with_delimiter wie folgt verwendet werden:
number_with_delimiter(2334.05, ".", ",")
Um sich nicht die Parameterreihenfolge merken zu müssen, verwenden die meisten Helper in Rails ab Version 2.2 Hash-Parameter.
number_with_delimeter erwartet jetzt als ersten Parameter eine Zahl und als zweiten Parameter einen Hash mit den weiteren Optionen.
Zum Beispiel:
number_with_delimiter(2334.05, :delimiter => ".", :seperator => ",") # => 2.334,05
Die Reihenfolgen der Hash-Parameter spielt dabei keine Rolle (jedoch muss an erster Stelle die Zahl angegeben werden):
number_with_delimiter(2334.05, :seperator => ",", :delimiter => ".") # => 2.334,05
Weitere Methoden, denen jetzt auch die Optionen als Hash übergeben werden können:
excerpt("Rails ist ein super Framework", "super", :radius => 5, :omission => " * ")
# => * ein super Frame *
highlight("Rails ist ein super Framework", "super", :highlighter => '<em>\1</em>')
# => Rails ist ein <em>super<em> Framework
truncate("Rails ist ein super Framework", :length => 15)
#=> Rails ist ei...
word_wrap("Rails ist ein super Framework", :line_width => 10)
# => Rails ist
# => ein super
# => Framework
auto_link("Kontaktiere http://www.railsbuch.de oder info@railsbuch.de@", :link => :urls)
# => Kontaktiere <a href="http://www.railsbuch.de">http://www.railsbuch.de</a> oder info@railsbuch.de
error_message_on @bookmark, :title,
:prepend => "The title",
:append_text => ". Please fill out the field.",
:css_class => "error_field"
# => <div class="error_field">
# => The title can't be blank. Please fill out the field.
# =>
Neue Methode current_cycle
Mit dem Helper cycle werden bei jedem Aufruf die Elemente eines angegebenen Arrays iteriert. Das können Sie zum Beispiel nutzen, um in einer Tabelle auf die Zeilen zwei unterschiedliche CSS-Klassen im Wechsel anzuwenden.
Rails 2.2 stellt die neue Helpermethode current_cycle zur Verfügung, mit der Sie bei Verwendung der Methode cycle auf den verwendeten Wert der aktuellen Iteration zugreifen können.
<% @items = [1,2,3] %>
<ul>
<% @items.each do |item| %>
<li class="<%= cycle('ungerade','gerade') %>">
<%= item %> (<%= current_cycle %>)
</li>
<% end %>
</ul>
Es wird folgende HTML-Code generiert (wobei die Umbrüche wegen der Übersicht, leicht verändert wurden):
<ul>
<li class="ungerade">1 (ungerade)</li>
<li class="gerade">2 (gerade)</li>
<li class="ungerade">3 (ungerade)</li>
</ul>
Erweiterung der Methode image_submit_tag um die Option :confirm
Die Option :confirm, die in vielen Helpern, wie z.B. link_to und submit_tag zur Verfügung steht, kann ab Rails 2.2 auch in der Methode image_submit_tag angewendet werden. image_submit_tag wird verwendet, wenn man ein Bild für den “Senden”-Button verwenden möchte.
Ist die Option gesetzt, öffnet sich ein JavaScript-Bestätigungs-Fenster mit einem individuellen Text. Bestätigt der User die Box, wird das Formular gesendet, bricht er den Vorgang ab, passiert nichts.
<%= image_submit_tag("delete.jpg", :confirm=>"Wirklich löschen ?") %>
Neue Option recursive für die Methoden javascript_include_tag und stylesheet_link_tag
Um alle JavaScript und Stylesheet-Dateien aus den Verzeichnissen public/javascripts und public/stylesheets einzubinden, auch wenn sie in Unterverzeichnissen liegen, kann den beiden Methoden javascript_include_tag und stylesheet_link_tag neben der Option all, die Option recursive mit dem Wert true übergeben werden:
Die Dateien
public/javascripts/bookmarks/bookmark.js public/stylesheets/bookmarks/bookmark.css
werden über folgenden Code eingebunden:
javascript_include_tag :all, :recursive => true stylesheet_link_tag :all, :recursive => true
Reguläre Ausdrücke in time_zone_select
Seit Rails 2.1 gibt es die Möglichkeit, die Zeitzone in einer Rails-Applikation festzulegen. Auch ist es möglich, über den Helper time_zone_select einem Benutzer die Zeitzone über eine Auswahlliste auswählen zu lassen. Da die Liste jedoch sehr lang ist, kann man angeben, dass die Zeitzonen aus einem bestimmten Kontinent oder einer Region zuerst angezeigt werden sollen. Dazu gibt man die Region als Regulären Ausdruck an.
In Rails 2.1 konnten man nur die US-Zeitzonen als bevorzugte Zeitzonen angeben. Im folgenden Beispiel wird ein Select-Feld generiert, indem die europäischen Zeitzonen zuerst gelistet werden.
<%= time_zone_select( "user", 'time_zone', /Europe/) %>
Folgender HTML-Code wird generiert:
<select id="user_time_zone" name="user[time_zone]"><option value="Dublin">(GMT+00:00) Dublin</option> <option value="Edinburgh">(GMT+00:00) Edinburgh</option> <option value="Lisbon">(GMT+00:00) Lisbon</option> <option value="London">(GMT+00:00) London</option> <option value="Amsterdam">(GMT+01:00) Amsterdam</option> <option value="Belgrade">(GMT+01:00) Belgrade</option> <option value="Berlin">(GMT+01:00) Berlin</option> ... </select>
Folgende Reguläre Ausdrücke können u.a verwendet werden:
- /Africa/
- /America/
- /Asia/
- /Australia/
- /Europe/
- /US/
- …
—-
Render und Partials
Vereinfachung von Partials mit collections
Angenommen wir möchten eine Liste von Produkten ausgeben, können wir das wie folgt machen:
# Datei app/views/products/index.html.erb <ul> <% for product in @products do %> <li><%= link_to product.title, product %></li> <% end %> </ul>
Mit Partials kann man den Code auch übersichtlicher gestalten:
# datei app/views/products/_product.html.erb <li><%= link_to product.title, product %></li> # Datei app/views/products/index.html.erb <ul> <%= render :partial => @products %> </ul>
Der Zugriff auf die einzelnen Elemente innerhalb des Partials erfolgt dann über eine Variable, die im Singular der Array-Variablen benannt war. In unserem Beispiel products.
Vor Rails 2.2 mussten man noch die Option :collection angeben.
<%= render :partial => “product”, :collection => @products %>
Ab Rails 2.2 können Sie den Namen der Variablen, über die auf die einzelnen Elemente zugegriffen werden kann, übergeben:
Angenommen wir haben eine Seite, die nur Bücher auflistet, so können wir das eben erstellte Partial auch benutzen, indem wir die Option :as verwenden:
<%= render :partial => @books, :as => :product %>
Die Option ist notwendig, damit im Partial die lokale Variable product verwendet werden kann, obwohl @books im Partial angegeben wird
Bis jetzt konnten wir auch ein Partial aufrufen, ohne explizit ein Objekt zu übergeben. Rails ist davon ausgegangen, dass das Objekt, das genauso heisst, wie das Partial in das Partial zu übergeben ist. Das funktioniert zwar auch noch in Rails 2.2, führt aber zu folgender deprecation-Warnung:
"@objektname will no longer be implicitly assigned to objekt"
Es ist also davon auszugehen, dass diese Funktionalität in Zukunft ganz verschwindet.
Partials und Blöcke
Angenommen, wir möchten auf einer Seite die Produkte mit unterschiedlichen Zusatzinformationen ausgeben (mal mit dem Änderungs-Datum und mal mit dem Hersteller des Produkts), können wir dem Partial auch einen Block übergeben, wie in folgendem Beispiel:
<ul> <%= render :partial => @products |product| do %> (<span class="info"> product.manufacturer </span>) </ul>
Im Partial steht dann
<li><%= link_to product.title, product %> <%= yield %></li>
An der Stelle wo yield steht, wird der Block eingefügt. In unserem Beispiel sieht der Code dann wie folgt aus:
<ul>
<li>Tastatur (<span class="info">Logitech</span>)</li>
<li>Drucker (<span class="info">HP</span>)</span></li>
Möchten wir das Partial nutzen, um z.B. das Änderungsdatum anzuzeigen, können wir das Partial wie folgt aufrufen:
<ul> <%= render :partial => @products |product| do %> (<span class="info"> product.updated_at </span>) </ul>
render
Von den Aufrufen render :action und render :partial kennen wir es, dass wir lokale Variablen über die Option :locals als Hash übergeben können. Ab Rails 2.2 können auch in ein Template lokale Variablen übergeben werden:
render :template => "bookmarks/show", :locals => {:bookmark => Bookmark.new}
Die Option use_full_path
render :file => "some/file", :use_full_path => true
steht ab Rails 2.2 nicht mehr zur Verfügung.
Routing und Resourcen
Vereinfachter Zugriff auf verschachtelte Ressourcen
Wenn eine Ressource von einer anderen abhängig ist, können wir das wie folgt im Routing definieren:
map.resources :users do |user|
user.resources :posts do |post|
post.resources :comments
end
end
Durch diese Definition wird festgelegt, dass ein User mehrere Posts hat und diese Posts wiederum Kommentare haben können. Diese Abhängigkeit wird auch in der URL abgebildet. Der Zugriff auf die Posts eines Users erfolgt über folgende URL:
/users/1/posts
Oder der Zugriff auf die Kommentare eines Posts über folgende URL:
/users/1/posts/5/comments
Die in Rails 2.2 neue Option :shallow => true gibt uns mehr Flexibilität beim Zugriff auf verschachtelte Ressourcen:
map.resources :users, :shallow => true do |user|
user.resources :posts do |post|
post.resources :comments
end
end
Ist die Option gesetzt, ist nach wie vor der Zugriff auf die Ressourcen über die URLs
/users/1/posts /users/1/posts/5/comments
möglich. Zusätzlich erhalten wir aber auch die Möglichkeit, auf Posts und ihre Kommentare zuzugreifen, ohne den zugehörigen User angeben zu müssen:
/posts/2 /posts/2/comments
Hinweis: Wird die Option auf die äussere Ressource angewendet, erben alle in ihr verschachtelten Ressourcen automatisch diese Eigenschaft.
Die neue Option kann auch auf Ressourcen angewendet werden, die mit has_many oder has_one definiert werden:
map.resources :users, :has_many => { :posts => :comments }, :shallow => true
Es werden auch Helper-Methoden generiert, um auf die Posts und ihre Kommentare verlinken zu können:
user_posts_path(@user) # => '/users/1/posts' posts_path # => '/posts' post_comments_path(@post) # => /posts/5/comments
—-
Ajax
page.reload
Die Methode reload, die die aktuelle Seite per Javascript im Browser neu lädt, ist ab Rails 2.2 in ActionView::Helpers::PrototypeHelper integriert, so dass sie in RJS-Templates oder im render(:update) Block verwendet werden kann. Die Methode reload kann also ab Rails 2.2 als Abkürzung für den Aufruf von window.location.reload(); verwendet werden.
respond_to do |format|
format.js do
render(:update) { |page| page.reload }
end
end
button_to_remote
Die Methode submit_to_remote wurde umbenannt in button_to_remote.
Bestehende Projekte müssen aber nicht angepasst werden, da die Methode submit_to_remote als Alias für die Methode button_to_remote erhalten bleibt.
Sicherheit
Option :session_http_only für Session-Cookies
Über die Option http_only kann in Rails konfiguriert werden, das auf Session-Cookies nur über das HTTP-Protokoll zugegriffen werden kann, um zu vermeiden, dass über JavaScript-Code auf die Cookies zugegriffen wird. Leider wurde die Option bis jetzt gerne übersehen. Der Default-Wert war bis Rails 2.2 auch false.
Ab Rails 2.2 wird die http_only – Option standardmässig gesetzt, um die Sicherheit von Rails 2.2 – Applikationen zu erhöhen.
Sie können die Option deaktivieren, indem Sie folgende Zeile in den ApplicationController oder in einen speziellen Controller einbinden:
session :session_http_only => false
Vermeidung von Response – Splitting Attacken
Bis jetzt wurden URLs, die der Methode redirect_to übergeben wurden, nicht darauf geprüft, ob sie eventuell schadhaften Code enthalten. Das war gefährlich, da es einen response splitting und header injection Angriff in einer Rails-App möglich machte.
Um das in Zukunft zu vermeiden, werden ab Rails 2.2 die URLs die der Methode redirect_to übergeben werden, geprüft und evtl vorhandener schadhafter Code entfernt. Was uns natürlich nicht davon abhalten sollte, selbst immer dafür zu sorgen, User-Eingaben zu prüfen und von evtl vorhandenem schadhaftem Code zu säubern, bevor wir sie weiterverarbeiten.
Caching
Template-Caching in Production-Umgebung
Wenn man in der Development-Umgebung ist, kann man z.B. den Code im Controller, Model oder View bearbeiten und sich direkt im Browser das Ergebnis anschauen, ohne die Applikation neustarten zu müssen.
In der Produktions-Umgebung werden aus Performanz-Gründen, die Model und die Controller Klassen nur beim starten der Applikation geladen. Vor Rails 2.2 konnten die Views ohne Neustart der Applikation verändert werden. In Rails 2.2 werden in der Production-Umgebung zur Verbesserung der Performance Template-Seiten automatisch gecached. Das heisst, damit Änderungen an Templates wirksam werden, muss die Applikation neu gestartet werden. Die Ergebnisseite (Seite, die im Browser ausgegeben wird), wird jedoch nicht automatisch gecacht. Dazu können u.a. die Methoden caches_page und caches_action verwendet werden.
Neue Option :layout in Methode caches_action
Zum Cachen einer Seite gibt es in Rails mehrere Möglichkeiten.
Um z.B. eine ganze Seite zu cachen kann im Controller die Methode caches_page verwendet werden. Beim Page-Caching wird die gesamte Seite in einer Datei zwischengespeichert. Wenn die Seite (Action) jedoch mit einem Passwort-Schutz versehen ist, sollte caches_action verwendet werden, damit die Seite nicht ohne Authentifizierung des Benutzers angezeigt wird. Diese Methode funktioniert jedoch nicht, wenn im Layout dynamische Inhalte, wie z.B. der Benutzername des aktuell angemeldeten Users oder die Uhrzeit angezeigt werden.
In Rails 2.2 wurde der Methode caches_action die Option :layout hinzugefügt. Mögliche Werte, die übergeben werden können, sind true und false.
Wird false übergeben, wird nur der Inhalt der action gecached, nicht das layout. Was besonders dann nützlich ist, wenn das layout dynamische Inhalte enthält.
Wird true übergeben oder die Option :layout nicht gesetzt, wird auch der Inhalt des layouts gecached, was dem Standardverhalten entspricht.
class BookmarksController < ApplicationController ... caches_action :index, :layout => false ... end
Sonstiges
define_javascript_functions
Die Methode define_javascript_functions wird ab Rails 2.2 nicht mehr unterstützt. Was nicht weiter schlimm ist, da Methoden wie javascript_include_tag dieselbe Funktionalität auf bessere Weise zur Verfügung stellen.
Deaktivierung des Accept-Headers in HTTP-Anfragen
Damit wir Ergebnisse in unterschiedlichen Formaten ausgeben können, nutzen wir die respond_to Methode:
def index
@bookmarks = Bookmark.find(:all)
respond_to do |format|
format.html
format.xml { render :xml => @bookmarks.to_xml }
end
end
Rails hat 2 Möglichkeiten das Format, das im respond_to Block verwendet werden soll, zu erkennen. Am häufigsten verwendet wird die übergabe des Formats über die URL:
/index.xml
Wird das Format nicht über die URL übergeben, guckt Rails im HTTP-Accept-Header der Anfrage nach, welches Format gefordert ist. Ein HTTP-Accept-Header kann wie folgt aufgebaut sein:
Accept: text/plain, text/html
Der Zugriff darauf erfolgt wie folgt:
@request.env["HTTP_ACCEPT"] = "text/html"
Da dieser header nicht einheitlich von allen Browsern interpretiert wird, wurde dieser header in den Standardeinstellungen ab Rails 2.2 deaktiviert. Um ihn zu aktivieren, muss folgendes in der environment.rb eingetragen werden:
config.action_controller.use_accept_header = true
Ist der HTTP-Accept-Header deaktiviert, geht Rails vom Format .html aus, wenn kein Format in der URL angegeben wurde. Die einzige Ausnahme bilden Ajax-Anfragen die mit dem X-Requested-With-Header geschickt werden. In diesem Fall geht Rails vom Format .js aus, wenn kein Format über die URL angegeben wurde.
Performance Warnmeldungen
In Rails 2.2 wurde ein Konfigurationsparameter hinzugefügt, mit dem Sie überwachen können, ob Views ausserhalb des View-Verzeichnisses geladen werden. Diese Views werden nämlich nicht gecached und wirken sich deshlab negativ auf die Performance aus. Um die Einstellung zu aktivieren, tragen Sie bitte folgendes in die environment.rb ein:
config.action_view.warn_cache_misses = true
Wird jetzt eine Datei ausserhalb des konfigurierten Verzeichnisses geladen, erhalten Sie folgende Warnmeldung:
[PERFORMANCE] Rendering a template that was not found in view path. Templates outside the view path are not cached and result in expensive disk operations. Move this file into /Users/user/project/app/views or add the folder to your view path list
Die Einstellung ist standardmässig deaktiviert.
Fehler besser abfangbar
Ab Rails 2.2 kann mit Hilfe der Methode rescue_from einfach eine individuelle Fehlermeldung ausgegeben werden:
class ApplicationController < ActionController::Base
rescue_from User::NotAuthorized, :with => :deny_access
rescue_from ActiveRecord::RecordInvalid, :with => :show_errors
protected
def deny_access
…
end
def show_errors(exception)
exception.record.new_record? ? …
end
end
Durch einbinden von ActiveSupport:Rescuable steht die Methode rescue_from in jeder Klasse zur Verfügung.
Singleton-Ressourcen
Bis Rails 2.2 funktionierte der polymorphic_url Helper nicht richtig mit Singleton-Ressourcen. Ab Rails 2.2 können Singleton-Ressourcen mit Symbolen spezifiziert werden, so wie es mit Namespaces auch möglich ist:
# Dieser Code polymorphic_url([:admin, @user, :blog, @post]) # ist dasselbe wie dieser Code admin_user_blog_post_url(@user, @post)
Sorry, comments are closed for this article.