Partials are a great way to break down complex view into more manageable chunks as well as keeping view code DRY. However, rendering a partial for each item in a collection looks ugly:

<% @user.comments.each do |comment| %> <%= render partial: 'comments/comment', locals: { comment: comment } %> <% end %>

Luckily, Rails gives us this beautiful shorthand syntax:

render @user.comments

How does this magic work? Under the hood, render calls to_partial_path on each of our objects to determine which partial to render. Models that inherit from ActiveRecord::Base will return a partial name based on the model’s name by default. For example:

User.new.to_partial_path # => 'users/user'

You can override this:

class User < ActiveRecord::Base def to_partial_path 'users/profile' end end

This works with plain old Ruby objects too. Here’s a Null Object Pattern example:

class Guest def name 'Guest' end def to_partial_path 'users/user' end end

@dashboard.users_online is a mix of ActiveRecord User objects and non-persisted Guest objects:

<h1>Users online:</h1> <%= render @dashboard.users_online %>

The same partial is used for both guests and registered users:

<%= user.name %>

It gets even better. The objects in the collection can be of different types, each with their own partial.

@user.favorites can contain any combination of Article, Comment, and Image objects:

<h1><%= @user.name %>'s Favorites</h1> <%= render @user.favorites %>

app/views/articles/_article.html.erb:

<h1><%= article.name %></h1> <p><%= article.content %></p>

app/views/comments/_comment.html.erb:

<div class="comment"> <em>Last updated: <%= comment.updated_at %></em> <p><%= comment.content %></p> </div>

app/views/images/_image.html.rb:

<h1><%= image.title %></h1> <%= image_tag image.url %>

The beauty of this polymorphic approach is that we don’t have to write any conditional code. As long as all the objects in the collection define to_partial_path , it all just works.

If you found this useful, you might also enjoy: