I'm in David Heinemeier Hansson's tutorial on Beneath-the-Page Application Development with Rails. His Rails tutorial from last summer remains one of my most viewed blog entries.
He starts out noting that AJAX is the most important innovation for the Web in years.
Part of the problem is the sorry state of browser. One line of change can lead to hours of regressions because of browser incompatibilities. Then there's the browser underworld (all the old, out of date browsers that are still out there). That's the bad.
Then there's the ugly. Nodes are not for people. The idea of "createNode" as an API call has a nice academic feel, but it doesn't add up to something that's pleasant for developers to use. But the main problem with nodes is that they cause you to repeat yourself. You have to create the first version of the page using HTML. Your entire UI is mapped out in template files. To make a dynamic change, you have to say it again another way. This creates two versions of the interface--you have to take great care to ensure that they don't drift apart.
So, how do we deal with the bad, not return calls to the ugly, and champion the good?
David is building a demo application from scratch using Rails (v1.1) to show his approach to page development and AJAX. The first time I saw this it was amazing. I did one for Kelly Flanagan (BYU CIO) a few months ago, so if I can do it...maybe it's not so amazing! :-) You can get the functionality he's demoing today using beta Gems.
The first part of the demo follows the basics "use rails to create a MVC system" script pretty closely. He's building a simple ecommerce site. Once he's got a way to add products, display them, and adding things to a shopping cart, he starts adding AJAX.
page[:cart].replace_html :partial=> "cart"
From within the page, you can get a reference to specific parts of the page and replace them with something else. This makes use of "partials" an rhtml page with a name that starts with the underscore.
So, David's pulled out the shopping cart HTML and made it a partial. That's referenced in the template and in the Ruby code. With that, he can update the shopping cart in an AJAX way without refreshing the page.
The cart has been in the session as an array. David creates an object for the cart (not persistent) and adds totals. By just changing the partial, the total is added to the shopping cart without modifying nodes in one place and HTML in another.
Adding a "discard" function to the cart demonstrates the use of inline rjs code. This is useful when the function is pretty simple. Adding this doesn't work, so he shows how you can add an "alert(exception)" to the app and get debugging info in the browser.
Next David adds a "remove from cart" link next to the product (pedagogically placed there instead of in the cart itself). He adds logic to test whether or not the product is in the cart to determine whether or not the link is shown. The problem is that you don't reload, so you need to update products whenever something is added to the cart.
This calls for another abstraction. Clipping out the product display HTML as another partial does the trick. Actually, there's two partials, one for products and one for product inside the products partial. The next problem is that the "add" method doesn't have all the products, it has the new one. That can be fixed by adding somme code. David mentions that this points out the problem of state maintenance that AJAX code introduces. I wonder if continuation-based Web applications could solve this?
if request.xhr? render else redirect :action => "index" end
The other key piece is to ensure that good hrefs are built in addition to the "onclick" actions. This creates some ugly code, so he defines a helper function to make it happen.
A new rule: make it snazzy. AJAX applications are distinctively different from regular Web apps. Effects not just fluff--they're there to give the user confirmation that something has happened. David shows the video from Fluxiom, an asset management application, that looks like a desktop application but is all in Rails. This is the future of Web applications.
Next David shows off a new feature of Rails that allows you to drive the application just like a browser would from the console. It's very similar to a command line browser that remembers sessions, etc. and gives them to the user as Ruby objects that can be manipulated. This is great for writing unit tests for a Web application.
David's been following microformats. Microformats, annotate XHTML in such a way that machines can easily digest it. It's a way to render data in a human readable way so that there's only one view. He returns to the shopping cart example to show how classes can be added to embed semantics. By adding "product", "name", and "price" class attributes to the template, he gets a microformat.
To make this useful for machines, he changes the controller so that the to check the content type of the request. If it is "application/xml" he assumes that the client wants just the products and not the HTML wrapper. This allows the same controller to function for both browsers and programs.
He starts writing code to process what gets returned and gets an error. Lesson: you have to generate valid XML if you're going to parse it as XML. Of course, you've got all the XHTML tags as nodes and so navigating it can be difficult (you want to navigate the class attributes). David's working on an API that would allow class attribute-based navigation.
An alternate solution is to use XSLT transforms from within Ruby to transform the XHTML to XML. This isn't actually working yet, apparently, but would be pretty easy to set up.