Pragmatic Rails: Write RIAs, not websites!

{{{
Dude, I was googling for _”RIA and Rails”_ recently. Latest posts were dated 2007. Oh wait, there’s “one”:http://raibledesigns.com/rd/entry/groovy_rails_with_components_ria from 2008 basically stating *”Rails needs components”* – which is “nothing new to me”:http://nicksda.apotomo.de/2010/10/10-points-how-cells-improves-your-rails-architecture/.

h3. 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_*.

h3. 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?*

h3. 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.

h3. 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!

RIAs don't 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”:http://apotomo.de, so let’s use it.

# Gemfile
source 'http://rubygems.org'

gem 'rails', '3.0.3'
gem 'haml'
gem 'apotomo'

h3. 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/@.

h3. 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?

h3. Fantastic, widgets in Rails!

Now let’s explore that *_inbox_ widget on the right* that shows the list of mails.

class InboxPanel  :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 @#display@ state is invoked, which *finds out which mails to list*.
* Subsequently, it *renders its view* @display.html.haml@. How would that view look like?

h3. 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.subject

Don’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”:http://rubyflow.com/items/4842 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 do@ in 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_id@ returns the id) and command it to *”If a message link is clicked, send an AJAX request to that link’s event url!”*.

h3. 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  :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.*”

h3. 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  :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.

h3. RIAs, not websites!
}}}

{{{
“Apotomo”:http://apotomo.de 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”:http://github.com/apotonick/apotomo, the “project page”:http://apotomo.de/ and a “real example”:http://github.com/apotonick/apotomo_peters_guide.

}}}

