Quantcast

Ruby-on-Rails and AJAX

(No Ratings Yet)
Loading ... Loading ...

If you're new here, you may want to subscribe to my RSS feed. So that you can read the latest updates about Web2.0 tools, Making Money Online, Tips in SEO, Ajax and many more. Thanks for visiting ProgramimiCOM!

Java and Flash offer real alternatives for those trying to build web applications that function more like desktop applications. However, they have their shortcomings. AJAX overcomes these shortcomings — especially if you use Ruby on Rails.

Interactivity and responsiveness has been considered the USP of desktop applications and not of web applications. Even though there exist mechanisms such as Java Applets and Flash files that provide interactivity and responsiveness up to a point, they are intrusive. That is, the user needs to install either a Java or a Flash plug-in.

However, with AJAX coming into the foreground, a non-intrusive way of providing interactive and responsive web applications has evolved. Even with AJAX, the problem is not fully solved for developers. The reason for this is the peripheral integration of AJAX in almost all the existing frameworks. Still, there are exceptions in the form of frameworks that provide AJAX-based functionalities as their core services. Ruby-on-Rails (RoR) is one of them.

In this discussion, I will be focusing on using AJAX with RoR. The first section will be a brief overview of AJAX. In the second section, I will detail the steps involved in AJAXifying the RoR application. In the last section, I will be developing a real world application using AJAX and RoR. That’s the agenda for this discussion.

AJAX: an Overview

By definition AJAX is “a technique that extends the traditional web application model to allow for in-page server requests.” In other words, AJAX provides the functionality to make on the fly requests to the server and present the response data to the user by modifying a part of the page without refreshing the whole page. AJAX is not a single technology; rather, it’s a combination of technologies. These technologies form the basic components of AJAX, which are:

  1. JavaScript
  2. XMLHttpRequest
  3. XML

The second component is an object of JavaScript itself. However the first ‘A’ of AJAX comes from it. The reason for this is detailed below.

JavaScript is the “J” in AJAX. It is the JavaScript that parses the response from the server and makes appropriate changes to the current page. Using JavaScript, one can change a paragraph, replace a product image or change the complete look and feel of the page without the intervention of the server. This reduces the bandwidth and makes the web site look more responsive to the user.

XMLHttpRequest is an object of JavaScript that not only allows the user to create or construct HTTP calls from within the script but also process the response sent by the server. It is available as an ActiveX control in Internet Explorer and as a JavaScript object in other browsers. Though it is known as XMLHttpRequest, one can wrap any kind of HTTP communication using it.

The first ‘A’ in AJAX, which stands for “Asynchronous,” comes at the “behest” of XMLHttpRequest. One of the abilities of XMLHttpRequest is to make a call to the web server in the background while the user continues to do his/her work on the UI. Whenever the response is received, the XMLHttpRequest accepts it and passes it over to the function that is registered to handle the response. Thus the request-response cycle is relieved off the paradigm of synchronous communication.

If one looks up the definition of XML on the net, the most common definition found is this: “XML (Extensible Markup Language) is a W3C initiative that allows information and services to be encoded with meaningful structure and semantics that computers and humans can understand and is great for information exchange, and can easily be extended to include user-specified and industry-specified tags.” The extensibility and the ability to provide data in a structured format is the main reason for XML to be the preferred response data format sent by the server. This doesn’t mean that the server can’t send data in any other format. It can send data in any format and XMLHttpRequest can accept and process it. However, since XML is a standard that is both extensible and structured, it is preferred over every other.

The next obvious question is how these three components fit in the overall picture. Here is an example of AJAX at work. It checks the validity of the username.

var xmlHttp;
function createXmlHttpRequest()
{
//check the type of browser and initialize XMLHttpRequest object
if(window.ActiveXObject)
{
xmlHttp=new ActiveXObject(”Microsoft.XMLHTTP”);
}
else if(window.XMLHttpRequest)
{
xmlHttp=new XMLHttpRequest();
//alert(”inside elseif…”+xmlHttp);
}
}

//This method creates an instance of XMLHttpRequest and calls the server
//Then delegates the data processing to another function on receipt of data
function startRequest()
{
createXmlHttpRequest();

var u1=document.f1.user.value;
xmlHttp.open(”GET”,”validate.php?user=”+u1,true)
xmlHttp.onreadystatechange=handleStateChange;
xmlHttp.send(null);
}

//Does the handling of data and presentation of data
function handleStateChange()
{
if(xmlHttp.readyState==4)
{
if(xmlHttp.status==200)
{
document.getElementById(”results”).innerHTML= xmlHttp.responseText;
}
else
{
alert(”Error loading pagen”+ xmlHttp.status +”:”+ xmlHttp.statusText);
}
}
}

function resetDisplay()
{
document.getElementById(”results”).innerHTML=” ”

}

The logic here is very simple, yet the amount of boilerplate code required is pretty high and the complexity of JavaScript increases with the increase in complexity of the logic of data processing and presentation. And I have not shown the logic at server-side. This is the story with almost all of the technology except Ruby-on-Rails. How RoR is different from the others is the topic of the next section.

Implementing AJAX

In the last section, the example demonstrated the complexity involved in AJAXifying an application having even the simplest logic to be implemented. So how can RoR make it simple? RoR does it by providing AJAX as one of its core functionalities. RoR has the prototype, effects, dragdrop and controls JavaScript libraries as built-in libraries. Secondly, a helper called JavaScriptHelper provides wrapping around JavaScript code so that switching between Ruby and JavaScript won’t be necessary. So RoR makes things simpler by wrapping JavaScript libraries and providing them as built-in RoR libraries.

Now let’s look at ways to use AJAX within RoR. To implement AJAX, RoR provides two basic ways which are:

  1. Using link_to_remote()
  2. Calling form_remote_tag()

The former is used with non-form HTML whereas the later is used with form and form based HTML elements.

The link_to_remote() is one of the simplest yet versatile and flexible helpers in RoR. It takes following parameters:

  1. the text for the link
  2. the id of the link
  3. the URL of the action to be called when the link is clicked
  4. the position which tells Rails to insert the response instead of replacing the existing content. This is an optional parameter used with the advanced form of link_to_remote().

So what are the steps required to use link_to_remote()? The steps are as follows:

  1. Create the page to be shown to the user
  2. Implement the controller to be called by link_to_remote()
  3. Develop the page that will provide the response

The steps are common to that of any typical RoR. That is the beauty of RoR. Of these steps, the third step is optional if rendering is taken care of by the action itself.

  1. Create the page to be shown to the user:This step involves creation of an RHTML template with the addition of link_to_remote(). However, to call it, another helper will have to be included - javascript_include_tag. Using this one can include any of the four JavaScript libraries. For calling link_to_remote() we need the prototype library. So the statement would look like this:

    <%= javascript_include_tag “prototype” %>

    Here is an example of a page having link_to_remote():

    <html>
    <head>
    <title>Ajax Demo</title>
    <%= javascript_include_tag “prototype” %>
    </head>
    <body>
    <h1>What time is it?</h1>
    <div id=”time_div”>
    I don’t have the time, but
    <%= link_to_remote( “click here”,
    :update => “time_div”,
    :url =>{ :action => :say_when }) %>
    and I will look it up.
    </div>
    </body>
    </html>

    The link_to_remote() is passed three arguments: the text to be shown i.e. “click here”, the id of the link created which is “time_div” and the action to be called which is say_when. The next step is to implement the controller containing the action.

  2. Implement the controller to be called by link_to_remote()The action within the controller can be implemented in two ways. The first way involves a simple action which in turn calls the corresponding RHTML, while the second way renders the response using render_text. The following is an example of the first type:

    class DemoController < ApplicationController
    def index
    end

    def say_when

    render(:layout => false)

    end
    end

    The render function is called with the layout option as false because only a part of the HTML page is being updated; hence there is no requirement of any Rails layout wrappers. The example of the second type is as follows:

    class DemoController < ApplicationController
    def index
    end

    def say_when
    render_text “<p>The time is <b>” + DateTime.now.to_s + “</b></p>”
    end
    end

    It uses render_text method to generate the response.

  3. Develop the page that would provide the response:This step is required only in case of an action that calls view to generate a response. The view corresponding to the action is again a straight forward RHTML:

    <p>The time is <b> <%=DateTime.now.to_s%>

    </b></p>

    That’s how link_to_remote() is used. Next let’s see how form_remote_tag() is used.

