9.4. Helpers

Whereas in ASP.NET you have controls that you can drag and drop to help ease the development process, keep you productive, and maintain relatively lean pages, in Rails you have helpers. These handy methods provide a convenient way to write HTML controls, as well as perform other operations on a given input in order to provide the required output. For example, whereas in ASP.NET you have the following line of code:

<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>

in Rails, you would use the text_field or text_field_tag helper, depending on whether or not the resulting input tag needs to be bound to a particular model attribute.

Rails defines many helpers besides form ones. Consider, for instance, the pluralize and excerpt textual helpers:

<%= excerpt(@article, 'sed', 50) %>

<%= pluralize(@people.size, 'person') %>

This will display output such as the following:

...lis porttitor. Curabitur elementum risus eu eros. Sed elit. Praesent elit sapien, dictum ac,
ornare por...

3 people

Helpers offer multiple advantages. In particular, they encourage reuse, simplify the templates, ease development, make testing easier, adhere to the DRY principle, and discourage the inclusion of application logic within the view templates. Their usage is more than encouraged. Two types of helpers exist: those included by ActionView and those defined by the developer.

9.4.1. Predefined Helpers

Helpers defined by Rails are included in the module ActionView::Helpers. Consulting the API documentation will reveal a wealth of helpers that are available, as well as examples of their usage. It is, however, important to first understand how these helpers are categorized. ActionView::Helpers includes many helper modules itself, which in turn define the actual helper methods. These are the children modules:

  • ActionView::Helpers::ActiveRecordHelper

  • ActionView::Helpers::AssetTagHelper

  • ActionView::Helpers::AtomFeedHelper

  • ActionView::Helpers::BenchmarkHelper

  • ActionView::Helpers::CacheHelper

  • ActionView::Helpers::CaptureHelper

  • ActionView::Helpers::DateHelper

  • ActionView::Helpers::DebugHelper

  • ActionView::Helpers::FormHelper

  • ActionView::Helpers::FormOptionsHelper

  • ActionView::Helpers::FormTagHelper

  • ActionView::Helpers::JavaScriptHelper

  • ActionView::Helpers::NumberHelper

  • ActionView::Helpers::PrototypeHelper

  • ActionView::Helpers::RecordIdentificationHelper

  • ActionView::Helpers::RecordTagHelper

  • ActionView::Helpers::SanitizeHelper

  • ActionView::Helpers::ScriptaculousHelper

  • ActionView::Helpers::TagHelper

  • ActionView::Helpers::TextHelper

  • ActionView::Helpers::UrlHelper

Their names are, in most cases, self-explanatory and you can bask in the knowledge that you will not need to memorize them by heart. As you progress in your journey of learning Ruby on Rails, you'll become accustomed to the most frequent helpers defined by these modules, and the less familiar ones will be at your fingertips in the documentation.

ActiveRecordHelper contains helper methods like form or error_messages_for that simplify working with forms built around ActiveRecord objects. AssetTagHelper simplifies the generation of HTML for linking to assets like images, JavaScript files, CSS files, and so on. The helper auto_discovery_link_tag that you used a few paragraphs ago is defined in this module, and so are, among many others, javascript_include_tag (to include JavaScript libraries), stylesheet_link_tag (to link to a stylesheet), and image_tag.

AtomFeedHelper defines the atom_feed helper you just used in your Builder template, and BenchmarkHelper implements a benchmark helper that's used to measure the execution time of blocks passed to the method. The resulting time is then logged. For example, the following fragment will log (by default at the info level) a message such as "Render chat log (0.602143)":

<% benchmark "Render chat log" do %>
  <%= print_chat(@chat_log) %>
<% end %>

CacheHelper provides a cache method that can be used to cache fragments of a view template (both static and dynamic content). I talk more about caching, performances, and benchmarking in Chapter 11.

CaptureHelper implements two useful methods for code reuse: capture and content_for. capture is used to easily assign fragments of a template to an instance variable. That instance variable will then be available throughout the view layer. content_for allows you to mark a given fragment of view that was passed to the helper as a block, so that it can be reused elsewhere. You could, for example, identify a section as your footer:

