Rails and JavaScript: Part 1
You got your Rails in my JavaScript…
Previously on Locally Sourced: Well, in 2005 I came across a cool web framework called Ruby on Rails. More recently, I wrote a book about integrating Rails and front-end tools. If you want, you could buy it. Today, I thought I’d go back to the beginning…
Over the fifteen or so years that Rails has been around, the relationship between Rails and client-side JavaScript has gone in many different directions. What with Hey.com’s big release, and the attendant yet-to-be-released open source library changes, now seemed a good time to look at how that relationship has evolved and eventually come back to where it started. This got long pretty quickly, so I’m splitting it into a few parts. In this part, we’re going to talk about early Rails, by which I mean before the 3.0 release in 2010.
Early on, Rails’ goal was to allow you to get access to the client-side power of JavaScript while avoiding actually having to write JavaScript. This made a lot of sense in 2005, when the JavaScript tools were way less advanced that Ruby tools for testing, debugging, frameworks, basically anything you’d need to support development at any scale. Support was coming along, and would eventually get there, but it wasn’t there yet.
In the Beginning…
It’s a little hard to believe now, but Rails’ support for client-side Ajax interactions was one of the three or four big reasons for the excitement around Rails when it first came out in the mid 2000s (along with convention over configuration, database migrations, and support for testing). “Rails was one of the first toolkits to make dealing with Ajax easy to do without having to know a great deal of JavaScript”. (This is from my 2008 Wrox book on Rails. I realize quoting myself is terrible, but I also have this record of exactly what 2008-me felt about a bunch of Rails topics…)
Ajax was the big new thing, it was Web 2.0, and Rails supported it out of the box and without having to write JavaScript, in specific contrast to Django, which didn’t. Rails made it much easier to do Ajax calls than any similar framework of the time.
The most common version of Ajax at the time was make a server call that returns HTML, and inserting that HTML into the client page without doing a full page refresh – using the server for data-only largely came later. Rails provided the link_to_remote
and remote_form_for
helpers, which automated the process of making the Ajax call and allowed you to specify the DOM ID where the resulting HTML should go. Typically, any further JavaScript needs would be added to the event handler in the HTML markup itself.
This is genuine example code from the 2008 book. The update option here is the DOM ID to replace with the server result.
<%= link_to_remote "Edit",
:url => "remote_edit_ingredient_path(@ingredient)",
:method => :get,
:update => "ingredient_#{ingredient_id}" %>
The Rails core team has always been more comfortable having HTML generated on the server, and has only moved away from that reluctantly. Turbolinks, and especially the as-yet-unreleased new version of Turbolinks, allow for HTML to be on the server with more performance and flexibility then the early version.
RJS
Returning HTML worked pretty well, but was limiting. The next level of functionality was to call the server, have the server return JavaScript, and evaluate that JavaScript to allow for more elaborate functionality.
However, before Firebug allowed for in-browser debugging it was very hard to manage any kind of complex JavaScript. It was common for JavaScript to be inline in the HTML in onclick
handlers, or added in script
tags inside the HTML file itself.
With the idea that Ruby was a better developer experience than JavaScript, Rails added RJS. RJS was made of a couple of Ruby objects that accepted about twenty or so commands for simple client-side effects and emitted JavaScript to be sent to the browser for eval. It was pretty simple, kind of ridiculous, and I kind of liked it?
Here’s some sample RJS code, this would go in a view file with a name like views/recipes/edit.rjs
and you’d get it by hitting a route like views/recipes/edit.js
.
page.visual_effect(:fade, "recipes", :duration => 0.5)
page.delay(0.5) do
page.replace_html("recipes", :partial => "recipes")
end
page.visual_effect(:appear, "recipes", :duration => 0.5
This snippet would do a crossfade: fade out a DOM element, delay, replace its text, and fade in the new text. All the visual effects here were provided by a JavaScript library called Script.aculo.us, which was the Rails default for JavaScript effects. (These days, you’d could do the effects with CSS animations).
A couple of sidebars: At the time, it was still somewhat unusual for JavaScript to be distributed as a separate script file. In particular, it was much, much more common for event handling to be managed directly in the HTML via an onclick
attribute or whatever. Until Firebug and jQuery came out, setting up event listeners indirectly was a pain to do and a pain to debug, so it wasn’t something you did casually.
Also: RJS was by no means the weirdest or most elaborate attempt to leverage the a developer might already know to write client-side browser code. I direct you to Google Web Toolkit, which was (and is, it’s still around) a very elaborate attempt to allow Java code (and more to the point, something like Java UI libraries) to be compiled and executed in a browser.
For all that it was wildly indirect, I weirdly liked RJS, it was nicely focused on its small domain and it did what you asked, as long as you didn’t ask it to do much. I don’t remember it being very highly used though. It was pretty limited in scope, and it the snippets of JavaScript being sent via the browser were usually simple enough that using JavaScript for them wasn’t that big of an effort. You could even have .js.erb
templates to dynamically generate JavaScript, but I don’t remember ever doing that in a real project.
Eventually people wanted to do more complex things in the browser that RJS wasn’t really built for. We always knew that having random JavaScript from the server being evaled wasn’t a great idea, but it was the least-bad idea until the pure JavaScript libraries, debug tools, and packaging got better. I don’t think Rails core ever made a serious attempt to upgrade RJS, it was just quietly deprecated in favor of the next attempt to make JavaScript more palatable for Ruby developers.
As far as the Rails Way is concerned, the 2020 replacement for RJS is Stimulus, which has a similar goal of being a lightweight way to convert actions to client-side effects. As far as the rest of the world is concerned, the replacement is heavier-weight client libraries. Next edition will talk more about how we got there…
Next time: UJS, CoffeeScript, and Sprockets.