Friday, 17 October 2008

The View Part 2 - Scope and Helpers

There are significant limits to what you can access from a view.

For local variables, you can only access variables defined in that view (anywhere in the file, not just that section of Ruby). You cannot access local variables in the controller, or in another view (not even in the layout that gets processed with your view).

You can access instance and class variables in your controller, as long as they have already been defined in the respective method (I guess a new instance is created with each web request). You cannot access instance and class variables that you have defined by calling a method from the view (they will always be null).

This brings us to helpers. Helpers are methods in modules that you keep mostly hidden out of the way. The idea is to keep as much Ruby code out of the views, so helpers are mainly for use in that context. Helpers are the only methods you can access, other than instance methods for an instance you have access to.

In the helper folder
You can put your helpers in the helper folder. Rails puts in a helper :all directive, which will automatically load any helpers it finds there, as well as creating an ApplicationHelper file, plus a helper file for each controller or scaffold you generate. In this configuration all helpers are available to all views, regardless of what file you put it in.

Helpers are set up just like any other Ruby method, the only difference being that the file is a module, not a class.

module ApplicationHelper
def project_name
'My Great Web App'

The file must be named in the format "_helper.rb", and the module named "Helper".

In the lib folder
Helpers can go into the lib directory, with a filename of the form _help.rb. Again, these are modules, not classes, and look just as before. You need to point Rails to the file, with this in the controller:
helper :myfile

Rails will then look for myfile_helper.rb, which should contain a module MyfileHelper.

In the controller
If you want to be able to access your helper methods in your controller too, your only option is to put them into the controller, and flag them as helpers.

To be able to access the methods, you need to put them in your controller (or application controller to allow all views and controllers to use them). the methods are set up as normal, but you need a "helper_method" macro at the top to register the specified methods as helpers.
helper_method :myfirstmethod, :mysecondmethod

Accessing Rails' helpers
You can use the helpers in Rails in your own helpers (at least in your helpers folder). For example, the following will set up a quick link to page, using the standard link_to helper.
def link_to_log
link_to 'Sample Log', { :action => 'home', :controller => 'samples' }, :method => :get
As an aside, I once had a method, select_options, in a controller that was used by several views, and worked fine. I decided it would be better in a helper, and so moved it across. It stopped working, the interpreter complaining there was no such method. Other methods in the helper file worked fine, even in the same view. After some head scratching, I decided that the problem was due to a name conflict; changing the name of the method got it working again. Why would it fail to find the method (as opposed to invoking the other method or complaining about the wrong number of arguments)? I do not know. However, should your view fail to find a helper method, this could be the reason.

Testing Helpers

Naturally you will want to test your helpers. You may be able to do this in your unit tests, but you may require the infrastruction that comes with a functional test - and anyway, helpers are for views/controllers, not models, so it makes more sense here.

Here are the bare bones of a test file:
require 'test_helper'

class AppHelperTest < ActionController::TestCase
tests SampleLog::GlcsController

include ApplicationHelper
include ActionView::Helpers::UrlHelper
include ActionView::Helpers::TagHelper

def test_link_code
# code here
Note that it is necessary to specify which controller you are testing. It may not matter; just pick any. To get access to your new methods, include the helper class. To get access to other methods, include them too. In the above, I wanted access to link_to in ActionView::Helpers::UrlHelper, which in turn accessed escape_once in ActionView::Helpers::TagHelper.

For more on helpers see also:

Edit: This page originally hada section on partials, however, partials have changed a lot in Rails 2.2.2, and most of it was no longer true. See this page:

Struggling with Ruby: Contents Page


Tarun said...

I have observed some kind of name conflict.

I created two helper methods of the same name in two different helper modules. This seems to be generating an error as one of them is not getting recognized.

Are all helpers in a single universal scope ?
Do they need to have unique names, even if they are in different modules (helper files for two different controllers)...

I am not putting them in application helper.

Tarun said...

Found an explanation here -

Deepak in action said...

can some one throw light on scope of a variable over a partial,