Rails Goodies

So, you’ve got Rails going and you’re wondering how to make your application wonderful. Not just great, but fun. Not just new, but full of brilliance. Or maybe you’d like to make you’re job easier and are looking for some nice code to ease into your application. This week is all about Rails Goodies.

Special Thanks to Dan Phiffer for coming in to help us really understand all that is AJAX and javascript.

Spicing things up with Javascript

Javascript is back in vogue, and everyone’s talking about AJAX. What is AJAX? It’s some old tech, that just became a lot easier to work with and is really beginning to catch on with web developers and users. Behind the scenes lies the XMLHttpRequest object which allows your page to pass and receive messages from an already loaded page. This allows you to pass new events such as user mouse clicks back as requests for updated information without having to reload the entire page. For example, the user clicks on a table column and that triggers a sorting event, and your application responds with the newly sorted table without refreshing the rest of the page.

The Setup

To get started using the javascript libraries in Rails, you need to include the following in your layout head:

<head> <title>Now with AJAX</title> <%= javascript_include_tag :defaults %> </head>

With the javascript_include_tag, :defaults will include everything. You can be more specific and specify :prototype for AJAX remote calls alone, :effects for the Scriptaculous visual effects, :dragdrop for drag and drop help or :controls for autocompletion, in-place editing and observers. You can specify more than one with comma separation. <% # javascript_include_tag :prototype, :controls, :effects, :dragdrop %>

Just place that javascript_include_tag in the layout you want to use it in or create an application layout under app/view/layouts/application.rhtml.

The presence of application.rhtml will trigger that layout for your whole application versus say shows.rhtml, which will only get triggered for requests through the shows_controller.

link_to_remote

It pretty easy to setup remote calls in Rails. Instead of a normal link_to call, replace it with link_to_remote. They’re very similar but link_to_remote not only specifies the link to trigger on the server (ie: one of the actions you set in a controller for your app) it can specify an element to put the results.