<% content_for :footer do %>
 <!-- some footer content -->
<% end %>

and then render that content elsewhere:

<%= yield :footer %>

If you define several fragments with the same identifier, they'll be chained in the order that they were processed.

DateHelper is a module you should familiarize yourself with. As the name implies, it implements helpers for working with dates and times. Helpers exist that produce HTML select/options tags, such as select_date, select_datetime, select_day, select_hour, select_minute, select_month, select_second, select_time, select_year, date_select, datetime_select, and time_select, as well as handy methods that work with the date and time provided in input, such as distance_of_time_in_words, distance_of_time_in_words_to_now, and time_ago_in_words. If you wanted to display the time that's elapsed because an article was published in your blog application, rather than its publication date, you could use one of these helpers.

DebugHelper defines the debug method that was mentioned earlier in this chapter. This makes it easy to inspect the content of complex objects as they are rendered on the pages.

9.4.1.1. FormHelper and FormTagHelper

FormHelper and FormTagHelper are both modules that relate to form generation, but they differ in the fact that FormHelper methods generate code that assumes that the form is for a model object, whereas FormTagHelper methods do not. You will use one or the other, depending on whether or not you need to "bind" a form to a particular model object's attribute.

The presence of tag in the name of a helper indicates that it will generate HTML code that's not associated with a model object or its attributes.

The methods implemented by FormHelper are check_box, fields_for, file_field, form_for, hidden_field, label, password_field, radio_button, text_area, and text_field. All but fields_for and form_for accept a first argument, which indicates the object, and a second one that individuates the attribute that's being represented. Alternatively, when invoked on the block variable, these methods do not require the object (because it's provided by the receiver):

<% form_for(@article) do |f| %>
  <%= f.error_messages %>
    <% field_set_tag do %>
        <div class="field">
            <%= f.label :title %>
            <%= f.text_field :title %>
        </div>

        <div class="field">
            <%= f.label :body %>
            <%= f.text_area :body %>
        </div>

        <div class="field">
            <%= f.label :published_at %>
            <%= f.datetime_select :published_at %>
        </div>

        <div class="field">
            <%= f.check_box :published %>
            <%= f.label :published %>
        </div>
    <% end %>

    <% field_set_tag do %>
        <%= f.submit button_value, :class => "button" %>
    <% end %>

<% end %>

form_for creates the form for a model instance, which is something that it can do in a resource-oriented way (REST style), by receiving the instance in argument and automatically configuring the form. For example, in a "new" form, the code will be equivalent to:

<% form_for :article, Article.new, :url => articles_path, :html => { :class => "new_article",
:id => "new_article" } do |f| %>
  <!-- ... -->
<% end %>

However, in an "edit" form, the same code becomes (depending on the id attribute of the object):

<% form_for :article, @article, :url => article_path(@article), :html => { :method => :put, :class =
> "edit_article", :id => "edit_article_3" } do |f| %>
  <!-- ... -->
<% end %>

How does the helper know whether you intend the form to be used for the creation of new records or to edit them by populating the existing attribute values in the form? The model instance passed to form_for is used to verify if it is a new record (for example, using the method new_record?) or if it's an existing one, and the form is then rendered accordingly.

This is the preferred way to operate with form_for in Rails, but it is still possible to manually configure all the form parameters, as shown in the preceding output, as needed.

fields_for is an important helper because it allows you to create a scope around a particular model instance, without creating a form tag in the HTML. What this means in practice is that you can use fields_for any time you need a form that represents more than one model. You cannot have more than one form HTML tag, so using form_for twice in the same template is out of the question, but by using fields_for to wrap the controls for a second model instance, you can have multiple model forms with relative ease.

NOTE

Rails 2.3 simplifies the process of creating complex forms through the so-called Nested Object Forms. You can read more about this online at http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes

File Uploads

file_field is a helper that generates a file upload input tag that's associated with the model instance attribute. That said, dealing with attachments is a notoriously error prone, and somewhat annoying, problem.

