Hack 56. Monitor Remote Calls with Rails


Display the status of your XMLHttpRequest remote calls in a Ruby on Rails application.

When debugging applications, it is handy to see the status of your remote request as the response handling unfolds. This hack displays the request status in a Ruby on Rails view before it displays the return value. The user sees the status message without a page refresh or rebuild. Developers do not have to explicitly deal with XMLHttpRequest at all, because the hack uses a method that is part of the RoR framework to implement the remote calls.

See the "XMLHttpRequest" section at the beginning of Chapter 1 for an explanation of the various stages of an XMLHttpRequest request: uninitialized, loading, loaded, interactive, and completed.


Like the other hacks in this chapter, this one runs within a Ruby on Rails application. Figure 7-5 shows what the hack looks like in the Safari browser.

Figure 7-5. Gentlemen, start your monitors


When the user clicks the Start Monitor button, the application sends a request to a server component, called an action in Rails parlance. This action returns some content to update a div in the web page, without refreshing the page itself. As Figure 7-6 depicts, the page shows the request status at each of its stages, in real time as it's happening.

Figure 7-6. Display the request's status in real time


The code for the Rails template associated with this action is shown below. It's very similar to the code for an HTML page, but the template has an .rhtml suffix, as in monitor.rhtml. This view name is mapped to the URL the user enters in the browser: http://localhost:3000/hacks/monitor.

When you start up Ruby on Rails (using the ruby script/server command), WEBrick binds to port 3000 by default. You can change the port number via the command line. For example:

ruby script/server p 8000


Here's the template code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"         "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head>     <meta http-equiv="content-type" content="text/html; charset=utf-8" />     <%= javascript_include_tag :defaults %>     <title>Monitor Ajax calls</title> </head> <body> <%= form_remote_tag(:update => "complete",:url => { :action => :zero_update }, :position => "top", :success => "$('success').innerHTML='Success; request  status='+request.status",  :loading => "$('loading').innerHTML='Loading...'", :loaded => "$('loaded').innerHTML='Loaded; request status='+request.status",  :interactive => "$('inter').innerHTML='Interactive; request status= '+request.status", :failure => "$('failure').innerHTML='Failure; request  status='+request.status") %> <h3>Monitor Your Ajax Calls with Rails</h3> <div  style="font-size: 1.2em"></div> <div  style="font-size: 1.2em"></div> <div  style="font-size: 1.2em"></div> <div  style="font-size: 1.2em; color: green"></div> <div  style="font-size: 1.2em; color: red"></div> <div  style="font-size: 1.2em; color: green" ></div> <p> <%= submit_tag "Start Monitor" %> </p> <%= end_form_tag %> </body> </html>

The RoR views use embedded Ruby script tags ( <%...%>), similar to the tags used in JSP or PHP applications. One of these tags contains a Rails method, form_remote_tag( ). This method takes care of all the XMLHttpRequest initializing and sending for you, so you can focus on the behavior you want your applications to initiate.

The parameters for form_remote_tag( ) are fairly dense, but they accomplish an awful lot beneath the surface. First, the :update parameter specifies the id of the page element (a div, in this case) that the action will update with the request's return value. Next, the :url parameter points to the name of the action that will handle the request (here, zero_update):

:url => { :action => :zero_update }

In Rails method calls, the code has to use the syntax :url as opposed to url. That's easy to forget, but if you do, the method will not be successfully called. Ruby refers to these types of references, which represent variable names rather than evaluating to variable values, as Symbol objects. See http://www.ruby-doc.org/docs/ProgrammingRuby/.


This is similar to other mapping mechanisms used by web development APIs such as Java Servlets (see the further explanation below). The rest of the method parameters specify the JavaScript code that the application should execute at each stage of the request's processing:

:success => "$('success').innerHTML='Success; request status='+request.status",  :loading => "$('loading').innerHTML='Loading...'", :loaded => "$('loaded').innerHTML='Loaded; request status='+request.status",  :interactive=>"$('inter').innerHTML='Interactive; request status='+request.status",  :failure => "$('failure').innerHTML='Failure; request status='+request.status"

For example, when the request object's readystate property equals loaded (it's actually a numerical value of 2; see Chapter 1), this part of our parameter specifies what will happen with the application:

:loaded => "$('loaded').innerHTML='Loaded; request status='+request.status"

The code will get a reference to an HTML element with an id of loaded. The code does so using a shortcut included in the prototype.js package, which this page imports: $('loaded'). This JavaScript code is the equivalent of document.getElementById('loaded'), but it sure is easier to type! With the reference to that Element object, the code sets its innerHTML property to Loaded; request status= plus the status code returned by the HTTP response.

