Serve up JSON Metadata in Rails

March 28th 2010

So Imagine that you’re using will_paginate in your Rails app.

In order for the display to update properly, you’ve gotta give the view back the current page number, the number of records on a page, and the total number of records you allow. You might do that like this through json:

    def index
      @articles = Article.paginate(:page => params[:page])
      render :json => {
        :data  => @articles,
        :total => Article.count,
        :page  => params[:page] || 0,
        :per_page => Article.per_page
      }
    end

But then that muddles up your pretty JSON interface with a bunch of metadata. Say goodbye to using JesterJS, or any other ActiveResource out there. It also means you miss out on the respond_with magic of Rails 3.

But hey – HTTP already serves up this metadata in the form of headers. Why don’t we just set all of these values in the appropriate header?

  def metadata opts = {}
if params[:format] == :json
response.headers['X-JSON'] = opts.to_json
elsif params[:format] == :xml
response.headers['X-XML'] = opts.to_xml(:root => 'metadata')
end
end

def index
@articles = Article.paginate(:page => params[:page])
metadata :total => Article.count,
:page  => params[:page] || 0,
:per_page => Article.per_page
respond_with @articles
end

Pretty and DRY, all at the same time! Looks like a win!

Since prototype automatically evals the X-JSON header, we can write a tiny bit of javascript to update our view accordingly:

new Ajax.Responder({
  onComplete: function(instance, request, metadata) {
    if ((typeof metadata !== "undefined" && metadata !== null)) {
      $$(.current_page).each(function(elt) {
        elt.update(metadata.page);
      });
      $$(.total_records).each(function(elt) {
        elt.update(metadata.count)
      });
    }
  }
});
blog comments powered by Disqus