One of my favourite “magical” characteristics of Ruby on Rails is its naming conventions. We don’t need to argue about what the name of the database table that will hold information about a widget should be called, it’s widgets. Other frameworks, especially really old ones, didn’t do that.
Now, Rails is also great at letting you override and tweaks those options, but really, you almost never need to.
This same convention over configuration mindset continues throughout the framework, even to rendering stuff, which is what we are going to be looking at today.
With Rails, when you pull a record from the database, you can simply call <%= render @widget %>
in your view to spit that thing out on the page. But what is it really doing? Well, assuming you haven’t changed any defaults, this is going to make a bunch of sane guesses:
@widget
is an instance of the Widget
class so the partial we are going to use is widgets/_widget
This means <%= render @widget %>
is effectively <%= render 'widgets/widget', locals: { widget: @widget } %>
.
That’s a lot of extra code that you just don’t need. …unless you do.
I frequently view my widgets in different ways, and have different partials for them:
_widget.html.erb
is the full page view_widget_row.html.erb
will be used in administrative table views_widget_card.html.erb
will be a concise view for use throughout the siteThis means I can’t use the simple render call, I need to do one with the partial defined: <%= render 'widgets/widget_row', widget: @widget %>
. That’s slightly annoying but tolerable. It even works with arrays. Unless you are rendering an array of mixed things: <%= render @things %>. The problem here is that you need to do a different partial for each instance in the array potentially.
Well, I’m not going to let that stop me. Inspired by dom_id(@widget, :specialization)
I decided to solve my rendering pain. Adding an additional helper gives me a flexible solution that feels like render
and dom_id
had a baby. Introducing polymorphic_render:
module ApplicationHelper
def polymorphic_render(resource, suffix = nil, options = {})
if resource.nil?
return
elsif resource.is_a? Array
capture do
resource.each { |res| concat polymorphic_render(res, suffix, options) }
end
elsif resource.respond_to? :to_partial_path
path = [resource.to_partial_path, *Array(suffix)].compact_blank.join('_')
name = File.basename(resource.to_partial_path)
render options.merge(partial: path, object: resource, as: name)
else
# maybe Rails can figure it out
render resource, options
end
end
end
The third branch is the workhorse of this method. It will wet the partial path to the normal partial path (widgets/widget
) and append the suffixes to it. It gets the appropriate name of the instance, and it passes all that to the normal render method.
Basically it does all the manual stuff we normally do, but automatically.
This makes feed rendering code so nice. Instead of some nested monstrosity, its just a simple call to <%= polymorphic_render @feed.items, :feed %>
and the correct things/thing_feed
partial will be used.