While dealing with links, link_to_remote() is handy but what about form elements? That’s where form_remote_tag() comes in. One can easily enable any Rails form to use AJAX using form_remote_tag(). It serializes and sends all the form elements to the server via XMLHttpRequest. All this is done automatically without any requirement for extra logic implementation. The form_remote_tag() takes three parameters:

  1. The update parameter specifies the id of the element that needs to be updated using the response of the executed action.
  2. The url parameter specifies the server-side action to call.
  3. The position parameter tells Rails the position in which to place the response data.

To use the form_remote_tag() there are three steps:

  1. Create the page to be shown to the user.
  2. Implement the controller to be called by form_remote_tag().
  3. Develop the page that will provide the response.

The steps are similar to those of using link_to_remote(). Only the way they are implemented is different.

  1. Create the page to be shown to the user:The main change from the page for link_to_remote() is that form_remote_tag() uses form elements. Hence the page also contains form elements instead of links. A simple page using form_remote_tag() would look as follows:

    <html>
    <head>
    <title>Ajax List Demo</title>
    <%= javascript_include_tag “prototype” %>
    </head>
    <body>
    <h3>Add to list using Ajax</h3>
    <%= form_remote_tag(:update => “my_list”,
    :url => { :action => :add_item },
    :position => “top” ) %>
    New item text:
    <%= text_field_tag :newitem %>
    <%= submit_tag “Add item with Ajax” %>
    <%= end_form_tag %>
    <ul id=”my_list”>
    <li>Original item… please add more!</li>
    </ul>
    </body>
    </html>

    Here the id of the element is given as my_list which is the id of the list that will be populated by the response provided by the action. The url specifies the action to be called and the position tells Rails to place the returned HTML snippet on the top of the element whose id has been specified in the update parameter. Now let’s look at the action.

  2. Implement the controller to be called by form_remote_tag():Just as in the case of link_to_remote() helper, the action can either render the response directly using render_text or else it can delegate it to another RHTML. If render_text is to be used the code would be:

    class ListdemoController < ApplicationController
    def index
    end

    def add_item
    render_text “<li>” + params[:newitem] + “</li>”
    end
    end

    whereas if the rendering is to be done by a template then the code would be:

    class ListdemoController < ApplicationController
    def index
    end

    def add_item
    @item= params[:newitem]
    render(:layout => false)
    end
    end

    The only difference is that the parameter passed by form_remote_tag() is placed in a variable called item which can be used in the template.

  3. Develop the page that will provide the response:The add_item.rhtml would be like:

    <li> <%=@item%> </li>
    That’s it.

    So now you have seen how easy the steps are for creating an AJAX based application in RoR. Next I will develop a real world application using RoR and AJAX.

Ruby-on-Rails and AJAX

In real world applications one of the most required functionalities is to populate a combo box based on the data entered in a preceding field such as a text box or another combo box. In this example, I will be developing such a part for an application. This can be used with any application with slight modifications. It contains two components:

index.rhtml - the view which contains the combo box to be filled and the
combo box on the basis of which the filling has to be done

PopulateController - the controller containing the action required for the
populating of the combo box.

Here is the index.rhtml:

<html>
<head>
<meta http-equiv=”content-type” content=”text/html; charset=utf-8″ />
<%= javascript_include_tag “prototype” %>
<title>Ajax Rails & Select lists</title>
</head>
<body>
<%= form_remote_tag(:update => “sel_con”,:url => {
:action => :create_select },
:position => “top”,:success => “$(’sel_con’).innerHTML=”” ) %>
<p>
Please select a sports category:
</p>
<p>
<%= select_tag “categories”,
“<option>Team</option><option>Individual</option>” %>
</p>
<div id=”sel_con”></div>
<p>
<%= submit_tag “Show Sports” %>
</p>
<%= end_form_tag %>
</body>
</html>

The combo box contains two values, team and individual. Based on the selection the second combo has to be populated and shown at the div having the id sel_con. If team is selected by the user, the second combo box would be filled with team events whereas in the case of individual, individual event is populated. The form_remote_tag is passed four parameters: the id of element to be updated, the action to be called, the position in which to place the result and what has to be done if the request is successfully executed. In this case the inner HTML is set to no value i.e. “”. Next is the controller:

class PopulateController < ApplicationController
def index
end

def create_select

indArr=[”Nordic Skiing”, “Inline Skating”,”Tennis”,
“Triathlon”,”Road Racing”,”Figure Skating”,
“Weight Lifting”,”Speed Skating”,”Snowboarding”];
teamArr=[”Soccer”,”Basketball”,”Football”,”Hockey”,
“Baseball”,”Lacrosse”];
str=”";

if params[:categories].index(’Team’) != nil
render :partial => “options”,
:locals => { :sports => teamArr,:sptype => “team”}
elsif params[:categories].index(’Individual’) != nil
render :partial => “options”,
:locals => { :sports => indArr, :sptype => “individual” }
else
str=”<select id=’individual’ name=’individual’>
<option>unknown</option></select>”;
render :text => str;
end

#end method
end
#end class definition
end

The create_select is the action where the logic for creating and populating the second combo box goes. First it creates two arrays with team and individual sports events. Then based on the parameter received, the combo box is created and populated. This is done in the statement

render :partial => “options”,
:locals => { :sports => teamArr,:sptype => “team”}

Now let’s look at _options.rhtml which is a partial template i.e. the a piece of HTML code that can be used again and again. Here is the code:

<select id=”<%= sptype %>” name=”<%= sptype %>”>
<% sports.each do |sport| %>
<option><%= sport %></option>
<% end %>
</select>

It first gives the combo box an id, iterates through the array passed and populates the combo box. That’s it. This completes the application. Though I had mentioned four libraries, I have introduced only one of them. In the next discussion, I will be focusing on UI using the other three libraries. Till then..

by A.P.Rajshekhar

Error Checking and Debugging with Ruby on Rails

(No Ratings Yet)
Loading ... Loading ...

In this conclusion to a six-part series covering web development and Ruby on Rails, you’ll learn how to send error messages to your email and more. This article is excerpted from chapter 15 of the Ruby Cookbook, written by Lucas Carlson and Leonard Richardson (O’Reilly, 2006; ISBN: 0596523696). Copyright © 2006 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.

15.20 Automatically Sending Error Messages to Your Email

Problem

You want to receive a descriptive email message every time one of your users encounters an application error.

