Using Rails's Object#to_json to create a clean JSON Presenter

by on

The problem: You have an ActiveRecord model (or any object, really) and you want to output it to json, but you need some specific methods and maybe some processing of its attributes before you can call .to_json.

Let's pretend we have the following object:

class Widget
attr_accessor :id
attr_accessor :name
attr_accessor :gadgets
end

class Gadget
attr_accessor :id
attr_accessor :name
end

And we want this json from a widget:

{
id : 'widget-47',
name: 'name of widget',
children: [
{
id : 'gadget-63',
name : 'name of gadget'
}
]
}

Note that ID has the class name on the front, the name is passed through directly. Also, the sub-objects (Gadget) have their id processed.

First, let's make the Gadget presenter:

class GadgetPresenter
attr_reader :id
attr_reader :name
def initialize(gadget)
@id = %{gadget-#{gadget.id}}
@name = gadget.name
end
end

Rails provides Object extensions that give us a to_json method on every object. By default, this will call all of our attr_readers. So, all we have to do is provide attr_readers for the attributes we want in the json. Now, Gadget#to_json would give us:

{
id: 'gadget-63',
name: 'name of gadget'
}

Now the Widget presenter:

class WidgetPresenter
attr_reader :id
attr_reader :name
attr_reader :children
def initialize(widget)
@id = %{widget-#{widget.id}}
@name = widget.name
@children = widget.gadgets.collect{|g| GadgetPresenter.new(g)}
end
end

Pretty much the same, except for the children. This lets us call the associate "children" instead of "gadgets". And all we have to do is collect up a bunch of gadget presenters. When .to_json is called on the widget, it calls to_json on the attributes. Since children is an array, it collects .to_json from each entry in the array, which calls the GadgetPresenter's to_json.

I really like this implementation because it's really just an adapter, and it only concerns itself with the data. It is also external to the Widget and Gadget class, allowing for multiple types of presenters easily.


Here is a running example: (requires rails 3) https://gist.github.com/703747

Comments

mattsears
Nice article, thanks for posting this. Quick question...where do you recommend storing the presenter classes in you Rails project? I thought about 'app/presenters', but maybe that's overkill?
bryanl
app/presenters works well. Keeping everything in app/models makes little babies cry.
blog comments powered by Disqus