Prototype and Javascript in Ruby on Rails

2009 April 9

I am a purist. Or obsessive compulsive. Or maybe just irrational. But I H-A-T-E inline javascript. Unobtrusive javascript (like SWFObject or its predecessor, UFO) is the only way to go in my opinion. Seriously. Keep that stuff out of my markup.

This is why I love the Prototype.js framework and hate Rails javascript helper methods. The methods I most commonly come across (and hate) are methods like remote_form_for, link_to_remote, observe_field, draggable_element, drop_receiving_element, and so on…

Now, I know that all of these helper methods exist to make implementing neato “Web 2.0” javascript effects easier (for the lazy programmer) in a Rails application. And I know that Rails has Prototype and Scriptaculous built in to the framework, so it seems like saying “I hate Rails javascript helper methods” equates to hating Prototype as well. But it doesn’t. There are three simple reasons why I love the Prototype:

  1. Its utility methods ($, $$, $H, etc.)
  2. Event.observe, including document.observe(”dom:loaded”, function() {})
  3. Its extension of the DOM

If you understand (take the time to learn) how those three elements of Prototype can work together in perfect harmony, it is enough to never have to use Rails javascript helper methods again.

Oh! And I almost forgot about RJS templates… my contempt for RJS templates actually surpasses my hatred of Rails javascript helper methods.

I know this all sounds irrational, but it’s not. I’ve thought it out very carefully, and here’s another list. A list of reasons why I despise these helpers:

  1. It makes programmers lazy. In my experience the programmers who use these methods or templates don’t actually understand what they do. If they don’t understand what they do, then they don’t really know how best to utilize them. Which brings me to my next point:
  2. It creates ugly code. And beyond that…
  3. It’s difficult to debug. This is also why I loathe inline CSS styles.

Here’s an example of bad usage of a Rails javascript helper method.

<% milestone_ids = @milestone_groups.collect{|milestone| milestone.id}.insert(0, "") %>
<% milestone_ids.each do |milestone_id|%>
  <%= sortable_element "milestone_list_#{milestone_id}", {
    :dropOnEmpty => true,
    :tag => "div",
    :url => move_child_ticket_space_ticket_path(@space, "milestone_#{milestone_id}"),
    :update => "agile_panner_page",
    :complete => "toggle_ticket_list('milestone_list_', opened_sections['milestone_list_'], false); toggle_ticket_list('story_list_', opened_sections['story_list_'], false);",
    :containment => milestone_ids.collect{|mls| "milestone_list_#{mls}"} + @story_tickets.collect{|tkt| "story_list_#{tkt.id}"},
    :handle => "dragg",
    :constraint => false,
    :format => '/([0-9]+)/' } %>
<% end %>

That block right there gave me fits.

For one, it generated a separate javascript code block in the HTML for each milestone (point #2 from above). In this application, it was entirely possible to have 10 or more milestones. Can you imagine the cruft this generated?!

Second, the developer elected to create sortable elements in a situation where it was inappropriate (point #1). All that was required was some simple drag and drop functionality, order was not important.

And thirdly, because a sortable element was inappropriate for this particular situation, it created issues for the droppable elements that were placed on top of the sortable element (I know how confusing it sounds…) which were incredibly difficult to debug (point #3).

Thanks to the awesomeness of Prototype, this was an easy fix. Below is a simplified implementation of the javascript I added.

document.observe("dom:loaded", function() {
  lists = $$('.milestone_list');
  lists.each(function(el) {
    Droppables.add(el.id, {
      dropOnEmpty: true,
      hoverclass: 'list_droppable_hover',
      onDrop: function(draggable, droppable, event) { doAjaxUpdate(draggable, droppable, event, args); }
    });
  });

Look how clear that is. And it’s separated from the view! A lot of Rails developers I know are all about strict MVC separation, but they don’t consider it beyond the immediate confines of their Ruby/Rails code. I think javascript could be classified as doing similar work to a Rails controller. In which case, it should be separated from the HTML — the view.

I appologize if I insulted anyone with my rant, but I get really worked up about this. I think it’s important. It’s the same way I feel about inline styles, comic sans and IE6.

If you think I’m completely off my rocker or have a different opinion on the subject, then let me know. Maybe if your argument is as well written (and as self-important) as mine is, I could be convinced otherwise.

Comments are closed.