Solution

Any errors that occur while running your application are sent to the ActionController::Base#log_error method. If you’ve set up a mailer (as shown in Recipe 15.19) you can override this method and have it send mail to you. Your code should look something like this:

class ApplicationController < ActionController::Base

private
def log_error(exception)
super
Notification.deliver_error_message(exception,
clean_backtrace(exception),
session.instance_variable_get(”@data”),
params,
request.env
)
end
end

That code rounds up a wide variety of information about the state of the Rails request at the time of the failure. It captures the exception object, the corresponding backtrace, the session data, the CGI request parameters, and the values of all environment variables.

The overridden log_error calls Notification.deliver_error_messsage, which assumes you’ve created a mailer called “Notification”, and defined the method Notification.error_message. Here’s the implementation:

class Notification < ActionMailer::Base
def error_message(exception, trace, session, params, env, sent_on = Time.now)

@recipients =
‘me@mydomain.com’
@from = ‘error@mydomain.com’
@subject = “Error message:
#{env[’REQUEST_URI’]}”
@sent_on = sent_on
@body = {
:exception => exception,
:trace => trace,
:session => session,
:params => params,
:env => env
}
end
end

The template for this email looks like this:

<!– app/views/notification/error_message.rhtml –>

Time: <%= Time.now %>
Message: <%= @exception.message %>
Location: <%= @env[’REQUEST_URI’] %>
Action: <%= @params.delete(’action’) %> </td> </tr>
Controller: <%= @params.delete(’controller’) %> </td> </tr>
Query: <%= @env[’QUERY_STRING’] %> </td> </tr>
Method: <%= @env[’REQUEST_METHOD’] %> </td> </tr>
SSL: <%= @env[’SERVER_PORT’].to_i == 443 ? “true” : “false” %>
Agent: <%= @env[’HTTP_USER_AGENT’] %>

Backtrace
<%= @trace.to_a.join(”</p>\n<p>”) %>

Params
<% @params.each do |key, val| -%>
* <%= key %>: <%= val.to_yaml %>
<% end -%>

Session
<% @session.each do |key, val| -%>
* <%= key %>: <%= val.to_yaml %>
<% end -%>

Environment
<% @env.each do |key, val| -%>
* <%= key %>: <%= val %>
<% end -%>

Discussion

ActionController::Base#log_error gives you the flexibility to handle errors however you like. This is especially useful if your Rails application is hosted on a machine to which you have limited access: you can have errors sent to you, instead of written to a file you might not be able to see. Or you might prefer to record the errors in a database, so that you can look for patterns.

The method ApplicationController#log_error is declared private to avoid confusion. If it weren’t private, all of the controllers would think they had a log_error action defined. Users would be able to visit /<controller>/log_error and get Rails to act strangely.

See Also

  • Recipe 15.19, “Sending Mail with Rails”

15.21 Documenting Your Web Site

Problem

You want to document the controllers, models, and helpers of your web application so that the developers responsible for maintaining the application can understand how it works.

Solution

As with any other Ruby program, you document a Rails application by adding specially-formatted commands to your code. Here’s how to add documentation to the FooController class and one of its methods:

# The FooController controller contains miscellaneous functionality
# rejected from other controllers.
class FooController < ApplicationController
# The set_random action sets the @random_number instance variable
# to a random number.
def set_random
@random_number = rand*rand
end
end

The documentation for classes and methods goes before their declaration, not after.

When you’ve finished adding documentation comments to your application, go to your Rails application’s root directory and issue the rake appdoc command:

$ rake appdoc

This Rake task runs RDoc for your Rails application and generates a directory called doc/app. This directory contains a web site with the aggregate of all your documentation comments, cross-referenced against the source code. Open the doc/app/index.rhtml file in any web browser, and you can browse the generated documentation.

Discussion

Your RDoc comments can contain markup and special directives: you can describe your arguments in definition lists, and hide a class or method from documentation with the :nodoc: directive. This is covered in Recipe 17.11.

The only difference between Rails applications and other Ruby programs is that Rails comes with a Rakefile that defines an appdoc task. You don’t have to find or write one yourself.

You probably already put inline comments inside your methods, describing the action as it happens. Since the RDoc documentation contains a formatted version of the original source code, these comments will be visible to people going through the RDoc. These comments are formatted as Ruby source code, though, not as RDoc markup.