27 thoughts on “Pragmatic Rails: Write RIAs, not websites!

  1. I think RIA will move towards nodejs or some new javascript server side technology. I love Rails but being able to use the same code on the server and client is awesome. The YUI3 guys have already shown how they can use a headless DOM on the server to render javascript rich controls before it’s even sent to the client. So, even if a user doesn’t have javascript enabled, the server can still render javascript controls. The kicker is, it’s exactly the same code.

    Used to be a big Rails fan, still am, but more and more I just create RESTful data services with NodeJS + Express. I use jQuery mobile for the UI. True DRY, one language for server and client.

    Like

  2. I gotta say, I would be much more receptive and more interested in all this and your cell framework if you lost the 4chan “fag” mentality and writing style.

    Like

  3. Yeah, the fag comment is completely offense and inappropriate. Remove it from your site. Do it right now. You’re welcome.

    Like

  4. This stuff is becoming more and more interesting to read, and easier to follow. I want to use it, but I don’t have the time right now. Some may find you writing style offensive, but I for one like it, I just ignore some words, and it’s all OK 😉

    Like

  5. Finally found Apotomo when looking for a widget based view for rails. Its easy to use, a great enhancement for rails.. i never really liked the original view of rails, so apotomo feels more like gui development. widgets are exchangeable and reusable, love it.

    Like

  6. Only #Flex can complement Rails to build real RIA apps.

    What do you think about Flex on Rails?.

    For me is the best combination. 🙂

    Regards.

    Like

  7. I like Rails and I like Ruby. But the only way to build “RIAs” is Silverlight. At least for me.

    Sorry, but I think whis effort will be one of many. Use it now. It works fine now. But in a year the project died and can’t be used anymore with a recent Rails version. Seen it too often that way.

    Where’s the point in building RIA’s by adding more and more pain to a web app? Sure with HTML5 and JS you can do nearly everything. But is it worth the price to pay? Web development is and will be always a pain. At least for me. I use Rails for simple Web stuff and .NET for “real” development.

    But keep up trying to RIAfy the web. Nothing is better than have a choice.

    Like

  8. >Who said this is a technical blog?

    Show me one non-technical entry on this blog in the last two years because I can’t find one.

    Like

  9. Silverlight? HA HA. Even MS isn’t emphasizing that anymore. Flex and Silverlight’s days are numbered. Mark gets it right above. Javascript rules all. Frameworks like Sproutcore, Cappuccino … are the future of RIA.

    Like

  10. @revoker: Did you really read all that shit? Wow.

    Honestly, entre nous, is it really that scandalising that I use the words i like, in my blog? Can’t understand that people take me that serious, I don’t want to insult anybody here… I’m just sick of enterprisy business talk, alright. Let’s be friends.

    Like

  11. @mark, @Andrew: I like the idea of one-language frameworks and I hate the fact that we have to use minimum 3 language here, but I love Ruby, so I try hard to connect at least Ruby+JS.

    @hmmm: Just stop reading my blog and start using my frameworks, they’re free of swear-words, promised!

    @Chris: Are you seriously feeling offended? Sorry for that :-* are you the mystery coder bloggin’ about components, too?

    @bobanj, @David, @Markus: Thanks, dudes!

    @RIA, @Silverlighter: That looks interesting but still is proprietary software which I dislike. The Silverlight page looks kinda out-dated… is that intended by M$?

    Cheers!

    Like

  12. Very well done, Rails need RIA efforts ! I Thought Sproutcore (and Cappuccino and similar js MVC framework…) will be the future, but more I use it and more I miss Ruby and Rails ! I miss the language and the philosophy, “Ruby is designed to make programmers happy (Matz)”, Javascript isn’t.
    In a perfect world the browsers would understand Ruby !
    Thanks fot the effort, I’ll try your framework soon, I just I have a small parked project…

    Like

  13. Hi Nick.

    I am working on an application with two layers of widgets on one page. In the rails controller there are two widgets added to the root; then in one of the the two widget’s “controller”, a number of child widgets are added.

    This is very similar to the sample application from http://github.com/apotonick/apotomo_peters_guide, where the tweet widgets are children of the panel widget which in turn is added to the root.

    I have noticed in the Rails development log that when I click on the heart icon on a tweet widget, first all tweets are selected from the database (SELECT * FROM “tweets”), then a second query is run which selects just the tweet that I have clicked (SELECT * FROM “tweets” WHERE “id” = ). It seems that the first query, the one which retrieves all tweet records, is run by the code which adds the tweet widgets to the panel widget ( for t in Tweet.find(:all) … ). This happens both in development and in production environments.

    My question is as follows: is there a way to avoid retrieving from the database all tweets when handling an event which should only affect one tweet?

    Like

  14. @djantea: Thanks, this is really a bit of inconvenient, however, we have to save the state somewhere, so I abuse the tweets table for that.

    I simply map every table entry to a child widget and thus can re-establish the same setup after a request. The problem is, when you trigger an event from a TweetWidget it is “not there” unless the TwitterWidget container sets up a widget tree in its has_widgets block.

    There are different solutions for that, e.g. use a stateful widget, store the list of child items in the session or a new approach which we’re developing these days where you can save the tweet ids in a “heartbeat” widget. I think this is exactly what you’re looking for and I will post about that as soon as it’s finished 😉

    Like

  15. Hi Nick. I tried to add a widgets called “rss” to this examples but I always get such an error:

    C:/Railsprojects/…./app/views/mail/inbox.haml where line #6 raised

    Missing template cell/rails with {:handlers=>[:erb, :rjs, :builder, :rhtml, :rxml, :haml], :formats=>[:html, :text, :js, :css, :ics, :csv, :xml, :rss, :atom, :yaml, :multipart_form, :url_encoded_form, :json], :locale=>[:en, :en]} in view paths “C:/Railsprojects/…/app/cells”, “C:/Railsprojects/…/app/cells/layouts” and possible paths [“rss_widget/”, “apotomo/widget/”, “cell/rails/”]

    from this line –> 6: = render_widget ‘rss’

    It was the same when I tried to run the example on the apotomo website.

    Can you help ?

    Like

  16. @Nathan: Your RssWidget should roughly look like this:

    class RssWidget < Apotomo::Widget
      def display
        render
      end
    end
    

    where the file app/cells/rss_widget/display.haml is a valid view. Does that help?

    Like

  17. Nick, thx for this & I agree that Rails is behind. The problem is there’s no Apotomo::Widget lib ready to use. If there was a group willing to wrap a JS widget lib, I wouldn’t choose jqueryui because there are very few widgets & there’s no documented look-&-feel (eg, nimbus)for widget developers. I just started reading about wijmo and Ext JS 4 is rumored to have a smaller footprint.

    I’m going to try JSF2 + primefaces widget lib.

    Like

  18. Great article and great effort!
    As a newbie to rails, I really feel that the RIA part is missing as opposed to the fantastic work that was done on the model in Rails.

    (I have experience in GWT and Silverlight which are pretty much RIA frameworks)
    I hope to try Apotomo and Cells soon!

    Like

  19. @Nick: I’m having the same problem as @Nathan (in case someone else stumbles upon this thread). My application has been upgraded so many times, it’s bound to be a misconfiguration somewhere. All the code is correct. Why it’s now looking for the view template in the cell/rails/display directory is a clue (notice it says “cell” and not “cells” — that’s another subtle clue).

    Like

  20. Ha, I just stumbled onto this prior comment of mine. It was definitely just something that got out of sync when I upgraded Rails.

    Like

Leave a comment