<ul id="shows"> <% for show in @shows %> <li><%=h link_to(show.name, { :action => :show, :id => show.id } %>: <%=h show.description %> <%= link_to_remote "Delete this Show", { :url => { :controller => :shows, :action => :delete, :id => show.id }, :update => "shows" } %> </li> <% end %> </ul>

There’s two parts here. The first part is just like the normal link_to it sets up the text portion of the link, which in this case is “Delete.” The second part builds the AJAX request, :url is the action we want to trigger in our app and :update is where the updated results will go. For this example, the url calls the shows_controller with the action delete and and id equal to show.id and the results will be placed in the portion of HTML with the id of #shows (eg. <div id="shows">shows go here...</div>).

form_remote_tag

Much like link_to_remote, form_remote_tag can replace your start_form_tag forms to make remote AJAX calls instead.

<div id="results"> Results will get added here </div> <%= form_tag_remote { :url => { :action => :create }, :update => { "results" }, :position => :bottom } %> <%= render :partial => "form" %> <%= submit_tag "Create" %> <%= end_form_tag %>

Now as you complete the form, the new items will fill in the div with id #results.

observe_field

Speaking of forms, how about interacting with form input? For that check out observe_field and observe_form. A simple example might be a guessing game where you check the input against a secret word and set user feedback along the way. It’s not hard to setup an observer on a form field, you can just drop the following into your form:

<div id="results"></div> <%= form_tag %> <%= text_field_tag :guess %> <%= observe_field :guess, { :frequency => 0.5, :update => :results, :url => { :action => 'check_guess' }, :with => "guess" } %> <%= end_form_tag %>

This will setup the form and drop in the javascript, which will send in a guess every 0.5 seconds to the action check_guess. Now to get the controller ready we need to fill in the details for the check_guess action.

def check_guess @guess = request.parameter["guess"] if @guess == "secret" render :text => "Shucks, you got it!" else render :text => "Nope, that's not it." end end

Here in the controller we’ve grabbed the parameter guess, which we slyly setup by specifying :with => "guess" above in the observe_field. Now @guess holds the user’s guess, which we’ll compare to our secret word. Based on the answer different text will be rendered, replacing our empty results div in our template. If you need to you can also make a template check_guess.rhtml which will then replace the results div, just make sure to specify render :layout => false so the whole page layout doesn’t get rerendered into our little div.

RJS is HOT

The hype behind RJS is red hot at the moment. One of the great, new, whiz-bang features of Rails 1.1 is the prospect of avoiding javascript entirely. Huh? In the place of writing your own javascript, with RJS templates you write Ruby code that gets translated into javascript when rendered.

Say you’ve got a blog and you want to add AJAXy comments. Our controller will have another action add_comment, but instead of add_comment.rhtml, we’ll create and RJS template. The code for the add_comment action is pretty simple.

def add_comment @post = Post.find(params[:id]) @comment = @post.comments.create(params[:comment]) end

It creates a new comment based on form input and associates it with a blog post. In order to view the results, you might have templates like the following

app/views/posts/show.rhtml: <%= render :partial => "post" %> <h3>Comments</h3> <ul id="comments"> <%= render :partial => 'comment', :collection => @post.comments %> </ul> <%= form_remote_tag :url => { :action => 'add_comment', :id => @post} %> <%= text_area :comment %><br /> <%= submit_tag 'Comment' %> <%= end_form_tag %> app/views/posts/add_comment.rjs: if @comment.valid? page.insert_html :bottom, 'comments', :partial => 'comment' page.visual_effect :shake, "comment-#{@comment.id}", :duration => 2.0 end

What do we have here? There’s a AJAX form sending new comments to the action add_comment, but instead of an rhtml view template in its place is an rjs template ready to be translated into javascript code. The action add_comment is executed in the posts_controller and the template add_comment.rjs is triggered. If the comment is valid, the RJS template will insert the comment at the bottom of the div with the id of #comments and use the visual effect shake for two seconds. Note that the partial comment is used as the partial template for the new comment.

One caveat: if you already have an add_comment.rhtml file, then that takes precedence over add_comment.rjs.

AJAX and Accessible Sites

We’ve left something out. This is the easy way to handle AJAX in Rails, but it’s not the most inclusive. It shuts out users without javascript enabled including Googlebot and other spiders, text-based browsers and blind folks using screenreaders. If javascript is not available then there should be some fallback action offered. The link_to_remote helper can take an html option to setup a link that can be used in place of the javascript call. This is great, but you’ll need to make sure that you’re controllers can process the request properly.

<%= link_to_remote "Show Description", { :url => { :action => :show, :id => show.id }, :update => "show-description" }, :href => url_for{ :action => :show, :id => show.id } %>

The above code might update an element showing the description of one of our shows. If the user doesn’t have javascript, that’s okay because when they click the link it will take them directly to that shows’ page. This is a pretty harmless link, meaning nothing changes or gets destroyed. There are cases, like a link that deletes, where we must tread lightly. The reason is anyone can write a spider or a bot that could come along and clumsily click all of our links including our delete links. Ouch, there goes all of our data. Normally, it’s suggested to avoid these kinds of links and use forms instead. There’s ways around this too though. Jarkko Laine has a nice article on creating safe and accessible links with Rails that also protect you from click happy spider bots like Google Web Accelerator.

The best advice seems to be that you should build your site without AJAX first and then add it where appropriate maintaining the non-javascript abilities and adding the fun new stuff alongside for those that can use it.

Plugins

There’s a world of shiny Rails plugins waiting for you to discover them. You can check out the Rails Wiki, for information and add the source to your rails app easily. Using script/plugin you can add a plugin’s Subversion repository url to the list from which your app can download, as well as install and uninstall the actual plugins.

$ ruby script/plugin discover ...list of repositories you can add... ...Type "Y" or Enter to add them and "N" to skip them... $ ruby script/plugin install acts_as_taggable $ ruby script/plugin list ...Lists available plugins...

Exercises

Homework (Due 2006-06-13)

Reading

Things to Try

Development Journal