If All Else Fails

What if the request fails? Figure 7-7 shows what the displayed message looks like when you add code to the server component to raise a response error.

Figure 7-7. Displaying a response status signifying failure


The return value is a bit of HTML announcing a problem, along with an HTTP response status code of 500.

The XMLHttpRequest object connects with a server component, or action, named zero_update. As described in "Install Ruby on Rails" [Hack #55], Rails explicitly uses a MVC architecture in the way that it sets up the directories for your web application. If your RoR URL is http://localhost:3000/hacks/monitor, the controller component is in the controllers directory at the following path: <app-root>/hacks/controllers/hacks_controller.rb. Let's take a look inside hacks_controller.rb for the definition of the zero_update action.

Controller objects are written in Ruby code. To generate an action, all you have to do is define a method inside the controller class (literally, a class that extends ActionController, which is part of the Rails API) with the action's name:

class HacksController < ApplicationController     def index     #defined in index.rhtml     end        def zero_update         render :text => "Ajax return value..."     end #rest of class... end

Complicated, huh? All the zero_update action does is return the specified text in the HTTP response using the Rails method render( ).

The code can also use the syntax render(:text => "Ajax return value...") to call this method, which takes a hash Ruby type as a parameter.


The request object handles the response behind the scenes, placing its content within the div with id complete. However, all the developer is responsible for is calling the method: you do not have to touch the request object or its response handlers. It's useful to have a look at the source code that Rails returns when the user requests the monitor.rhtml template:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"         "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head>     <meta http-equiv="content-type" content="text/html; charset=utf-8" />     <script src="/books/4/254/1/html/2//javascripts/prototype.js" type="text/javascript"></script>     <script src="/books/4/254/1/html/2//javascripts/effects.js" type="text/javascript"></script>     <script src="/books/4/254/1/html/2//javascripts/dragdrop.js" type="text/javascript"></script>     <script src="/books/4/254/1/html/2//javascripts/controls.js" type="text/javascript"></script>     <script src="/books/4/254/1/html/2//javascripts/application.js" type="text/javascript"></script>     <title>Monitor Ajax calls</title> </head> <body> <form action="/hacks/zero_update" method="post"  onsubmit="new Ajax.Updater('complete', '/hacks/zero_update',  {asynchronous:true, evalScripts:true, insertion:Insertion.Top,  onFailure:function(request){$('failure').innerHTML='Failure; request  status='+request.status}, onInteractive:function(request){$('inter').innerHTML='Interactive;  request status='+request.status},  onLoaded:function(request){$('loaded').innerHTML='Loaded; request  status='+request.status},  onLoading:function(request){$('loading').innerHTML='Loading...'},  onSuccess:function(request){$('success').innerHTML='Success; request  status='+request.status}, parameters:Form.serialize(this)}); return false;"> <h3>Monitor Your Ajax Calls with Rails</h3> <div  style="font-size: 1.2em"></div> <div  style="font-size: 1.2em"></div> <div  style="font-size: 1.2em"></div> <div  style="font-size: 1.2em; color: green"></div> <div  style="font-size: 1.2em; color: red"></div> <div  style="font-size: 1.2em; color: green"  onclick="clearIt($('complete'))"></div> <p> <input name="commit" type="submit" value="Start Monitor" /> </p> </form> </body> </html>

Notice all the script tags embedded in the HTML source. This was made possible by the <%= javascript_include_tag :defaults %> embedded method (see "Make Your JavaScript Available to Rails Applications" [Hack #57] for details on that one).

The server has to issue a separate GET request for each of these JavaScript files, so if you only need the prototype.js file, for instance, use <%= javascript_include_tag "prototype.js" %> instead of <%= javascript_include_tag :defaults %>. The effects.js, dragdrop.js, and controls.js files derive from the script.aculo.us library (see Chapter 8).


The imported JavaScript files include prototype.js, and the Prototype library just happens to include the Ajax.Updater JavaScript object that wraps the initializing and use of XMLHttpRequest. (See Chapter 6 for more details on this package.) So there you have itthe form_remote_tag( ) method represents a wrapper enclosing another wrapper, which handles the request object automatically.




Ajax Hacks
Ajax Hacks: Tips & Tools for Creating Responsive Web Sites
ISBN: 0596101694
EAN: 2147483647
Year: 2006
Pages: 138

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net