For this reason, when dealing with attachments, many Rails developers prefer more refined solutions, such as those that are made available through plugins. Two popular choices are attachment_fu and Paperclip.

These facilitate handling uploads in Rails and add further features like the ability to thumbnail images, resize them, or store the uploaded file on Amazon S3's storage service.

You can find them online at http://svn.techno-weenie.net/projects/plugins/attachment_fu and http://github.com/thoughtbot/paperclip, respectively

FormTagHelper includes the following methods: check_box_tag, field_set_tag, file_field_tag, form_tag, hidden_field_tag, image_submit_tag, label_tag, password_field_tag, radio_button_tag, select_tag, submit_tag, text_area_tag, and text_field_tag. Again, these are meant to be used when the form does not have a corresponding model object.


9.4.1.2. FormOptionsHelper

FormOptionsHelper offers a quick way to convert collections into select/options tags. The methods implemented by this module are collection_select, option_groups_from_collection_for_select, options_for_select, options_from_collection_for_select, select, time_zone_options_for_select, and time_zone_select.

Consider the following usage of the select helper:

select("article", "category_id", Category.all.collect {|c| c.name, c.id },
{ :include_blank => 'None' })

This would generate the following:

<select name="article[category_id]">
  <option value="1">Python</option>
  <option value="2" selected="selected">Ruby</option>
  <option value="3">C#</option>
  <option value="4">Visual Basic</option>
  <option value="5">Delphi</option>
</select>

This assumes that the helper was placed within a form that was being displayed so as to edit an article, and as such you can imagine that the value "Ruby" was already selected because the article's category_id attribute is 2.

Consult the official API documentation to learn more about the other methods in the module and their usage.

country_select

In September 2008, before Rails 2.2 was released, the Rails team decided to remove the country_select and country_options_for_select helpers. The main reason for this decision was the lack of agreement on a definitive list of countries to be included and the culturally sensitive issue that this provoked. If you are curious, you can read about the controversy on the blog of a Rails Core team member at http://www.koziarski.net/archives/2008/9/24/countries-and-controversies.

Listing countries is still a recurring feature in many registration and shipping forms, though, so once again you can rely on plugins to help you out with this feature.

Currently a good choice for this purpose is the localized_country_select plugin, because it also leverages Rails' i18n features in order to provide a list of countries that are localized to the language of the application. You can find this plugin on GitHub: http://github.com/karmi/localized_country_select.


9.4.1.3. JavaScriptHelper, PrototypeHelper, and ScriptaculousHelper

These three modules are there to facilitate working with JavaScript and Ajax in your views (RJS templates or not). JavaScriptHelper implements several useful methods including the following:

  • button_to_function: Generates a button that triggers a JavaScript function. For example, button_to_function "Hello", "alert('Hello!')" translates into <input onclick="alert('Hello!')," type="button" value="Hello" />.

  • link_to_function: Equivalent to button_to but generates a link instead: <a href="#" onclick="alert('Hello!'), return false;">Hello</a>.

  • define_javascript_functions: Used to include all the JavaScript libraries that ship with ActionPack within a single <script> tag.

  • escape_javascript: Escapes carrier returns and both single and double quotes for JavaScript (that is, prefixing them with a backslash).

  • javascript_tag: Generates a JavaScript <script> tag that wraps the content passed as an argument or a block. For example, <%= javascript_tag "alert('Hello!')" %> returns:

    <script type="text/javascript">
    //
    alert('Hello!')
    //
    </script>

CDATA sections are used to allow modern browsers to interpret the content as a script, while being safely ignored by XML parsers (which would otherwise interpret it as XML).

Prototype is one of the most popular JavaScript frameworks; it simplifies working with the DOM (Document Object Model), Ajax, and object-orientation in JavaScript. Rails ships with this library and provides you with the PrototypeHelper to access its functionalities from Ruby.

Prototype ships with Rails, but it must be included in your application in order to be able to use it, as you'll see later on.

