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.