Representers: a brilliant idea from Nick Sutterer

(written by Lawrence Krubner, however indented passages are often quotes)

This article is like a complete education regarding REST in Rails, both its strengths and weaknesses. And then Nick Sutterer ends by suggesting a new abstraction layer that cleans up the clutter that can occur if you are not very, very careful.

What’s wrong with form POSTs?

Nothing! Using the #form_for helper and Rails params is absolutely ok. However, I want to point out that POSTs are not limited to HTML forms – they can also send other representations.

So, what Rails does behind the scenes is parsing the incoming application/x-www-form-urlencoded representation of your resource and make the deserialized representation available in #params.

This automatical behaviour is all possible if you stick to Rails’ conventions, meaning very limited XML formats and thus representations.

Working with parsed representations

In the simplest case it looks as if the Rails approach would strike.

class OrdersController < ApplicationController
def create
@order = Order.create(params[:order])
# ...
end

However, in our case, this won’t work as we have “non-standard” tags in our incoming representation. The internal parser in Rails doesn’t recognize that and things will explode.

The internal parser?

Yeah, Rails has a hard-wired internal parser to deserialize a couple of mime- types (“POST parameters”, XML and JSON). This code is found in ParamsParser here.
I’m sorry to say that but that middleware is a debatable concept in Rails. While it blindly parses incoming request data it also urges you to use Rails’ limited representation convention.

If you don’t go the Rails way and use very simple XML representations, it will simply not work.

Parsing D.I.Y.

Now that Rails doesn’t give us what we need we have to do it ourself.

class OrdersController < ApplicationController
def create
item_urls = Nokogiri::XML(request.raw_post).
xpath("//item/link").
collect { |i| i[:href] }

@order = Order.new
# ...
end

Looks horrible.

The dilemma – where is my representation?

A severe problem emerges – where is my representation? Or, in other words: where are transformation rules stored?

Rendering the representation involves either an XML template or an adjusted #to_xml which knows a lot about the structure and semantics of our representation.

Parsing the representation implies a hand-made XML parser that itself knows about the very structure and semantics of the representation, again.

The dilemma: Knowledge about the representation is cluttered over the entire MVC-framework.

A new abstraction layer: Representers

What might sound over-engineered to many developers used to the Rails convention is my new approach which abstracts “working with representations” to a new layer.

A so called Representer handles both the in- and outs of representations, not sharing the internal syntax and semantics with the outer world (the best we can).

The Item representer looks simple.

class ItemXmlRepresenter < Roar::Representer::Xml
name :item
link

property :amount
end

The Order representer already contains a composition – the ordered items.

class OrderXmlRepresenter < Roar::Representer::Xml
name :o rder

property :id
property :items, :tag => :item,
:as => [ItemXmlRepresenter]

link do |controller|
:rel => :self, :href => controller.order_url(self)
end

link do |controller|
:rel => :checkout,
:href => controller.order_checkout_url(self)
end
end

This is a part of my REST framework roar which tries to help with writing real RESTful apps in Rails, Sinatra and Padrino.

How does roar help me?

Now that we defined the representers we can use them in controllers.

class OrdersController < ApplicationController
def show
@order = Order.find(params[:id])

xml = OrderXmlRepresenter.serialize_model(@order)
# ...

The call to #serialize_model will render the XML representation for the @order object using the proper representers, including real HATEOAS hyperlinks, nested items, and so on.

What comes out is the XML document I introduced in the beginning paragraph.

Now, how do we process input?

class OrdersController < ApplicationController
def create
rep = OrderXmlRepresenter.
deserialize(request.raw_post)

@order = Order.create(rep.to_nested_attributes)

# ...

When handling incoming data the #to_nested_attributes method from the representer helps transforming the data into a hash complying to nested-attributes requirements used with #update_attributes.

Benefits?

Representers try to improve the architecture. They:

embody the transformation of a representation in one class while allowing serialization and deserialization.

allow compositions of representations. Order has_many items.

make hypermedia support easy (we will learn more about that in the next post)

are inheritable and extendable and bring back OOP. For instance, you might write convenience accessors into your representer class.

are usable in REST clients as well, which allows decoupled, solid systems (see next post).

make testing easier.

Source