Pragmatic Rails: Write RIAs, not websites!
Wednesday, November 24th, 2010Dude, I was googling for “RIA and Rails” recently. Latest posts were dated 2007. Oh wait, there’s one from 2008 basically stating “Rails needs components” – which is nothing new to me.
What’s a RIA?
A RIA nothing more than an acronym for Rich Internet Application. What’s that, again?
- Usually that’s a Web 2.0 application running in your browser with rounded-corners, drop shadows, gradients, drag&drop, autocompletion and using at least one Javascript framework.
- All of them heavily rely on AJAX to update parts of the page dynamically in the background.
- A prominent feature is a dashboard with multiple widgets that update dynamically. Everybody wants dashboards.
- Lots of intranet applications like administration backends or CMSes are RIAs whereas the companyies’ public websites usually suck.
- They look and feel just like real desktop applications back in the days when we all had fat clients.
The goal of RIAs is to maximize, well, usability.
RIA and Rails?
Ok, now what’s wrong in Rails? It’s a great web framework. Where are all the RIAs written in Rails? Where are all the HOW-TOs about writing RIAs in Rails?
Honestly, please google and tell me what you found, I’m more than curious.
Let me state two thesis why we don’t see too many RIAs with a Rails backend.
- RIAs on Rails exclusively run in company intranets. Since your NDA prevents you from telling us how you wrote it it’s needless to look for posts.
- It is comparably hard writing a RIA with Rails and most people do 90% of their work in a JS framework directly – Rails doesn’t do much except sending back JSON to the untested code running in the client, so why write a post about RIA on Rails?
Rails’ VC layer is plain
Compared to the work put into the model tier where we got Active[Model|Record|Relation|...], the view layer is still plain. We got a bit AJAX here, a bit useless RJS there, however, there’s a lack of something called web components.
The problem here is that most people just take it for granted. They don’t question the paucity of components – and use work-arounds.
This is my plea for a better view layer. I don’t blame Rails for its thrifty widgets support – I have an offer to make.
Let’s write a RIA in Rails!
This is a schematic screenshot of an email app. Hey, I never said RIAs have to be pretty!

That’s three widgets.
- Top-left lists the mail folders, bold ones contain new mail. Naturally, a folder has to update when the user read the new mails, and when all mails are read, it shouldn’t be bold anymore, right?
- Top-right shows the mails in the currently selected folder, like the inbox.
- When clicking a mail, it will pop up in the bottom so the user may read it.
Now it does make sense to to model these three independent parts as widgets, or web components. Apotomo is Rails’ one and only web component framework, so let’s use it.
# Gemfile source 'http://rubygems.org' gem 'rails', '3.0.3' gem 'haml' gem 'apotomo'
Generating widgets
In order to save time, why not let Apotomo create us widget stubs? This is just like creating controllers.
$ rails g apotomo:widget inbox_panel display --haml $ rails g apotomo:widget mail_folders display --haml $ rails g apotomo:widget mail_reader display --haml
Widgets classes and assets are placed in app/cells/.
Controllers use widgets
We need to tell the controller about our plans on using widgets.
class MailController < ApplicationController include Apotomo::Rails::ControllerMethods has_widgets do |root| root << widget(:mail_folders, 'folders') root << widget(:inbox_panel, 'inbox') root << widget(:mail_reader, 'mail') end def inbox end
This is really just a call to has_widgets where we configure the widget tree. Passing the widget class and unique id to #widget we actually create ‘em.
Rendering widgets usually happens in controller views.
# views/mail/inbox.haml = render_widget 'folders' = render_widget 'inbox' = render_widget 'mail'
See how I refer to the widget ids in order to render them?
Fantastic, widgets in Rails!
Now let’s explore that inbox widget on the right that shows the list of mails.
class InboxPanel < Apotomo::Widget responds_to_event :msgClicked, :with => :process_click def display @mails = Mail.inbox render end def process_click Mail.find(param(:id)).read! replace :state => :display end end
When calling #render_widget('inbox'), two things happen.
- The
#displaystate is invoked, which finds out which mails to list.
- Subsequently, it renders its view
display.html.haml. How would that view look like?
Widgets have their own views
This is the widget’s view in app/cells/inbox_panel/display.haml.
= widget_div do
%table
- @mails.each do |m|
%tr
%td
= m.sender
%td
%a{'data-event-url' =>
url_for_event(:msgClicked, :id => m.id)}
= m.subjectDon’t tell me that tables are out, fag.
— UPDATE —
The fag term doesn’t refer to homosexuals at all, but to stupid “software developers” who prefer to argue about tables vs. divs instead of concentrating on designing better software.
The crucial point here is the call to #url_for_event which sets up a data attribute used in our unobstrusive Javascript. Right! We missed the Javascript of our view.
:javascript
$("##{widget_id} a").click(function() {
$.ajax({url: $(this).attr("data-event-url")})
});Now what’s going on here?
- The
widget_div doin line 1 (next-to-last snippet) simply wraps our widget view in a div with the widget id.
- Using a small Jquery snippet we refer to exactly that div (
widget_idreturns the id) and command it to “If a message link is clicked, send an AJAX request to that link’s event url!”.
Responding to events
Apotomo processes that AJAX request and as the inbox widget is looking out for :msgClicked events it processes the click, marks the mail as read and redraws itself in the page.
class InboxPanel < Apotomo::Widget responds_to_event :msgClicked, :with => :process_click # ... def process_click Mail.find(param(:id)).read! replace :state => :display end end
The curious reader might ask “What does that #replace do?” and I would reply “Just read! Replace yourself on the page by re-rendering the #display state.“
Loosely-coupled pieces
How’s the rest of the widgets informed about the click? The folders should update, and the message should be displayed in the mail widget.
Right, we (again!) use event observers to keep the widgets up-to-date. The mail widget roughly looks like this.
class MailReader < Apotomo::Widget after_add do root.respond_to_event :mailClicked, :with => :read, :on => self.name end # ... def read @mail = Mail.find(param(:id)) replace :view => :display end end
Again, we use #respond_to_event, this time we attach it to the very root widget in order the catch all bubbling events. Regardless what happens here in detail, the event system already shows its strengths.
- Two independent widgets, one triggers and catches, the other one only consumes events. Both do update at the same time without knowing anything about each other.
- By attaching observers to root – which are a kind of “global observers”, widgets stay completely decoupled from the outer world. They just state their interest in a certain event and take action.
RIAs, not websites!
Apotomo is simple to learn, and we got plenty of tutorials on the project page.
However, its fine-grained widgets, the event system and the ability to send any Javascript back to the browser opens a new way for web application development in Rails – for RIA development in Rails.
- Take the Javascript framework of your choice, Apotomo is completely JS-agnostic.
- Write rock-solid tests for each component and complete widget trees.
- Move away from the page-centric controller to a more GUI-like development process.
Tell me what you think about it and check out the github repo, the project page and a real example.