This module implements several helpers that conveniently wrap the Prototype API. Many of these contain the word remote to distinguish them from the regular helpers that are used in non-Ajax forms. For example, you have remote_form_for and its alias form_remote_tag in place of form_for and form_tag, and you have link_to_remote instead of link_to, or again submit_to_remote. When a remote_form_for or its alias form_remote_for is used, the form will be submitted in the background using XMLHttpRequest, instead of sending (by default) the POST request and reloading the page. For instance, consider the following:

<% remote_form_for(@article) do |f| %>
  <!-- ... -->
<% end %>

This would translate into a <form> tag along the lines of the following:

<form action="/articles/3" class="edit_article" id="edit_article_3" method="post"
onsubmit="new Ajax.Request('/articles/3', {asynchronous:true, evalScripts:true,
parameters:Form.serialize(this)}); return false;">

When the form is submitted, a new asynchronous Ajax.Request is instantiated. In fact, as fancy as it sounds, Ajax is all about being able to send an XMLHttpRequest from the browser to Rails, so that a background request for a given action is initiated without having to completely reload the page. Rails will then be able to process the action and update the page accordingly. The helper update_page yields a JavaScriptGenerator, which can be used to update multiple elements on the same page, for a single request as it's received. I highly recommend that you read the extensive API reference for the PrototypeHelper and for PrototypeHelper::JavaScriptGenerator::GeneratorMethods so as to gain familiarity with working with Prototype in Rails.

Firebug and the Web Developer Toolbar

Most Web developers, including myself, use Mozilla Firefox as their browser of choice. Though testing for Internet Explorer compatibility is still done, it is rare to see a Rails developer who consciously chooses to use Internet Explorer over other open-source browsers that are available. The main reason for this is not purely ideological, but finds its roots in the advantages and far smaller number of quirks that Firefox provides. In fact, many Web applications end up requiring special arrangements to guarantee compatibility with the latest versions of Internet Explorer.

Two killer Firefox add-ons that are must-haves for any Web developer are Firebug and the Web Developer toolbar. Firebug in particular is essential for any developer who intends to work with Ajax applications. This Firefox extension provides a console, a DOM inspector, HTTP traffic analysis, and many other fundamental features that are necessary to be able to debug the client side. If you haven't already done so, be sure to install Firefox, and get Firebug and Web Developer. I cannot stress enough how much these three will improve your development workflow.

No matter how good Firefox is, it's important to test your application with multiple browsers, particularly with Internet Explorer; and because it is with this browser that most compatibility quirks arise, it makes sense to have similar tools for Internet Explorer as well. Though not as refined as their Firefox counterparts, two equivalent tools for IE have been released: the IE Developer Toolbar and DebugBar. I highly encourage you to get and install them as well.

Other browsers have similar projects underway to provide comparable functionalities. For example, Opera is developing a Firebug equivalent known as Dragonfly.


script.aculo.us is a JavaScript library that's built on top of Prototype and its main aim is to provide visual effects and nice-looking Ajax controls. ActionPack ships this library as well, and just like Prototype, it needs to be included explicitly in your application before you're able to use it. Prototype is also required because script.aculo.us builds on top of it. Luckily, Rails provides a convenient way to include them both in your layouts via the include_javascript_tag helper, as you'll see later on in a practical Ajax example.

ScriptaculousHelper defines four highly configurable helpers: draggable_element, drop_receiving_element, sortable_element, and visual_effect. This last method is often used in conjunction with other Ajax helpers to give them nice visual effects once the background request has been completed. This can be done inline:

<%= link_to_remote "Load Results",
             :update => "results",
             :url => { :action => "results" },
             :complete => visual_effect(:highlight, "results", :duration => 0.7) %>

or it can be used more elegantly in an RJS template. In Appendix 4, I've provided you with resources and a bibliography to help you explore the topic of Ajax further. You will also see a quick, concrete example in the section titled "Adding a Sprinkle of Ajax."

Many more Ajax functionalities are available through plugins. Browse http://agilewebdevelopment.com/plugins/list and http://www.railslodge.com/plugins for an extensive list of available plugins.

