If we want to return custom JSON data from a Rails application based on model data we can do this in a number of ways. We could override the as_json method in the model or use the Jbuilder gem as we did in episode 320. Another popular solution is the RABL gem. RABL stands for Ruby API Builder Language and is a little more feature-complete that some of the other solutions.

We’ll show you how RABL works in this episode, using the same example app we used in the Jbuilder episode to give you an idea of the differences between the two libraries. This app is a blogging application that has a number of articles and we want to able to view a single article’s data as JSON by appending .json to the article’s URL. If we try this now we’ll just get a an error message as the application doesn’t know how to respond to JSON requests.

Getting Started With RABL

To do this we could modify the ArticlesController, make sure that the show action responds to JSON requests and then return a JSON representation of the selected article, but instead we’re going to use RABL. This comes as a gem and is installed in the usual way, by adding it to the gemfile and running bundle.

/Gemfile

gem 'rabl'

Like Jbuilder, RABL includes a template handler so that we can define the JSON response in the view layer. We’ll create one of these now for creating a JSON representation of an article. We can use Ruby code to do this using RABL’s DSL.

/app/views/articles/show.json.rabl

object @article
attributes :id, :name, :published_at

The first thing in a RABL template is usually a call to object and we pass this the object we want to work with. We can then call attributes to define the attributes of the object that we want to return. If we visit the article’s JSON URL now we’ll see the JSON we defined in the template returned.

We have access to helper methods in RABL templates so we can use edit_article_url to get the article’s edit URL and current_user so that the URL is only shown if the current user is an admin (assuming that the authentication solution we’re using has a current_user helper method).

The object that is passed to the block is the same object that’s passed to the object method but there’s a good reason why we should use the object passed to the block instead and we’ll explain why later in this episode. If we look at the article’s JSON now we’ll see the edit_url attribute with the correct value.

Associated Records

If we want to include data from associated records it’s each to do so. In our application an Article belongs to an Author and has many Comments. We can use RABL’s child method to include information from these associated models.

The code to generate JSON for the associated author is very similar to the code for generating the article’s JSON. The only difference is that an author is passed to node’s block instead of an article. When we view the JSON now we’ll see the article’s author’s details just like we expect.

In the JSON response the comments will be nested in an array, not just displayed in a single record as the author was.

terminal

$ curl http://localhost:3000/articles/1.json
{"article":{"id":1,"name":"Superman","published_at":"2012-01-19T18:38:50Z","edit_url":"http://localhost:3000/articles/1/edit","author":{"id":2,"name":"Clark Kent","url":"http://localhost:3000/authors/2"},"comments":[{"comment":{"id":1,"name":"Lois Lane","content":"Does anyone know where I can find Superman?"}},{"comment":{"id":2,"name":"Lex Luthor","content":"I have some Kryptonite for you Superman!"}}]}}

Reusing Templates

Our JSON data is nicely defined for an article but what if we want to use this output elsewhere in our application? We might want the ArticlesController’s index action to display the same JSON but for all the articles. This is where RABL shines; all we have to do is create a new RABL template for the index action.

/app/views/articles/index.json.rabl

collection @articles
extends "articles/show"

As we’re working with multiple articles we use collection instead of object here. We could define the attributes we want to use here like we did in the show template but as we want the same JSON for each article that we can reuse that template here by calling extends and passing in its name. If we look at articles.json now we’ll see the JSON data for all the articles.

terminal

$ curl http://localhost:3000/articles.json
[{"article":{"id":1,"name":"Superman","published_at":"2012-01-19T18:38:50Z","edit_url":"http://localhost:3000/articles/1/edit","author":{"id":2,"name":"Clark Kent","url":"http://localhost:3000/authors/2"},"comments":[{"comment":{"id":1,"name":"Lois Lane","content":"Does anyone know where I can find Superman?"}},{"comment":{"id":2,"name":"Lex Luthor","content":"I have some Kryptonite for you Superman!"}}]}},{"article":{"id":2,"name":"Krypton","published_at":"2012-01-05T18:38:50Z","edit_url":"http://localhost:3000/articles/2/edit","author":{"id":2,"name":"Clark Kent","url":"http://localhost:3000/authors/2"},"":[]}},{"article":{"id":3,"name":"Batman & Robin","published_at":"2012-01-26T18:38:50Z","edit_url":"http://localhost:3000/articles/3/edit","author":{"id":1,"name":"Bruce Wayne","url":"http://localhost:3000/authors/1"},"comments":[{"comment":{"id":3,"name":"The Joker","content":"Haha, Batman, you will see your bat signal tonight!"}},{"comment":{"id":4,"name":"Robin","content":"Enough with the games Joker."}},{"comment":{"id":5,"name":"Riddler","content":"Did someone say games?"}}]}}]

