Collections rendern

Immer, wenn eine Menge von Objekten (collection) gerendert werden soll, wird dem REST Schema zufolge die index action angefragt.
Zum Beispiel der controller für eine Liste von Artikeln:

class ArticlesController < ApplicationController
  def index
    @articles = Article.all
  end
end

Der Rails Konvention gemäß wird automatisch der entsprechende View gerendert. Mit HAML wäre das die articles/index.html.haml:

%ul.articles
  - @articles.each do |article|
    = render partial: 'article', locals: { article: article }

mit dem Partial articles/_article.html.haml:

%li{ id: dom_id(article) }= article.title

Allerdings hat das wiederholte Rendern des Partials in der Schleife gleich mehrere Nachteile.
Es enthält unnötige Logik im View (die Schleife).
Außerdem ist der Ansatz nicht performant. Im Hintergrund wird nämlich das Partial für jeden Schleifendurchlauf neu geladen. Bei 10 Artikeln also 10 Mal:

Started GET "/" for 127.0.0.1 at 2016-01-03 21:37:36 +0100
Processing by ArticlesController#index as HTML
Rendered articles/_article.html.haml (2.9ms)
Rendered articles/_article.html.haml (3.1ms)
Rendered articles/_article.html.haml (2.6ms)
Rendered articles/_article.html.haml (2.1ms)
Rendered articles/_article.html.haml (1.9ms)
Rendered articles/_article.html.haml (2.8ms)
Rendered articles/_article.html.haml (2.8ms)
Rendered articles/_article.html.haml (2.5ms)
Rendered articles/_article.html.haml (2.2ms)
Rendered articles/_article.html.haml (1.8ms)
Rendered articles/index.html.haml within layouts/application (47.2ms)
Completed 200 OK in 244ms (Views: 239.6ms | ActiveRecord: 1.4ms)

Die Alternative dazu ist Partial caching durch collection rendering. Der verbesserte View articles/index.html.haml:

%ul.articles
  = render partial: 'article', collection: @articles

Der Iterator wurde rausoptimiert und implizit wird der jeweilige Artikel als lokale Variable an das Partial übergeben.
Das Partial wird immer noch explizit benannt. Wenn der Partialname der Konvention entspricht, gibt es die noch kürzere Variante:

%ul.articles
  = render @articles

Dabei fragt Rails für jedes Objekt die Methode ‘to_partial_path’ ab, wenn sie implementiert ist, was bei ActiveRecord Objekten der Fall ist. So wird das richtige Partial gerendert.
In beiden Fällen ist der View sehr viel lesbarer und das Log sieht dann so aus:

Started GET "/" for 127.0.0.1 at 2016-01-03 21:40:21 +0100
Processing by ArticlesController#index as HTML
Rendered articles/_article.html.haml (17.3ms)
Rendered articles/index.html.haml within layouts/application (40.2ms)
Completed 200 OK in 223ms (Views: 220.8ms | ActiveRecord: 0.9ms)

Das ergibt beim Rendern dieses Partials einen Performancegewinn von 7,4 ms.
Aus meiner Sicht mindestens genauso wichtig aber auch ist der von Logik befreite View.