9.4.1.4. Other Helpers

NumberHelper provides helpers that convert numbers into strings. The exposed methods are number_to_currency, number_to_human_size, number_to_percentage, number_to_phone, number_with_delimiter, and number_with_precision. They are useful at times, but admittedly not revolutionary at all.

SanitizeHelper and TextHelper, respectively, offer methods that sanitize potentially unsafe user input and work with text. TextHelper in particular has many interesting methods including auto_link, which automatically recognizes and transforms text into links; pluralize and excerpt, which were mentioned before; truncate, word_wrap (which forces the wrapping of long lines of text), and highlight (for highlighting certain words in the input text); simple_format to apply simple transformation rules to the input text like adding a <br /> tag after a single newline; and the familiar textilize and markdown.

UrlHelper is a module that contains many of the methods used in previous chapters. Besides link_to, button_to, and url_for, this also implements the link_to_if, link_to_unless, link_to_unless_current, current_page?, and mail_to helpers. The first three are conditional links, each of which has a tag that is generated only when a given condition is satisfied. link_to_unless_current is particularly handy when creating navigation menus where you do not want the current page to be linked. When visiting the Archive page, the following will render links for the Home and About pages only:

<ul id="menubar">
  <li><%= link_to_unless_current("Home", { :action => "index" }) %></li>
  <li><%= link_to_unless_current("Archive", { :action => "archive" }) %></li>
  <li><%= link_to_unless_current("About", { :action => "about" }) %></li>
</ul>

current_page? verifies that the current URI was generated by the options passed in argument to the helpers, and mail_to generates a link to a mailto:, which triggers the user's default mail client to send emails client-side.

The three remaining, not particularly common helper modules are TagHelper, RecordTagHelper, and RecordIdentificationHelper.

TagHelper allows you to define tags programmatically (check out the tag and content_tag helpers) and RecordTagHelper exposes methods that create HTML elements whose id and class parameters are tied to a specified ActiveRecord object. Finally, RecordIdentificationHelper exposes three delegate helpers used by the view to identify conventional names for markup code from ActiveRecord and ActiveResource objects.

9.4.2. Creating Helpers

Predefined helpers will take you a long way when it comes to Rails applications, but helpers would be very limiting if it wasn't for the ability to define your own.

To define a helper all you have to do is declare a method in a helper module. By default, application-wide helpers defined by the user are contained within ApplicationHelper in apphelpersapplication_helper.rb, whereas controller-specific helpers are defined in their own module/file. For example, if the controller is ExampleController, the associated helper will conventionally be in ExampleHelper, which in turn is defined in apphelpersexample_helper.rb.

helper and helper_method

The helper method is used to indicate which helper module should be made available for templates that correspond to a particular controller. Because helper :all is present within ApplicationController, all the helpers within the folder apphelpers will be available as well.

It's important to understand that helpers are available to the view layer (including other helper modules) only, and not to controllers. If you tried to use the method time_ago_in_words in a controller, for example, you'd get an error because the method is not defined in that scope.

In the rare instances where a user-defined helper needs to be available to both the controller and the view, you can define a method in the controller and then use helper_method to declare it as a helper (for example, helper_method :my_helper). As usual, don't abuse this facility, because it's important not to mix presentation and business logic, and to respect the Separation of Concerns principle.


Custom-defined helpers are regular methods that accept an input (usually a few options) and spit out an output that's normally used in the rendering of a page. For example, consider the following trivial helper:

module ApplicationHelper
  def three_times
    3.times { yield }
  end
end

Once defined, this would allow you to insert the following in any ERb template:

<% three_times do -%>
  <para>Lorem Ipsum...</para>
<% end -%>
</programlisting>

and obtain:

<p>Lorem Ipsum...</p>
<p>Lorem Ipsum...</p>
<p>Lorem Ipsum...</p>]]>

In all fairness, this helper is not terribly useful, but it shows the flexibility of this approach. This is particularly true when you consider that any predefined Rails helper is available within helper modules, and as such, it is possible to create helpers that build on ones that already exist.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.93.64