Rails JSON Templates Through RABL

Written by

rabl

These days we need our rails apps to do double duty- serving first as the user experience when someone comes to the site (rendering out HTML) and second serving as a platform for mobile applications or partner application. The current solution to serving as a platform is to render out JSON. This is a great solution, making use of our existing controller and model infrastructure. However Rails is a framework built on Model View and Controller. Where is the View when we’re serving out JSON? I think that using a JSON view fits the rails paradigm and makes development easier. I’m using RABL, I’d like to walk you through what using RABL for your JSON view looks like.

First, though, let’s look at how rails handles the JSON layer before any of this. Hopefully I can help illustrate why I believe this is not a scalable solution. ActiveModel includes a method called as_json. This takes your model and serializes it down into JSON. Neat! And it works pretty well. What happens though when you want to include nested attributes? Well there are two option, use the :include keyword in your controller or override the as_json method. Something like:

def as_json(options={})
 super(:only => [:username, :favorite_coffee], :include =>[:recipes])
end

This includes online the username and includes the nested recipes that belong to each User. This is great! By now your front end guy (or hey, maybe that guy is you wearing a different hat), needs the favorite_coffee attribute to come back as favoriteCoffee. Well, ok it’s one change away in the as_json. And, shoot, now you need to include different attributes based on different permissions. What’s the solution then? Pass what level the current user is down to the model? That doesn’t make a ton of sense to me, authentication is usually handled at the Controller level.

And, here we are at RABL. RABL is a view layer you’ve been dreaming about for Javascript. Now I’d like to walk through building a sample app.

Quickly!

 rails new coffeeHouseApp
 rails generate scaffold Patron name:string favorite_coffee:string credits:integer grouchy:boolean
 rails generate scaffold Laptop name:string operating_system:string patron_id:integer
 rake db:migrate

And then drop in the relationships

class Laptop < ActiveRecord::Base
 belongs_to :patron
end

class Patron < ActiveRecord::Base
  has_many :laptops
end

Ok, great!  We have a functioning app.  Now the real RABL’ing begins.  First, we need to make sure rabl is part of our Gemfile.  You know how that goes, drop this line into your Gemfile:

gem 'rabl'

and then tell bundler to go get that dependency

bundle install

Now we want our patron record to render out as a nice JSON representation.  We first need to update the controller:

# GET /patrons/1
# GET /patrons/1.xml
def show
  @patron = Patron.find(params[:id])

  respond_to do |format|
    format.html # show.html.erb
    format.json
  end
end

Now we need to provide that JSON template.  Create a new file under app/views/patrons/show.json.rabl  Inside that file let’s put the following:

object @patron

attributes :name, :favorite_coffee

child :laptops do
  attributes :name
end

This exposes an object called patron, and only the name and favorite coffee attributes.  It also exposes the nested laptops, but only their name.  This is great, we’re keeping the grouchy attribute secret on the Patron.  After all, we don’t want our third party partners taking advantage of our grouchy patrons.  Also under laptop we’re only exposing the name.  Good call again, for security.  The resulting JSON looks like this:

{
    "patron": {
        "name": "Nick Rowe",
        "laptops": [
            {
                "laptop": {
                    "name": "Lappy"
                }
            }
        ],
        "favorite_coffee": null
    }
}

So now here’s where it gets cool.  One of the business guys gets wind that we’re calling them patrons.  “Oh no, he insists, that’s far too formal.  We call them Guests.  And, we do want to expose the grouchy attribute but only on days of the month divisible by 7”  A bit, weird but not problem.  Our template now looks like this:

object @patron => :guest

attributes :name, :favorite_coffee => :favoriteCoffee

if (Date.today.day % 7 == 0)
  attributes :grouchy
end 

child :laptops do
  attributes :name
end

Nice!  Well that’s a taste of RABL.  You can learn a ton more by checking out the RABL github page.  You can also learn more about why the original author thought RABL was a good idea by checking out his blog.

What do you think about RABL?

Comments or Questions? Contact Nick @nixterrimus on twitter.

Nick is a software engineer, geek, web enthusaist, open source contributor, home automation tinkerer, ocean admirer and all around general optimist living in San Francisco. Want to get in touch about professional matters? Nick Rowe is also available on LinkedIn.