See Also

  1. Recipe 17.11, “Documenting Your Application”
  2. Chapter 19, especially Recipe 19.2, “Automatically Generating Documentation”
  3. The RDoc for RDoc (http://rdoc.sourceforge.net/doc/index.html)

15.22 Unit Testing Your Web Site

Problem

You want to create a suite of automated tests that test the functionality of your Rails application.

Solution

Rails can’t write your test code any more than it can write your views and controllers for you, but it does make it easy to organize and run your automated tests.

Rails can’t write your test code any more than it can write your views and controllers for you, but it does make it easy to organize and run your automated tests.

When you use the ./script/generate command to create controllers and models, not only do you save time, but you also get a generated framework for unit and functional tests. You can get pretty good test coverage by filling in the framework with tests for the functionality you write.

So far, all the examples in this chapter have run against a Rails application’s development database, so you only needed to make sure that the development section of your config/database.yml file was set up correctly. Unit test code runs on your application’s test database, so now you need to set up your test section as well. Your mywebapp_test database doesn’t have to have any tables in it, but it must exist and be accessible to Rails.

When you generate a model with the generate script, Rails also generates a unit test script for the model in the test directory. It also creates a fixture, a YAML file containing test data to be loaded into the mywebapp_test database. This is the data against which your unit tests will run:

./script/generate model User
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/user.rb
create test/unit/user_test.rb
create test/fixtures/users.yml
create db/migrate
create db/migrate/ 001_create_users.rb

When you generate a controller with generate, Rails creates a functional test script for the controller:

./script/generate users list
exists app/controllers/
exists app/helpers/
create app/views/users
exists test/functional/
create app/controllers/ users_controller.rb
create test/functional/ users_controller_test.rb
create app/helpers/
users_helper.r b
create app/views/users/list.rhtml

As you write code in the model and controller classes, you’ll write corresponding tests in these files.

To run the unit and functional tests, invoke the rake command in your home directory. The default Rake task runs all of your tests. If you run it immediately after generating your test files, it’ll look something like this:

$ rake
(in /home/lucas/mywebapp)
/usr/bin/ruby1.8 “test/unit/user_test.rb”
Started
.
Finished in 0.048702 seconds.

1 tests, 1 assertions, 0 failures, 0 errors
/usr/bin/ruby1.8 “test/functional/users_controller_test.rb”
Started
.
Finished in 0.024615 seconds.

1 tests, 1 assertions, 0 failures, 0 errors

Discussion

All the lessons for writing unit tests in other languages and in other Ruby programs (see Recipe 17.7) apply to Rails. Rails does some accounting for you, and it defines some useful new assertions (see below), but you still have to do the work. The rewards are the same, too: you can modify and refactor your code with confidence, knowing that if something breaks, your tests will break. You’ll hear about the problem immediately and you’ll be able to fix it more quickly.

Let’s see what Rails has generated for us. Here’s a generated test/unit/user_test.rb:

require File.dirname(__FILE__) + ‘/../test_helper’

class UserTest < Test::Unit::TestCase
fixtures :users

# Replace this with your real tests.
def test_truth
assert true
end
end

A good start, but test_truth is kind of tautological. Here’s a slightly more realistic test:

class UserTest
def test_first
assert_kind_of User, users(:first)
end
end

This code fetches the first element from the users table, and asserts that ActiveRecord turns it into a User object. This isn’t testing our User code (we haven’t written any) so much as it’s testing Rails and ActiveRecord, but it shows you the kind of assertion that makes for good unit tests.

But how does users(:first) return anything? The test suite runs against the mywebapp_test database, and we didn’t even put any tables in it, much less sample data.

We didn’t, but Rails did. When you run the test suite, Rails copies the schema of the development database to the test database. Instead of running every test against whatever data happens to exist in the development database, Rails loads special test data from YAML files called fixtures. The fixture files contain whatever database data you need to test: objects that only exist to be deleted by a test, strange relation ships between rows in different tables, or anything else you need.

In the example above, the fixture for the users table was loaded by the line fixtures :users. Here’s the generated fixture for the User model, in test/fixtures/users.yml:

first:
id: 1
another:
id: 2

Before running the unit tests, Rails reads this file, creates two rows in the users table, and defines aliases for them (:first and :another) so you can refer to them in your unit tests. It then defines the users method (like so much else, this method name is based on the name of the model). In test_first, the call to users(:first) retrieves the User object corresponding to :first in the fixture: the object with ID 1.

Here’s another unit test:

class UserTest
def test_another
assert_kind_of User, users(:another)
assert_equal 2, users(:another).id
assert_not_equal users(:first), users(:another)
end
end

Rails adds the following Rails-specific assertions to Ruby’s Test::Unit:

  1. assert_dom_equal
  2. assert_dom_not_equal
  3. assert_generates
  4. assert_no_tag
  5. assert_recognizes
  6. assert_redirected_to
  7. assert_response
  8. assert_routing
  9. assert_tag
  10. assert_template
  11. assert_valid

See Also

  1. “Testing the Rails” is a guide to unit and functional testing in Rails (http:// manuals.rubyonrails.com/read/book/5)
  2. Rails 1.1 supports integration testing as well, for testing the interactions between controllers and actions; see http://rubyonrails.com/rails/classes/ActionController/ IntegrationTest.html and http://jamis.jamisbuck.org/articles/2006/03/09/ integration-testing-in-rails-1-1
  3. The ZenTest library inclues Test::Rails, which lets you write separate tests for your views and controllers (http://rubyforge.org/projects/zentest/)
  4. Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
  5. Read about the assertions that Rails adds to Test::Unit at http://rails.rubyonrails. com/classes/Test/Unit/Assertions.html
  6. Recipe 15.6, “Integrating a Database with Your Rails Application”
  7. Recipe 17.7, “Writing Unit Tests”
  8. Chapter 19

15.23 Using breakpoint in Your Web Application

Problem

Your Rails application has a bug that you can’t find using log messages. You need a heavy-duty debugging tool that lets you inspect the full state of your application at any given point.

Solution

The breakpoint library lets you stop the flow of code and drop into irb, an interactive Ruby session. Within irb you can inspect the variables local to the current scope, modify those variables, and resume execution of the normal flow of code. If you have ever spent hours trying to track down a bug by placing logging messages everywhere, you’ll find that breakpoint gives you a much easier and more straightforward way to debug.

But how can you run an interactive console program from a web application? The answer is to have a console program running beforehand, listening for calls from the Rails server.

The first step is to run ./script/breakpointer on the command line. This command starts a server that listens over the network for breakpoint calls from the Rails server. Keep this program running in a terminal window: this is where the irb session will start up:

$ ./script/breakpointer
No connection to breakpoint service at druby://localhost:42531
Tries to connect will be made every 2 seconds…

To trigger an irb session, you can call the breakpoint method anywhere you like from your Rails application–within a model, controller, or helper method. When execution reaches that point, processing of the incoming client request will stop, and an irb session will start in your terminal. When you quit the session, processing of the request will resume.

Discussion

Here’s an example. Let’s say you’ve written the following controller, and you’re having trouble modifying the name attribute of an Item object.

class ItemsController < ApplicationController
def update
@item = Item.find(params[:id])
@item.value = ‘[default]’
@item.name = params[:name]
@item.save
render :text => ‘Saved’
end
end

You can put a breakpoint call in the Item class, like this:

class Item < ActiveRecord::Base
attr_accessor :name, :value

def name=(name)
super
breakpoint
end
end

Accessing the URL http://localhost:3000/items/update/123?name=Foo calls Item-Controller#update, which finds Item number 123 and then calls its name= method. The call to name= triggers the breakpoint. Instead of rendering the text “Saved”, the site seems to hang and become unresponsive to requests.

But if you return to the terminal running the breakpointer server, you’ll see that an interactive Ruby session has started. This session allows you to play with all the local variables and methods at the point where the breakpoint was called:

Executing break point “Item#name=” at item.rb:4 in `name=’
irb:001:0> local_variables
=> [”name”, “value”, “_”, “__”]
irb:002:0> [name, value]
=> [”Foo”, “[default]”]
irb:003:0> [@name, @value]
=> [”Foo”, “[default]”]
irb:004:0> self
=>
#<Item:0×292fbe8 @name=”Foo”, @value=”[default]”>
irb:005:0> self.value = “Bar”
=>
“Bar”
irb:006:0> save
=> true
irb:006:0> exit

Server exited. Closing connection…

Once you finish, type exit to terminate the interactive Ruby session. The Rails application continues running at the place it left off, rendering “Saved” as expected.

By default, breakpoints are named for the method in which they appear. You can pass a string into breakpoint to get a more descriptive name. This is especially helpful if one method contains several breakpoints:

breakpoint “Trying to set Item#name, just called super”

Instead of calling breakpoint directly, you can also call assert, a method which takes a code block. If the block evaluates to false, Ruby calls breakpoint; otherwise, things continue as normal. Using assert lets you set breakpoints that are only called when something goes wrong (called “conditional breakpoints” in traditional debuggers):

1.upto 10 do |i|
assert { Person.find(i) }
p = Person.find(i)
p.update_attribute(:name, ‘Lucas’)
end

If all of the required Person objects are found, the breakpoint is never called, because Person.find always returns true. If one of the Person objects is missing, Ruby calls the breakpoint method and you get an irb session to investigate.

Breakpoint is a powerful tool that can vastly simplify your debugging process. It can be hard to understand the true power of it until you try it yourself, so go through the solution with your own code to toy around with it.

See Also

  1. Recipe 17.10, “Using breakpoint to Inspect and Change the State of Your Application,” covers breakpoint in more detail.
  2. http://wiki.rubyonrails.com/rails/show/ HowtoDebugWithBreakpoint

————————————————-

* Python, for instance, has several excellent web application frameworks, but that’s just the problem. It has several, and a powerful community is fractured on the issue of which to use. Ruby has no major web application frameworks apart from Rails. In a sense, Ruby’s former obscurity is what made the dominance of Rails possible.

* You could throw an exception, but then your redirect wouldn’t happen: the user would see an exception screen instead.

* More precisely, our models have been embedded in our controllers, as ad hoc data structures like hardcoded shopping lists.

* The helper function time_ago_in_words() calculates how long it’s been since a certain time and returns English text such as “about a minute” or “5 hours” or “2 days”. This is a nice, easy way to give the user a perspective on what a date means.

* Rails extends Ruby’s numeric classes to include some very helpful methods (like the hour method shown here). These methods convert the given unit to seconds. For example, Time.now + 1.hour is the same as Time. now + 3600, since 1.hour returns the number of seconds in an hour. Other helpful methods include minutes, hours, days, months, weeks, and years. Since they all convert to numbers of seconds, you can even add them together like 1.week + 3.days.

* This doesn’t quite stand for Asynchronous JavaScript and XML. The origins of the term Ajax are now a part of computing mythology, but it is not an acronym.

* This will happen if someone’s using your application with JavaScript turned off.

* You can even add your web interface actions to the ItemController class. Then a single controller will implement both the traditional web interface and the web service interface. But you can’t define a web application action with the same name as a web service action, because a controller class can contain only one method with a given name.

—-

by O’Reilly Media 

Web Forms and Ruby on Rails

(No Ratings Yet)
Loading ... Loading ...

In this fifth article of a six-part series covering web development and Ruby on Rails, you’ll learn how to create an AJAX form and more. This article is excerpted from chapter 15 of the Ruby Cookbook, written by Lucas Carlson and Leonard Richardson (O’Reilly, 2006; ISBN: 0596523696). Copyright © 2006 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.

15.16 Generating Forms for Manipulating Model Objects

Problem

You want to define actions that let a user create or edit objects stored in the database.

Solution

Let’s create a simple model, and then build forms for it. Here’s some MySQL code to create a table of key-value pairs: 

  use mywebapp_development;
DROP TABLE IF EXISTS items;
CREATE TABLE `items` (
    `id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL default ”,
`value` varchar(40) NOT NULL default ‘[empty]’,
PRIMARY KEY (`id`)
);

Now, from the command line, create the model class, along with a controller and views:

  $ ./script/generate model Item
exists  app/models/
exists  test/unit/
exists  test/fixtures/
create  app/models/item.rb
create  test/unit/item_test.rb
create  test/fixtures/items.yml
create  db/migrate
create  db/migrate/ 001_create_items.rb

  $ ./script/generate controller items new create edit
exists  app/controllers/
exists  app/helpers/
create  app/views/items
exists  test/functional/
create  app/controllers/ items_controller.rb
create  test/functional/ items_controller_test.rb
create  app/helpers/items_helper.rb
create  app/views/items/new.rhtml
create  app/views/items/edit.rhtml

The first step is to customize a view. Let’s start with app/views/items/new.rhtml. Edit it to look like this:

  <!– app/views/items/new.rhtml –>

  <%= form_tag :action => “create” %>
Name: <%= text_field “item”, “name”
%><br />
Value: <%= text_field “item”, “value”
%><br />
<%= submit_tag %>
  <%= end_form_tag %>

All these method calls generate HTML: form_tag opens a <FORM> tag, submit_tag generates a submit button, and so on. You can type out the same HTML by hand and Rails won’t care, but it’s easier to make method calls, and it makes your templates neater.

The text_field call is a little more involved. It creates an <INPUT> tag that shows up in the HTML form as a text entry field. But it also binds the value of that field to one of the members of the @item instance variable. This code creates a text entry field that’s bound to the name member of @item:

  <%= text_field “item”, “name” %>

But what’s the @item instance variable? Well, it’s not defined yet, because we’re still using the generated controller. If you try to access the page /items/new page right now, you may get an error complaining about an unexpected nil value. The nil value is the @item variable, which gets used (in text_field calls) without ever being defined.

Let’s customize the ItemsController class so that the new action sets the @item instance variable properly. We’ll also implement the create action so that something actually happens when the user hits the submit button on our generated form.

  class ItemsController < ApplicationController
def new
@item = Item.new
end

    def create
@item = Item.create(params[:item])
redirect_to :action => ‘edit’, :id => @item.id
end
end

Now if you access the /items/new page, you’ll see what you’d expect: a form with two text entry fields. The “Name” field will be blank, and the “Value” field will contain the default database value of “[empty]”.

Fill out the form and submit, and a new row will be created in the items table. You’ll be redirected to the edit action, which doesn’t exist yet. Let’s create it now. Here’s the controller part (note the similarity between ItemsController#edit and ItemsController#create above):

  class ItemsController < ApplicationController
def edit
@item = Item.find(params[:id])

      if request.post?
@item.update_attributes(params[:item])
redirect_to :action => ‘edit’, :id => @item.id
end
end
end

In fact, the edit action is so similar to the create action that its form can be almost identical. The only differences are in the arguments to form_tag:

  <!– app/views/items/edit.rhtml –>

  <%= form_tag :action => “edit”, :id => @item.id %>
Name: <%= text_field “item”, “name”
%><br />
Value: <%= text_field “item”, “value”
%><br />
<%= submit_tag %>
<%= end_form_tag %>

Discussion

This is probably the most common day-to-day task faced by web developers. It’s so common that Rails comes with a tool called scaffold that generates this kind of code for you. If you’d invoked generate this way instead of with the arguments given above, Rails would have generate code for the actions given in the Solution, plus a few more:

  $ ./script/generate scaffold Items

Starting off with scaffolding doesn’t mean you can get away with not knowing how Rails form generation works, because you’ll definitely want to customize the scaffolding code.

There are two places in our code where magic happens. The first is the text_field call in the view, which is explained in the Solution. It binds a member of an object (@item.name , for instance) to an HTML form control. If you view the source of the /items/new page, you will see that the form fields look something like this:

  Name: <input type=”text” name=”item[name]” value=”" /><br />
Value <input type=”text” name=”item[value]” value=”[empty]” /><br />

These special field names are used by the second piece of magic, located in the calls to Item.create (in new) and Item#update_attributes. In both cases, an Item object is fed a hash of new values for its members. This hash is embedded into the params hash, which contains CGI form values.

The names of the HTML form fields (item[name] and item[value]) translate into a params hash that looks like this:

  {
:item => {
:name => “Name of the item”,
:value => “Value of the item”
},
:controller => “items”,
:action => “create”
}

So this line of code:

  Item.create(params[:item])

is effectively the same as this line:

  Item.create(:name => “Name of the item”, :value => “Value of the item”)

The call to Item#update_attributes in the edit action works exactly the same way.

As mentioned above, the views for edit and new are very similar, differing only in the destination of the form. With some minor refactoring, we can remove one of the view files completely.

A call to <%= form_tag %> without any parameters at all sets the form destination to the current URL. Let’s change the new.rhtml file appropriately:

  <!– app/views/items/new.rhtml –>
  <%= form_tag %>

  Name: <%= text_field “item”, “name”
%>&#x00A;
Value: <%= text_field “item”, “value”
%>&#x00A;

  <%= submit_tag %>
  <%= end_form_tag %>

Now the new.rhtml view is suitable for use by both new and edit. We just need to change the new action to call the create method (since the form doesn’t go there anymore), and change the edit action to render new.rhtml instead of edit.rhtml (which can be removed):

  class ItemsController < ApplicationController
def new
@item = Item.new
create if request.post?
end

def edit
@item = Item.find(params[:id])

if request.post?
@item.update_attributes(params[:item])
redirect_to :action => ‘edit’, :id => @item.id and return
           end
render :action => ‘new’
      end
end

Remember from Recipe 15.5 that a render call only specifies the template file to be used. The render call in edit won’t actually call the new method, so we don’t need to worry about the new method overwriting our value of @item.

In real life, there would be enough differences in the content surrounding the add and edit forms to a separate view for each action. However, there’s usually enough similarity between the forms themselves that they can be refactored into a single partial view (see Recipe 15.14) which both views share. This is a great example of the DRY (Don’t Repeat Yourself) principle. If there is a single form for both the add and edit views, it’s easier and less error-prone to maintain that form as the database schema changes.

See Also

  1. Recipe 15.5, “Displaying Templates with Render”
  2. Recipe 15.14, “Refactoring the View into Partial Snippets of Views”

15.17 Creating an Ajax Form

Problem

You want to build a web application that’s responsive and easy to use. You don’t want your users to spend lots of time waiting around for the browser to redraw the screen.

Solution

You can use JavaScript to make the browser’s XMLHTTPRequest object send data to the server, without dragging the user through the familiar (but slow) page refresh. This technique is called Ajax,* and Rails makes it easy to use Ajax without writing or knowing any JavaScript.

Before you can do Ajax in your web application, you must edit your application’s main layout template so that it calls the javascript_include_tag method within its <HEAD> tag. This is the same change made in Recipe 15.15:

  <!– app/views/layouts/application.rhtml –>

  <html>
<head>
<title>My Web App</title>
      <%= javascript_include_tag “prototype”, “effects” %>
</head>
<body>
<%= @content_for_layout %>
</body>
</html>

Let’s change the application from Recipe 15.16 so that the new action is AJAX-enabled (if you followed that recipe all the way through, and made the edit action use new.rhtml instead of edit.rhtml, you’ll need to undo that change and make edit use its own view template).

We’ll start with the view template. Edit app/views/items/new.rhtml to look like this:

  <!– app/views/items/new.rhtml –>
<div id=”show_item”></div>

        <%= form_remote_tag :url => { :action => :create },
:update => “show_item”,
:complete => visual_effect(:highlight, “show_item”) %>

  Name: <%= text_field “item”, “name”
%><br />
Value: <%= text_field “item”, “value”
%><br />
<%= submit_tag %>
<%= end_form_tag %>

Those small changes make a standard HTML form into an Ajax form. The main difference is that we call form_remote_tag instead of form_tag. The other differences are the arguments we pass into that method.

The first change is that we put the :action parameter inside a hash passed into the :url option. Ajax forms have more options associated with them than a normal form, so you can’t describe its form action as simply as you can with form_tag.

When the user clicks the submit button, the form values are serialized and sent to the destination action (in this case, create) in the background. The create action processes the form submission as before, and returns a snippet of HTML.

What happens to this HTML? That’s what the :update option is for. It tells Rails to take the result of the form submission, and stick it into the element with the HTML ID of “show_item”. This is why we added that <div id=”show_item”> tag to the top of the template: that’s where the response from the server goes.

The last change to the new.rhtml view is the :complete option. This is a callback argument: it lets you specify a string of JavaScript code that will be run once an Ajax request is complete. We use it to highlight the response from the server once it shows up.

That’s the view. We also need to modify the create action in the controller so that when you make an Ajax form submission, the server returns a snippet of HTML. This is the snippet that’s inserted into the “show_item” element on the browser side. If you make a regular (nonAjax) form submission, the server can behave as it does in Recipe 15.16, and send an HTTP redirect.* Here’s what the controller class needs to look like:

  class ItemsController < ApplicationController
def new
@item = Item.new
end

    def create
@item = Item.create(params[:item])
       if request.xml_http_request?
render :action => ’show’, :layout => false
else
redirect_to :action => ‘edit’, :id => @item.id
end
end

    def edit
@item = Item.find(params[:id])
 

      if request.post?
@item.update_attributes(params[:item])
redirect_to :action => ‘edit’, :id => @item.id
end
end
end

This code references a new view, show. It’s the tiny HTML snippet that’s returned by the server, and stuck into the “show_element” tag by the web browser. We need to define it:

  <!– app/views/items/show.rhtml –>

  Your most recently created item:<br />
Name: <%= @item.name %><br />
Value: <%= @item.value %><br />
<hr>

Now when you use http://localhost:3000/items/new to add new items to the data base, you won’t be redirected to the edit action. You’ll stay on the new page, and the results of your form submission will be displayed above the form. This makes it easy to create many new items at once.

Discussion

Recipe 15.16 shows how to submit data to a form in the traditional way: the user clicks a “submit” button, the browser sends a request to the server, the server returns a response page, and the browser renders the response page.

Recently, sites like Gmail and Google Maps have popularized techniques for sending and receiving data without a page refresh. Collectively, these techniques are called Ajax. Ajax is a very useful tool for improving your application’s response time and usability.

An Ajax request is a real HTTP request to one of your application’s actions, and you can deal with it as you would any other request. Most of the time, though, you won’t be returning a full HTML page. You’ll just be returning a snippet of data. The web browser will be sending the Ajax request in the context of a full web page (which you served up earlier) that knows how to handle the response snippet.

You can define JavaScript callbacks at several points throughout the lifecycle of an Ajax request. One callback, :complete, was used above to highlight the snippet after inserting it into the page. This table lists the other callbacks.

Callback name Callback description
:loading Called when the web browser begins to load the remote document.
:loaded Called when the browser has finished loading the remote document.
:interactive Called when the user can interact with the remote document, even if it has not finished loading.
:success Called when the XMLHttpRequest is completed, and the HTTP status code is in the 2XX range.
:failure

Called when the XMLHttpRequest is completed, and the HTTP status code is not in the 2XX range.

:complete

Called when the XMLHttpRequest is complete. If :success and/or :failure are also present, runs after they do.

15.18 Exposing Web Services on Your Web Site 

Problem

You want to offer SOAP and XML-RPC web services from your web application.

Solution

Rails comes with a built-in web service generator that makes it easy to expose a controllers actions as web services. You don’t have to spend time writing WSDL files or even really know how SOAP and XML-RPC work.

Here’s a simple example. First, follow the directions in Recipe 15.16 to create a database table named items, and to generate a model for that table. Don’t generate a controller.

Now, run this from the command line:

  ./script/generate web_service Item add edit fetch
create  app/apis/
exists  app/controllers/
exists  test/functional/
create  app/apis/item_api.rb
create  app/controllers/ item_controller.rb
create  test/functional/ item_api_test.rb

This creates an item controller that supports three actions: add, edit, and fetch. But instead of web application actions with .rhtml views, these are web service actions that you access with SOAP or XML-RPC.

A Ruby method doesn’t care about the data types of the objects it accepts as arguments, or the data type of its return value. But a SOAP or XML-RPC web service method does care. To expose a Ruby method through a SOAP or XML-RPC interface, we need to define type information for its signature. Open up the file app/apis/ item_api.rb and edit it to look like this:

  class ItemApi < ActionWebService::API::Base
api_method :add, :expects => [:string, :string], :returns => [:int]
api_method :edit, :expects => [:int, :string, :string], :returns => [:bool]
api_method :fetch, :expects => [:int], :returns => [Item]
end

Now we need to implement the actual web service interface. Open app/controllers/ item_controller.rb and edit it to look like this:

  class ItemController < ApplicationController
wsdl_service_name ‘Item’

    def add(name, value)
Item.create(:name => name, :value => value).id
end

    def edit(id, name, value)
Item.find(id).update_attributes(:name => name, :value => value)
end

    def fetch(id)
Item.find(id)
end
end

Discussion

The item controller now implements SOAP and XML-RPC web services for the items table. This controller can live alongside an items controller that implements a traditional web interface.*

The URL to the XML-RPC API is http://www.yourserver.com/item/api, and the URL to the SOAP API is http://www.yourserver.com/item/service.wsdl. To test these services, here’s a short Ruby script that calls the web service methods through a SOAP client:

  require ’soap/wsdlDriver’ 

  wsdl = http://localhost:3000/item/service.wsdl
item_server = SOAP::WSDLDriverFactory.new(wsdl).create_rpc_driver

  item_id = item_server.add(’foo’, ‘bar’)

  if item_server.edit(item_id, ‘John’, ‘Doe’)
puts ‘Hey, it worked!’
else
puts ‘Back to the drawing board…’
end
# Hey, it worked!

  item = item_server.fetch(item_id)
item.class     # => SOAP::Mapping::Object
item.name      # => “John”
item.value     # => “Doe”

Here’s the XML-RPC equivalent:

  require ‘xmlrpc/client’
item_server = XMLRPC::Client.new2(’http://localhost:3000/item/api’)

  item_id = item_server.call(’Add’, ‘foo’, “bar”)
if item_server.call(’Edit’, item_id, ‘John’, ‘Doe’)
puts ‘Hey, it worked!’
  else
puts ‘Back to the drawing board…’
  end
# Hey, it worked!

  item = item_server.call(’Fetch’, item_id)
# => {”name”=>”John”, “id”=>2, “value”=>”Doe”}
item.class                   # => Hash

See Also

  1. Matt Biddulph’s article “REST on Rails” describes how to create REST-style web services on top of Rails (http://www.xml.com/pub/a/2005/11/02/rest-on-rails.html)
  2. Recipe 16.3, “Writing an XML-RPC Client,” and Recipe 16.4, “Writing a SOAP Client”
  3. Recipe 16.5, “Writing a SOAP Server,” shows a nonRails implementation of SOAP web services

15.19 Sending Mail with Rails

Problem

You want to send an email from within your Rails application: perhaps a confirmation of an order, or notification that some action has been taken on a user’s behalf.

Solution

The first is to generate some mailer infrastructure. Go to the application’s base directory and type this command: 

  ./script/generate mailer Notification welcome
exists  app/models/
create  app/views/notification
exists  test/unit/
create  test/fixtures/notification
create  app/models/notification.rb
create  test/unit/ notification_test.rb
create  app/views/notification/ welcome.rhtml
create  test/fixtures/notification/ welcome

We’re giving the name “Notification” to the mailing center of the application; it’s some-what analogous to a controller in the web interface. The mailer is set up to generate a single email, called “welcome”: this is analagous to an action with a view template.

Now open app/models/notification.rb and edit it to look like this:

  class Notification < ActionMailer::Base
def welcome(user, sent_at=Time.now)
@subject = ‘A Friendly Welcome’
@recipients = user.email
@from = ‘admin@mysite.com’
@sent_on = sent_at
@body = {
:user => user,
:sent_on => sent_at
}
attachment ‘text/plain’ do |a|
a.body = File.read(’rules.txt’)
end
end
end

The subject of the email is “A Friendly Welcome”, and it’s sent to the user’s email address from the address “admin@mysite.com”. It’s got an attachment taken from the disk file rules.txt (relative to the root directory of your Rails application).

Although the file notification.rb is within the models/ directory, it acts like a controller in that each of its email messages has an associated view template. The view for the welcome email is in app/views/notification/welcome.rhtml, and it acts almost the same as the view of a normal controller.

The most important difference is that mailer views do not have access to the instance variables of the mailer. To set instance variables for mailers, you pass a hash of those variables to the body method. The keys become instance variable names and the values become their values. In notification.rb , we make two instance variables available to the welcome view, @user and @sent_on. Here’s the view itself:

  <!– app/views/notification/welcome.rhtml –>

  Hello, <%= @user.name %>, and thanks for signing up at <%= @sent_on
%>. Please print out the attached set of rules and keep them in a
prominent place; they help keep our community running smoothly. Be
sure to pay special attention to sections II.4 (”Assignment of
Intellectual Property Rights”) and XIV.21.a (”Dispute Resolution
Through Ritual Combat”).

To send the welcome email from your Rails application, add the following code to either a controller, a model, or an observer:

  Notification.deliver_welcome(user)

Here, the user variable can be any object that implements #name and #email, the two methods called in the welcome method and in the template.

Discussion

You never call the Notification#welcome method directly. In fact, Notification#welcome is not even available, since it’s an instance method, and you never instantiate a Notification object directly. The ActionMailer::Base class defines a method_missing implementation that looks at all calls to undefined class methods. This is why you call deliver_welcome even though you never defined it.

The welcome.rhtml template given above generates plaintext email. To send HTML emails, simply add the following code to Notification#welcome:

  content_type ‘text/html’

Now your templates can generate HTML; email clients will recognize the format of the email and render it appropriately.

Sometimes you’ll want more control over the delivery process–for example, when you’re unit-testing your ActionMailer classes. Instead of calling deliver_welcome to send out an email, you can call create_welcome to get the email as a Ruby object. These “create” methods return TMail objects, which you can examine or manipulate as necessary.

If your local web server is incapable of sending email, you can modify environment.rb to contact a remote SMTP server:

  Rails::Initializer.run do |config|
config.action_mailer.server_settings = {
:address => ’someserver.com’,
:user_name => ‘uname’,
:password => ‘passwd’,
:authentication => ‘cram_md5′
}
end

See Also

  1. Recipe 10.8, “Responding to Calls to Undefined Methods”
  2. Recipe 14.5, “Sending Mail,” has more on ActionMailer and SMTP settings

Please check back tomorrow for the conclusion to this article.

by O’Reilly Media 

Handling Cookies and DHTML Effects with Ruby on Rails

(1 votes, average: 4 out of 5)
Loading ... Loading ...

In this fourth article of a six-part series covering web development and Ruby on Rails, you’ll learn how to extract code into helper functions, add DHTML effects, and more. This article is excerpted from chapter 15 of the Ruby Cookbook, written by Lucas Carlson and Leonard Richardson (O’Reilly, 2006; ISBN: 0596523696). Copyright © 2006 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.

15.12 Setting and Retrieving Cookies

Problem

You want to set a cookie from within Rails.

Solution

Recall from Recipe 15.11 that all Rails controllers, views, helpers, and mailers have access to a method called sessions that returns a hash of the current client’s session information. Your controllers, helpers, and mailers (but not your views) also have access to a method called cookies, which returns a hash of the current clients HTTP cookies.

To set a cookie for a user, simply set a key/value pair in that hash. For example, to keep track of how many pages a visitor has looked at, you might set a “visits” cookie:

  class ApplicationController < ActionController::Base
before_filter :count_visits

    private

    def count_visits
value = (cookies[:visits] || ‘0′).to_i
cookies[:visits] = (value + 1).to_s
@visits = cookies[:visits]
end
end

The call to before_filter tells Rails to run this method before calling any action method. The private declaration makes sure that Rails doesn’t think the count_visits method is itself an action method that the public can view.

Since cookies are not directly available to views, count_visits makes the value of the :visits cookie available as the instance variable @visits. This variable can be accessed from a view:

  <!– index.rhtml –>
You’ve visited this website’s pages <%= @visits %> time(s).

HTTP cookie values can only be strings. Rails can automatically convert some values to strings, but it’s safest to store only string values in cookies. If you need to store objects that can’t easily be converted to and from strings, you should probably store them in the session hash instead.

Discussion

There may be times when you want more control over your cookies. For instance, Rails cookies expire by default when the user closes their browser session. If you want to change the browser expiration time, you can give cookies a hash that contains an :expires key and a time to expire the cookie. The following cookie will expire after one hour:*

  cookies[:user_id] = { :value => ‘123′, :expires => Time.now + 1.hour}

Here are some other options for a cookie hash passed into cookies.

The domain to which this cookie applies:

  :domain

The URL path to which this cookie applies (by default, the cookie applies to the entire domain: this means that if you host multiple applications on the same domain, their cookies may conflict):

  :path

Whether this cookie is secure (secure cookies are only transmitted over HTTPS connections; the default is false):

  :secure

Finally, Rails provides a quick and easy way to delete cookies:

  cookies.delete :user_id

Of course, every Ruby hash implements a delete method, but the cookies hash is a little different. It includes special code so that not only does calling delete remove a key-value pair from the cookies hash, it removes the corresponding cookie from the user’s browser.

See Also

  1. Recipe 3.5, “Doing Date Arithmetic”
  2. Recipe 15.11, “Setting and Retrieving Session Information,” has a discussion of when to use cookies and when to use session

15.13 Extracting Code into Helper Functions

Problem

Your views are getting cluttered with Ruby code.

Solution

Let’s create a controller with a fairly complex view to see how this can happen: 

  $ ./scripts/generate controller list index
exists  app/controllers/
exists  app/helpers/
create  app/views/list
exists  test/functional/
create  app/controllers/ list_controller.rb
create  test/functional/ list_controller_test.rb
create  app/helpers/list_helper.rb
create  app/views/list/index.rhtml

Edit app/controllers/list_controller.rb to look like this:

  class ListController < ApplicationController
def index
@list = [1, “string”, :symbol, [’list’]]
end
end

Edit app/views/list/index.rhtml to contain the following code. It iterates over each element in @list, and prints out its index and the SHA1 hash of its object ID:

  <!– app/views/list/index.rhtml –>
<ul>
<% @list.each_with_index do |item, i| %>
    <li class=”<%= i%2==0 ? ‘even’ : ‘odd’ %>”><%= i %>:
<%= SHA1.new(item.id.to_s) %></li>
<% end %>
</ul>

This is pretty messy, but if you’ve done much web programming it should also look sadly familiar.

To clean up this code, we’re going to move some of it into the helper for the controller. In this case, the controller is called list, so its helper lives in app/helpers/list_helper.rb.

Let’s create a helper function called create_li. Given an object and its position in the list, this function creates an <LI> tag suitable for use in the index view:

  module ListHelper
def create_li(item, i)
%{<li class=”#{ i%2==0 ? ‘even’ : ‘odd’ }”>#{i}:
#{SHA1.new(item.id.to_s)}</li>}
end
end

The list controller’s views have access to all the functions defined in ListHelper. We can clean up the index view like so:

  <!– app/views/list/index.rhtml –>
<ul>
<% @list.each_with_index do |item, i| %>
    <%= create_li(item, i) %>
<% end %>
</ul>

Your helper functions can do anything you can normally do from within a view, so they are a great way to abstract out the heavy lifting.

Discussion

The purpose of helper functions is to create more maintainable code, and to enforce a good division of labor between the programmers and the UI designers. Maintainable code is easier for the programmers to work on, and when it’s in helper functions it’s out of the way of the designers, who can tweak the HTML here and there without having to sifting through code.

A good rule of thumb for when to use helpers is to read the code aloud. If it sounds like nonsense to someone familiar with HTML, or it makes up more than a short English sentence, hide it in a helper.

The flip side of this is that you should minimize the amount of HTML generated from within the helpers. That way the UI designers, or other people familiar with HTML, won’t wander your code, wondering where to find the bit of HTML that needs tweaking.

Although helper functions are useful and used very often, Rails also provides partials, another way of extracting code into smaller chunks.

See Also

  • Recipe 15.14, “Refactoring the View into Partial Snippets of Views,” has more on partials

15.14 Refactoring the View into Partial Snippets of Views

Problem

Your view doesn’t contain a lot of Ruby code, but it’s still becoming more complicated than you’d like. You’d like to refactor the view logic into separate, reusable templates.

Solution

You can refactor a view template into multiple templates called partials. One template can include another by calling the render method, first seen in Recipe 15.5.

Let’s start with a more complex version of the view shown in Recipe 15.5:

  <!– app/views/list/shopping_list.rhtml
–>
<h2>My shopping list</h2>

  <ul>
<% @list.each do |item| %>
<li><%= item.name %> -
<%= link_to ‘Delete’, {:action => ‘delete’, :id => item.id},
:post => true %>
</li>
<% end %>
</ul>

  <h2>Add a new item</h2>

  <%= form_tag :action => ‘new’ %>
Item: <%= text_field “product”, “name”
%>&#x00A;
<%= submit_tag “Add new item” %>
<%= end_form_tag %>

Here’s the corresponding controller class, and a dummy ListItem class to serve as the model:

  # app/controllers/list_controller.rb
class ListController < ActionController::Base
def shopping_list
@list = [ListItem.new(4, ‘aspirin’), ListItem.new(199, ’succotash’)]
end

    # Other actions go here: add, delete, etc.
# …
end

  class ListItem
def initialize(id, name)
@id, @name = id, name
end
end

The view has two parts: the first part lists all the items, and the second part prints a form to add a new item. An obvious first step is to split out the new item form.

We can do this by creating a partial view to print the new item form. To do this, create a new file within app/views/list/ called _new_item_form.rhtml. The underscore in front of the filename indicates that it is a partial view, not a full-fledged view for an action called new_item_form. Here’s the partial file.

  <!– app/views/list/_new_item_form.rhtml –>

  <%= form_tag :action => ‘new’ %>
Item: <%= text_field “item”, “value” %>&#x00A;
<%= submit_tag “Add new item” %>
<%= end_form_tag %>

To include a partial, call the render method from within a template. Here is the _new_ item_form partial integrated into the main view. The view looks exactly the same, but the code is better organized.

  <!– app/views/list/shopping_list.rhtml
–>
<h2>My shopping list</h2>

  <ul>
<% @list.each do |item| %>
<li><%= item.name %> -
<%= link_to ‘Delete’, {:action => ‘delete’, :id => item.id},
:post => true %>
</li>
<% end %>
</ul>
<%= render :partial => ‘new_item_form’ %>

Even though the filename starts with an underscore, when you call the partial, you omit the underscore.

Discussion

Partial views inherit all the instance variables provided by the controller, so they have access to the same instance variables as the parent view. That’s why we didn’t have to change any of the form code for the _new_item_form partial.

We can create a second partial to factor out the code that prints the <LI> tag for each list item. Here’s _list_item.rhtml:

  <!– app/views/list/_list_item.rhtml –>
<li><%= list_item.name %> -
<%= link_to ‘Delete’, {:action => ‘delete’, :id => list_item.id},
:post => true %>
</li>

And heres the revised main view:

  <!– app/views/list/shopping_list.rhtml
–>
<h2>My shopping list</h2>

  <ul>
<% @list.each do |item| %>
<%= render :partial => ‘list_item’, :locals => {:list_item => item} %>
<% end %>
</ul>

  <%=