The ability to reuse templates like this is the reason why we should minimize the use of instance variables in RABL templates. As we only use the @article variable in the show template when we define the object it’s easier to reuse this template in the index template as we’re using the article that’s passed into the block in the call to node instead of the instance variable.

Root Nodes

One thing you might notice about the JSON that RABL outputs is that it includes the model name as the root node.

terminal

$ curl http://localhost:3000/articles/1.json
{"article":{"id":1,"name":"Superman","published_at":"2012-01-19T18:38:50Z","edit_url":"http://localhost:3000/articles/1/edit","author":{"id":2,"name":"Clark Kent","url":"http://localhost:3000/authors/2"},"comments":[{"comment":{"id":1,"name":"Lois Lane","content":"Does anyone know where I can find Superman?"}},{"comment":{"id":2,"name":"Lex Luthor","content":"I have some Kryptonite for you Superman!"}}]}}

Sometimes we want this but sometimes we don’t. In Rails 3.1 the default JSON output didn’t include the root element and if we want RABL to match this we’ll need to configure it. We can do this in a new file in the /config/initializers directory, setting include_json_root to false.

/config/initializers/rabl_config.rb

Rabl.configure do |config|
config.include_json_root = falseend

As we’ve created an initializer we’ll need to restart the app for these changes to be picked up. After we have the first article’s JSON will no longer include that root node.

terminal

$ curl http://localhost:3000/articles/1.json
{"id":1,"name":"Superman","published_at":"2012-01-19T18:38:50Z","edit_url":"http://localhost:3000/articles/1/edit","author":{"id":2,"name":"Clark Kent","url":"http://localhost:3000/authors/2"},"comments":[{"id":1,"name":"Lois Lane","content":"Does anyone know where I can find Superman?"},{"id":2,"name":"Lex Luthor","content":"I have some Kryptonite for you Superman!"}]}

There are details of the configuration options that can be set in the Configuration section of RABL’s README file. There are other features that we haven’t covered here. Other serialization options are covered such as XML and Message Pack.

Embedding JSON in a Web Page

Sometimes we need to embed JSON data in an HTML document instead of calling a separate controller action. How do we do this with something like RABL? The HTML template for the index action looks like this:

We’ll add the JSON for the articles in a data- attribute in the wrapper div. We could just call @articles.to_json but this won’t use the RABL template. Instead we can call render(:template) and pass the template’s name.

If we reload the page and view the source now we’ll see that embedded JSON.

html

<divid="articles"data-articles="[{&quot;id&quot;:1,&quot;name&quot;:&quot;Superman&quot;,&quot;published_at&quot;:&quot;2012-01-19T18:38:50Z&quot;,&quot;edit_url&quot;:&quot;http://localhost:3000/articles/1/edit&quot;,&quot;author&quot;:{&quot;id&quot;:2,&quot;name&quot;:&quot;Clark Kent&quot;,&quot;url&quot;:&quot;http://localhost:3000/authors/2&quot;},&quot;comments&quot;:[{&quot;id&quot;:1,&quot;name&quot;:&quot;Lois Lane&quot;,&quot;content&quot;:&quot;Does anyone know where I can find Superman?&quot;},{&quot;id&quot;:2,&quot;name&quot;:&quot;Lex Luthor&quot;,&quot;content&quot;:&quot;I have some Kryptonite for you Superman!&quot;}]},{&quot;id&quot;:2,&quot;name&quot;:&quot;Krypton&quot;,&quot;published_at&quot;:&quot;2012-01-05T18:38:50Z&quot;,&quot;edit_url&quot;:&quot;http://localhost:3000/articles/2/edit&quot;,&quot;author&quot;:{&quot;id&quot;:2,&quot;name&quot;:&quot;Clark Kent&quot;,&quot;url&quot;:&quot;http://localhost:3000/authors/2&quot;},&quot;&quot;:[]},{&quot;id&quot;:3,&quot;name&quot;:&quot;Batman &amp;Robin&quot;,&quot;published_at&quot;:&quot;2012-01-26T18:38:50Z&quot;,&quot;edit_url&quot;:&quot;http://localhost:3000/articles/3/edit&quot;,&quot;author&quot;:{&quot;id&quot;:1,&quot;name&quot;:&quot;Bruce Wayne&quot;,&quot;url&quot;:&quot;http://localhost:3000/authors/1&quot;},&quot;comments&quot;:[{&quot;id&quot;:3,&quot;name&quot;:&quot;The Joker&quot;,&quot;content&quot;:&quot;Haha, Batman, you will see your bat signal tonight!&quot;},{&quot;id&quot;:4,&quot;name&quot;:&quot;Robin&quot;,&quot;content&quot;:&quot;Enough with the games Joker.&quot;},{&quot;id&quot;:5,&quot;name&quot;:&quot;Riddler&quot;,&quot;content&quot;:&quot;Did someone say games?&quot;}]}]">

If we do use this technique it’s important that any instance variables use inside the rendered template are set in the controller’s action.