How to fix your Rails helpers

Posted by Eric Chapweske
on Thursday, August 21

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.rb
1
2
3
4
5
6

module ApplicationHelper
  def birthday_in_words(child, prefix = 'born')
    "(#{prefix} #{child.birthday_in_words})" if child.birthday?
  end
end
models/child.rb
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.

helpers/table_helper.rb
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:

controllers/application_controller.rb
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
References
Comments

Leave a response

  1. justinAugust 21, 2008 @ 09:03 PM

    ” hg remove” was something new to me. Learn some thing new everyday . Nice article.

  2. grosserAugust 22, 2008 @ 12:37 AM

    I usually allow new methods to live in application_helper until there are 2-3 related methods that could be extracted into a separate helper.

    Using a ton of helper modules with 1 method each is counterproductive since i always have to remember if the xx method was in abc_helper or bcd_helper. It also reduces rework since i do not have to name/create/create-testfile for the new helper, which often would change its name or join with someone else faster than i can say “oh no, we wanted this to look like…”

    Summary: Let the lonely live in application helper, extract groups of 2-3

  3. Ed SpencerAugust 22, 2008 @ 06:01 AM

    Great writeup. Although I do create new helper modules like your TableHelper module, I never really remove the empty ones – I’ll certainly start now. No hg on Windows though sadly :(

    You’re also making me feel bad about not testing my helpers. I test everything else so they must be feeling a little left out. My shame has almost reached the threshold where I’ll start giving them some testing love… almost :)

  4. Luke FranclAugust 22, 2008 @ 09:06 AM

    FYI: hg remove is a Mercurial command. It’d be like doing svn rm or git rm.

    I plead guilty to some of the code in this article, but I still like the idea of one honking big application_helper.rb. Why? I like to know where to find the methods! (I guess this would be a moot point if I used a TAGS file but I don’t.)

    Eric’s got a good point about non-markup helpers though. Those don’t belong in the helper files. They should be in the model.

  5. Stephen CelisAugust 22, 2008 @ 10:28 AM

    Helpers are markup generators. If they’re not involved in generating markup, they’re not helpers and can be pushed into a model.

    I’m not so sure about this. Helpers are abstractions of view logic, and don’t necessarily generate markup. There’s a reason why methods like time_ago_in_words are helper methods and not instance methods on Time.

    Child#birthday_in_words truly does belong in a helper…just not in ApplicationHelper, if possible.

  6. AdamSeptember 01, 2008 @ 03:50 AM

    So do you see this as encapsulation vs DRY?

  7. Jon DahlSeptember 03, 2008 @ 07:13 AM

    Interesting – DHH recommended this exact thing at his RailsConf Europe keynote today. Ditch your ApplicationHelper, or that methods shouldn’t stay in the module long-term.