Many Rails applications have this basic structure in their helpers folder:
1 2 3 4 5 6 7 8 9 10 11 |
application_helper.rb accounts_helper.rb audits_helper.rb comments_helper.rb images_helper.rb orders_helper.rb posts_helper.rb sessions_helper.rb users_helper.rb ... etc. |
The most important file, as we all know, is application_helper.rb, because this is where code goes to die. It’s often a few hundred lines of randomly added, unrelated methods. This is a confusing, scary place for methods to be. Here’s a few tips for rescuing them:
What’s that noise?
Most projects use script/generate to make their controllers. This leaves a ton of empty helper files. Remove them to better focus on the task at hand:
1 2 |
hg remove accounts_helper.rb audits_helper.rb images_helper.rb ... |
Usually this will prune the list down to two or three files.
Farewell, application_helper.rb
The easiest way to clean up the ApplicationHelper module is to remove it. This is a great way to ensure methods don’t stay there, or get inserted in the future. But, if they don’t belong in ApplicationHelper, where’s the best place for them?
1. Remove fake helpers
Helpers are markup generators. If they’re not involved in generating markup, they’re not helpers and can be pushed into a model:
helpers/application_helper.rb1 2 3 4 5 6 |
module ApplicationHelper def birthday_in_words(child, prefix = 'born') "(#{prefix} #{child.birthday_in_words})" if child.birthday? end end |
1 2 3 4 5 6 |
class Child < ActiveRecord::Base def birthday_in_words(prefix = 'born') "(#{prefix} #{birthday})" if birthday? end end |
Unfortunately Rails relies on this ambigious ‘helper’ naming convention internally, making it tricky to change the naming in your own application. (I find the concept of a helper to be… unhelpful, and will be referring to them as ‘markup generators’ for the rest of this post.)
2. Separate into logical units
By default, Rails makes all markup generators available to any view via helper :all. The relationship between a model and markup generation tends to be incidental, and script/generate’s ‘ModelNameHelper’ convention is a bit sketchy. Better to name it like anything else, so a module that generates, say, HTML for tables, gets named TableHelper.
1 2 3 4 5 6 7 8 9 10 |
module TableHelper def default_sort_column(title, direction) ... end def sort_column(title, direction) ... end end |
Better yet, if the generation starts getting complex, take a page from one of Ryan Bate’s screencasts and turn it into a class
3. Test!
Well organized code is great. Tested, well organized code? Even better!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
require File.join(File.dirname(__FILE__), '..', 'test_helper') require File.join('action_view', 'test_case') class AlexaThumbnailHelperTest < ActionView::TestCase context "Generating Alexa image tags" do setup do @url = 'http://ted.com' @alexa_image_tag_html = %(<img src="http://ast.amazonaws.com/?...=#{@image_url}"/>) end should "return the image tag as html" do assert_equal @alexa_image_tag_html, alexa_image_tag(@url) end end end |
And a method
On a related note, in a few cases it’s useful to allow your templates access to controller methods. Rails provides helper_method to handle this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class ApplicationController < ActionController::Base # Give views access to these methods: helper_method :current_user, :logged_in? protected def current_user ... end def logged_in? ... end end |
