Return Data not Code
When people think about AJAX in the Rails community they might think of something like:
Where this is meant to use format.js to take care of the ajaxy stuff. However, I believe this is incorrect and stems from a misunderstanding of what Javascript does. This is not a shortcoming of the knowledge of the Rails community. Javascript has had a troubled life. It wasn’t until libraries like Prototype.js came along and started to show web developers the good parts of Javascript that I really started to see things change. And then it wasn’t until Progressive Enhancement and Unobtrusive Javascript came along that we started to treat Javascript like a first-class citizen.
If you were playing with the web stack (HTML/CSS/JS) ten years ago then you know that Javascript boiled down to a lot of horribly obtrusive one-liners that fit into onclick attributes in links. We’re past that. I think that as developers living on the edge of the second decade of the 21st century we should be able to say “We are past that.” We have libraries like jQuery. We have highly concurrent servers like node.js. We have an understanding of Javascript functionally rather than procedurally.
Getting Past That
Oftentimes the format.js will contain something like this:
This is not okay. I admit I have code doing this right now. I do not like it, and I am actively seeking out ways to kill it. Writing something like this means that when I eventually build an API for my resources (and I will have to if I believe the year is 2009) that a Javascript representation will make the following assumptions about the requester:
- it has jQuery
- it will
evalthe response - it has a DOM element with the ID
some_id_in_the_layoutthat should be shown
An application ought to return data, not code[needs citation :P]. The javascript conventions provided by Rails hardly embrace this idea with things like RJS. But I will readily admit that I often don’t know how to solve certain problems without having an action return data and code. One way that I’ve started to approach this problem is by letting my javascript do things based on HTTP status codes. I’m not too sure about these ideas but I imagine sharing them engenders solutions. Doing what I was doing ten years ago engenders ignorance.
Let’s say you want an action to return real data on a AJAXy GET.
Why is this approach better?
- It is all Ruby. There is no javascript in a
render :updateblock. And there are no RJS idioms tightly coupled to Prototype.js - It will honor the HTTP Request’s Accept Header but only render that resource, not an entire page.
- It makes it difficult to make unnecessary exceptions about a resource. Each resource should have a set of representations (i.e., formats). I honestly don’t know what the Javascript representation of a Post resource would be, but I think it might have something to do with embedding (e.g., Embedding a gist) rather than out-of-context manipulation.
So what about the HTTP Status Codes? One place I’m currently using this approach is with an unobtrusive login system. The require_login filter might look something like this:
Now in javascript(with jQuery) an attempt to perform an action that requires a login can behave appropriately:
So the idea here is that when any AJAX request receives an HTTP Error Code it can act appropriately without the server needing to return javascript which is tightly coupled to the application server. This can apply to success codes as well with the ajaxSuccess hook in jQuery. I’m still trying to figure out which codes go where and in which circumstances they ought to be returned. My theory is that the 406 Not Acceptable could be used when a validation fails while attempting to create or update a resource. If you have any ideas what the right idioms might be please leave a comment or contact me @scudco.
In the end this is all an evolutionary process. Most advances in understanding do not come from “You have been doing it wrong.” Instead, they come from phrases like “Maybe I am doing it wrong?” or “Maybe I can do this differently.” And that is exactly how I feel about RJS. Something rubs me the wrong way and I know it can be done differently. For me, RJS was a great stepping stone toward a deeper respect for javascript.
Adam