Disambiguate Rails helpers

Eric Torrey

Here at thoughtbot we strive to make explicit, agile code. We know the specification changes, it’s what we’re machined for. The place that changes the most, and is usually the hardest to maintain are the views. As my colleague pointed out the views usually run wild, so we use helper methods to prevent a long method chain, and keep things a little more readable. Sometimes however that usually leads to methods that aren’t used, or methods that are duplicated.

For example. Let’s assume you have a Blog, that belongs to a User, and has many Posts. Pretty standard right? There is no requirement for the Blog to have it’s own title so instead we use a helper method. On blogs#show we use a Blog helper method defined here:

module BlogsHelper

  def display_title_for(blog)
    "#{blog.user.name} - The Blog!"
  end

end

Good, now when a user clicks on a Post for that Blog, we get to posts#show. At the top of the page, we want to display the title of the Blog. So we try to call #display_title_for and get a NameError, something along the lines of undefined local variable or method.

We can’t use the method in the BlogsHelper from the Post’s views yet. We could go into the Post’s controller and add this:

class PostsController < ApplicationController

  helper :blogs

  def show
    @post = Post.find params[:id]
  end

end

I don’t like this solution, sure your being DRY, but your not being explicit. What happens when someone wants to change the display of the Blog’s title, and they are looking at the posts#show view? They will immediately look for a displaytitlefor(blog) in the Post’s helper - too bad it’s not there.

Another solution would be to duplicate the method on the PostsHelper. Although this also is a poor solution, if someone wants to change the way the blog title is being displayed, it will need to be changed in two spots.

So, I want helpers to be called explicitly, and defined explicitly. I want to know what is being called, and where it’s being called from. Let’s redefine the display_title_for(blog) on the BlogsHelper:

module BlogsHelper

  def self.display_title_for(blog)
    "#{blog.user.name} - The Blog!"
  end

end

Now that we have defined it as a module method we can use it in the post#show view like so.

BlogsHelper.display_title_for(@post.blog)

Now we have explicit, dry, maintainable code.