Using Ruby-generated JSON with Prototype.js
We are using JSON to return snippets of data from the server back to the requesting web browser. This post will explain the various steps needed to get this all working. Note: I am using Rails 1.1.6; some of this has improved in the beta releases for Rails 2.0.
First, we need an action. In my case, I’m doing a bit of data cleaning with a Ruby library, but you could use anything. The action sends a hash back to the client browser as a JSON object.
class WorkController < ApplicationController
def do_work
# Populate the hash however you choose, or use any
# object that supports to_json
result = {
:data => @params[:data],
:message => “the data is #{@params[:data]}”
}
render_json(result.to_json)
end
end
The render_json call doesn’t exist in Rails 1.1.6. In Rails 2.0, there will be a way to directly render for JSON. Until then, add the following method to your application.rb.
class ApplicationController < ActionController::Base
Mime::JSON = Mime::Type.new "application/json", :json, %w( text/x-json )
Mime::LOOKUP["application/json"] = Mime::JSON
Mime::LOOKUP["text/x-json"] = Mime::JSON
def render_json(json, callback = nil, status = nil) #:nodoc:
json = "#{callback}(#{json})" unless callback.blank?
headers["Content-Type"] = Mime::JSON
# Prototype.js will automatically evaluate X-JSON headers.
headers["X-JSON"] = json
#response.content_type = Mime::JSON
render_text(json, status)
end
end
Note that we are including the JSON twice in our rendered document. We include the JSON text in the document body but also in a X-JSON header. The X-JSON header is important, as it allows the Prototype.js JavaScript library to automatically build an object from the JSON text.
All the server-side work is now done. On the client, you will need to request and then process the result. We accomplish this with an asynchronous AJAX call:
/* Pass in some content id that will be updated with the result
obtained from the server */
function setup_work(contentid) {
/* Define a handler to process the result data.
The Ajax.Request call passes the XmlHttpRequest object
and an object created by evaluating the JSON data */
function handler(request,object) {
if (object) { // null if it couldn't be evaluated
document.getElementById(contentid).innerHTML = object.message;
}
}
new Ajax.Request("/work/do_work", {
parameters: "data=rails",
asynchronous:true,
onComplete:handler // Call our handler when the request completes.
});
}
For more help, you will want to reference the Prototype.js developer notes. The Ajax.Request method is well-documented along with its options and handlers.
2 Comments so far
Leave a reply


I am trying to use your method, however can’t seem to get either a) the headers right, or b) my view right.
What did you do for a view to accomplish this? Just an empty do_work.rhtml file?
Mike,
There is no need for a view, as render_text is just sending the requested string to the browser.
If you’re trying to troubleshoot this, I had the most luck using a Javascript debugger (both Venkman and Firebug under Firefox worked for me) and putting a breakpoint inside the prototype code. You should break at the point where the content is coming back from the server, before it is evaluated — evalJSON might be a good point.
If evalJSON isn’t getting called, your headers are wrong. If the headers look right and evalJSON is still returning null, the response text is malformed. In my case, this was usually a syntax error (bad quoting).
Let me know if you are still running into trouble. We are no longer using these JSON-generating actions, but I can resurrect them from Subversion if needed.
Aron