Representable gem released: Improve your REST representation code!
Monday, May 9th, 2011The representable gem helps to keep parsing and rendering representations in one place. Once defined, it gives your model #to_xml, #from_xml and more. The initial version was released today, let’s see how it helps cleaning up your representation code.
Let’s write a Blog app!
Since your imaginery blog app has a REST API you want to provide a post resource serving and consuming article representations. Here’s a sample XML representation of an arbitrary blog post.
<post> <id>1</id> <posted_at>2011-05-09</posted_at> <comment id="1"> <text>This is stupid!</text> </comment> <comment id="3"> <text>No, awesome post!</text> </comment> </post>
Basically, a post representation contains comments in addition to some meta data.
The Problem: Two places need to know about your Representation.
In, say, a Rails app, you’d have to tweak the models’ #to_xml method in order to render an XML representation. Alternatively, you’d use a Builder template.
If, and only if, you stick to the Rails conventions, #from_xml will work out-of-the-box. Otherwise, you have to write a manual parser as discussed earlier. Note that the <comment> tag contains an attribute not compliant with Rails.
You usually end up with some model code, maybe even a template and a hand-made parser. This distributes representation knowledge over the entire MVC framework. While this won’t kill your mother it still makes things more work to manage.
Defining a Representation
The representable gem is designed to prevent you from doing that. You simply define the representation in one place and it does the REST (I still love the word-play).
1 2 3 4 5 6 7 8 | require 'representable/xml' class Comment # < ... include Representable::XML representable_property :text representable_property :id, :from => "@id" end |
The Representable::XML module allows to define plain properties (line 6) and attributes (line 7).
Nesting Representations
The post representation uses the compositing feature in representable in order to embed comments.
1 2 3 4 5 6 7 8 | class Post # < ... include Representable::XML representable_property :id representable_property :posted_at representable_collection :comments, :tag => :comment, :as => Comment end |
Wow, you can define nested compositions with representable (line 6). This is easy, so how do we work with it?
Rendering Documents
You want to render a document representing your post now.
Post.find(1).to_xml # => "<post><id>1</id>comment id=\"1\"..."
Representable takes care of rendering. No magic included.
Parsing Representations
What if you allow people to POST an XML document to the blog post resource?
@post = Post.from_xml(request.body) @post.comments # => "[#<Comment:0x8545a08 @text=\"Your blog sucks.\">]"
Again, representable deserializes the incoming representation and creates the respective objects for you. Representable doesn’t know anything about databases or your underlying data layout, though. It just creates fresh instances for compositions making it your job to process it.
Other media types?
Representable comes with XML and JSON support, you just have to mix in the respective module.
@comment.to_json # => "\"{text => ..."
How does it help?
Representable is a completely abstract module for rendering and parsing representations and mapping document fragments to object attributes. Nothing more.
It makes working with representations object-oriented where you had view templates and parsers before. While representable makes that workflow as simple as possible it also allows distributing your representation code, since it can be pushed into Ruby modules. Maybe it makes sense to use it on both client and server, in Rails and Sinatra?
Representable is also used in Roar to make life easier when working with REST representations and embedding or consuming hypermedia. We will discuss how representations and HATEOAS work in the next post. Cheers.
