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!
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”
%>

Value: <%= text_field “item”, “value”
%>

<%= 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
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
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
Please check back tomorrow for the conclusion to this article.
—
by O’Reilly Media
Print This Post
Email This Post
Comments RSS
TrackBack Identifier URI
You must be logged in to post a comment.