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 %>
Partial paths
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
POROs
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 %>
Heterogeneous collections
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.
What’s next
If you found this useful, you might also enjoy:
- Ruby Science to read more about replacing conditionals with polymorphism in Ruby
- Using Polymorphism to Make a Better Activity Feed in Rails to see another example of polymorphic Rails partials