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

5 little-known Rails methods

Posted by Eric Chapweske
on Wednesday, April 23

While the next release of Rails appears to be coming up, there’s still plenty of small, useful features from previous releases that aren’t widely used.

A few of my favorites:
  1. query_attribute
  2. polymorphic_path
  3. debug
  4. rake -T `query` ( Not Rails specific, but still handy! )
  5. extract_options!

1. ActiveRecord’s query_attribute

Query methods are available for each of a record’s attributes, providing for a cleaner way to check for the presence of an attribute.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# == Schema Information
# Schema version: 17
#
# Table name: users
#
#  id                    :integer(11)     not null, primary key 
#  first_name            :string(255)     
#  last_name             :string(255) 

# Original
class User < ActiveRecord::Base
  def named?
    !first_name.blank? && !last_name.blank?
  end
end

# Refactored to use query_attribute
class User < ActiveRecord::Base
  def named?
     first_name? && last_name?
  end
end

2. Indifferent links with polymorphic paths.

Rails has polymorphic edit/new/formatted path routing available out of the box. Providing an array will namespace the path with those array parameters. Available Methods: (edit|new|formatted|)polymorphic_path(record_or_hash_or_array)

1
2
3
4
5
6
7
8
9
10

# Before:
<% if @record.is_a?(User) %>
<%= user_path(@record) %>
<% elsif @record.is_a?(Friend)
<%= friend_path(@record) %>
 ... etc.

# After:
<%= polymorphic_path(@record) %>
Quite a few options are supported, and other Rails methods take advantage of polymorphic routing:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Paths can be namespaced:
#=> admin/users/5/edit
edit_polymorphic_path([:admin, @record])

# Polymorphic urls are also internally used by helpers:
# redirects to store_path(@store)
redirect_to @store
  
# builds a form with an action to 'new_admin_stores_path'
#=> <form action="admin/stores/new" ... />
form_for([:admin, Store.new])

# <a href="/stores/5">A store in Minneapolis, MN</a>
link_to @store.name, @store

3. debug

Especially useful when starting out a project, this is quick way to understand what objects are being used in the view.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


<%= debug @user %>

# Yields this in the view:
# - !ruby/object:User 
#  attributes: 
#    salt: 7be4287a1b27426fa6e5b6d733c707dd66425e82
#    updated_at: 2008-04-22 19:01:23
#    crypted_password: abb611def895dac923ba8ea59a78451f77473d5e
#    id: "1"
#    first_name: Eric
#    last_name: Chapweske
#    created_at: 2008-04-06 01:45:33
#  attributes_cache: {}

4. rake -T task

This is a handy Rake feature, and not limited to Rails. Can’t remember the exact syntax for a particular rake task? Trim the results generated with `rake -T` with an optional search parameter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

$rake -T

rake annotate_models                 # Add schema information (as comments)...
rake audit:purge                     # Removes Audit records older than 2 m...
rake db:abort_if_pending_migrations  # Raises an error if there are pending...
... etc.
rake tmp:sockets:clear               # Clears all files in tmp/sockets

// Searching by the task's name

$rake -T db:migrate:r

rake db:migrate:redo   # Rollbacks the database one migration and re migrat...
rake db:migrate:reset  # Resets your database using your migrations for the...

5. extract_options!

While not needed very often, Rails comes bundled with a method to extract the options from methods that utilize the splat operator. This method removes the last object from an array if it’s a Hash, otherwise an empty hash is returned.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16


class Story < ActiveRecord::Base

  # Example: 
  # Story.published_and_tagged_with('deep', 'thoughts', :order => 'created_at desc') 
  # The generated options for this method look like this: 
  #=> { :include => :tags, :order => 'created_at desc' }
  def self.published_and_tagged_with(*tag_names)
    options = tag_names.extract_options!
    options[:include] ||= :tags
    
    ...
  end
end

References

I wasn’t able to find any write ups on the above methods, so reading the source code may be the best path if you’re curious about their exact implementations.