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

Rendering with Erubis and Rails 2.0

Posted by Eric Chapweske
on Monday, December 10

Update: ActionView has been refactored in Rails 2.0.2, making Erubis’ Rails helper, and the “Create an Erubis Initializer” section of this article, obsolete. See the comments for a Rails 2.0.2 compatible initializer. Thanks for the tip, Jason!

Erubis is a drop in replacement for Erb. Among its many features are a few notable improvements in terms of speed and security (it optionally supports auto-html escaping).

Sample Erubis Syntax:
1
2
3
4
5
# Erubis with auto HTML escaping enabled:

Hello, <%= current_user.name %> # equivalent to h(current_user.name)

<%== render :partial => 'user' %>

Installing Eribus:

1. Install the gem


gem install erubis

2. Create an Erubis initializer

app/config/initializers/erubis.rb
1
2
3
4
5
6
7
8
9
10
11
# Via http://www.kuwata-lab.com/erubis/users-guide.05.html#topics-rails
# The above link also references an optional patch that can be applied.

require 'erubis/helpers/rails_helper'

# These are optional settings:
Erubis::Helpers::RailsHelper.init_properties = { :escape => true, :escapefunc => 'h' }

# Erubis::Helpers::RailsHelper.engine_class = Erubis::Eruby # or Erubis::FastEruby
# Erubis::Helpers::RailsHelper.show_src = false
# Erubis::Helpers::RailsHelper.preprocessing = true

3. Create custom rescue templates

The default Rails debug views need to be slightly modified to support Eribus. This problem only pops up in a few spots, but Eribus doesn’t handle inline statements:

1
2
3
4
5
6
7
# Default Rails sample:
<%= request.parameters["controller"].capitalize if request.parameters["controller"] %>

# Erubis compatible rewrite:
<% if request.parameters["controller"] %>
<%= request.parameters["controller"].capitalize %>
<% end %>

If auto-escaping is enabled, all instances of <%= need to be replaced with <%== and <%=h replaced with <%=.

Step 1: Download these auto escaped, Eribus-friendly templates and put them in app/views/rescues

Step 2: Redefine the rescues path to point to the modified templates:

controllers/application.rb
1
2
3
4
# For Erubis compatible debug templates  
def rescues_path(template_name)
  "#{view_paths.first}/rescues/#{template_name}.erb"
end

Using Erubis

Auto escaped Erubis vs Erb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Erubis doesn't handle inline parsing well.
# While in Erb, this would work:
# <%=h status = current_user.status if returning_user? %> 
# It needs to be broken out in Erubis:

<% if returning_user? %>
  <%= current_user.status %> # Auto HTML escaped
<% end %>
                                                                          
Here's a list of your friends, <%= current_user.first_name %>        # Auto-escaped
<ul>
  <%== render :partial => 'friend', :collection => @friends %>       # Not escaped
</ul>
<p><%== submit_tag "Add a friend", :class => "button" %></p>         # Not escaped
<% end %>
A few things to remember:
  • Erubis doesn’t handle inline statements well
  • If auto escape is enabled, use the <%== operator when rendering partials and helpers
  • Update app/views/layout templates
References