Posts Tagged representation

Ruby On REST 3: One Model, Multiple Representations!

Monday, January 9th, 2012

Happy New Year to all my fellow readers! I hope you had a good party!

In this post I wanna clarify why representers in Roar are much more versatile than one might think at first sight.

Model != Resource

I know, Rails teaches us that every resource is a model with exactly one representation. This is wrong. Not only can a resource be just anything, ranging from simple 1-to-1 mappings of database rows to virtual models like a workflow but also can your business models be represented in completely different contexts.

Let’s get back to our fruity example to learn more about this.

orange = Fruit.find_by_title("Orange")
orange.extend(FruitRepresenter).to_json
#=> {"title":"Orange","colors":["orange"]}

In the last discussion we had a Fruit and a Bowl model which were represented in their respective resources. While the fruit just contains some properties the bowl was a composition thereof. This is pretty “rails-conform” (I use this word with a negative undertone) and can simply be abstracted to hundreds of projects where people expose users, blog posts or events in their resources. This is what we call a 1-to-1 mapping of model to resource.

Let’s have some Booze!

We wrote a simple FruitRepresenter for mapping apples, lemons or oranges to documents, and we had the BowlRepresenter to provide documents for fruit collections. That was nice. But you can do more with fruits. Why not open a distillery and make some good booze from pears or grapes?

What that means for our REST API is that we have to represent fruits as ingredients. We also want to document what fruits are contained in a bottle of schnaps.

module IngredientRepresenter
  include Roar::Representer::JSON
 
  property :title
  collection :tastes_like
end

The new IngredientRepresenter looks a bit similar to our FruitRepresenter, however, it represents a fruit in another context.

pear = Fruit.find_by_title("Pear")
pear.extend(IngredientRepresenter).to_json
#=> undefined method `tastes_like'

An exception. While the pear exposes a #title method it does not have a #tastes_like method. Why did I do that? I wanted to emphasize that the representer doesn’t guess anything from the represented object (the pear). It is up to the fruit to provide decent accessors like the #tastes_like reader. No magic or implicit semantics in Roar. And this is a good thing.

Another Context, Another Module.

We could easily push the missing methods into the IngredientRepresenter itself, but we will learn in an upcoming post that it is better to provide additional accessors in a separate module.

module Ingredient
  def tastes_like
    TastesLike.query(title) # returns array.
  end
 
  def tastes_like=(*)
  end
end

In the reader, we query the world-famous TastesLikeā„¢ web service. Since we don’t need a writer, the second method ain’t doing nothing.

pear.extend(Ingredient, IngredientRepresenter).to_json
#=> {"title":"Pear","tastes_like":["pear","mango"]}

Cool, this is a fruit represented in a completely different context. What about the parsing, does that work, too?

apple = Fruit.new
apple.extend(Ingredient, IngredientRepresenter)
apple.from_json("{\"title\":\"Apple\",
  \"tastes_like\":[\"Pear\",\"Apple\",\"Mango\"]}")
#=> #<Fruit title="Apple", colors=[]>

Yepp, parsing an ingredient and mapping it to a fruit also works fine.

Fruits in a Bottle.

Let’s see how collections can be represented in different contexts.

module BottleRepresenter
  include Roar::Representer::JSON
 
  collection :fruits, :class => Fruit, 
    :extend => [Ingredient, IngredientRepresenter]
end

The bottle representer knows that it’s composed of Fruit instances. It does use the IngredientRepresenter for parsing and rendering, though. BTW, if you don’t like the long :extend assignment you are free to “hard-include” the modules directly in your models.

Summary

So what we’re doing here is having one model used in two completely different contexts: the conventional “fruit” context and the “ingredient” context. Imagine you’d be doing this with #as_json and different hash-parsing algorithms in your controllers – good luck.

Ruby On REST 2: Representers and the DCI Pattern

Friday, December 16th, 2011

We talked about Representers earlier last week. Today, I wanna describe DCI and how it fits perfectly together with representers in Roar.

What is DCI?

For years we’ve been taught that “Fat Models, Skinny Controllers” is the way to organize code. Push all your domain code into your ActiveRecord classes. Many people, including me, agree that this is bullshit. This leads to bloated classes, usually combined with one or two object instances taking care of an entire workflow (aka request).

DCI is an architectural pattern that makes the model “fat-on-demand”: Data objects are enhanced with roles at runtime, but only when needed.

The DCI pattern consists of three concepts.

  1. Data objects are supposed to contain application data. Nothing else. This would be your ActiveRecord row instance, but without any predefined behaviour. Just data.
  2. Context objects define what is done in the current workflow. In Rails, a controller running an action could be a context object.
  3. Roles are behaviour for data objects in a particular context. In Ruby, a Rails controller could mixin a module (a “role”) into an empty model row and thus let it process something.

Hooray for DCI – But Why?

Now the thing is, I absolutely hate to hype something (except stuff I did, of course). However, DCI makes code better maintainable, easier to follow and way easier to test. Period.

BTW, did you already check out my new Roar gem? It’s hip!

Think of a classical model found in many Rails applications with maybe one hundred methods. Or fifty. Fifty methods to interfere with each other. In a DCI setup, an object has a limited behaviour scope. The limited knowledge makes it easier to debug and predictable.

One Model, Multiple Faces

Since representers are modules they fit perfectly into the DCI approach, making them roles. Let’s consider the fruit representer from last week, again.

require 'roar/representer/json'
 
module FruitRepresenter
  include Roar::Representer::JSON
 
  property :title
  collection :colors
end

You still can include this representer into its “host” class directly.

class Fruit
  include Roar::Representer
  include FruitRepresenter
end

The problem now is that the Fruit class is limited to one representation, only. Forever and a day you may only render one particular representation. The key about representations, however, is that one model may be represented by multiple, yeah, representations.

Now, back to the start, let’s assume the Fruit class is some empty ActiveRecord class without any prior knowledge of representations at all.

class Fruit < ActiveRecord::Base
end
 
lemon = Fruit.from_json("{\"title\":\"Lemon\"}")
#=> undefined method `from_json' for Fruit:Class

Yeah, there is no #from_json class method.

Representers On DCI

In order to use the DCI approach, we first need an instance. Here’s how the lemon instance, and only this instance, can be extended with the fruit representer.

lemon = Fruit.new.extend(FruitRepresenter)
lemon.from_json("{\"title\":\"Lemon\"}")
lemon.title #=> "Lemon"

Awesome! The built-in Ruby #extend method makes the lemon instance aware of its representation and mixes in #from_json and friends.

Naturally, this works in both ways.

orange = Fruit.find_by_title("Orange")
orange.extend(FruitRepresenter).to_json
#=> {"title":"Apple","colors":["green"]}

The mixed-in representer also lets us render the JSON document.

Gimme Complexity!

On a flat level with single objects, this works out-of-the-box with recent Roar/representable versions. Problems arise when we have nested setups.

module BowlRepresenter
  include Roar::Representer::JSON
  include Roar::Representer::Feature::Hypermedia
 
  property :location
  collection :items, :as => Fruit,
    :extend => FruitRepresenter
 
  link :self do
    "http://bowl/#{location}"
  end
end

Using the :extend option we can give representable a hint about the representer to use when rendering/parsing contained objects.

And, hey, this works.

empty_bowl = Bowl.new
empty_bowl.extend(BowlRepresenter)
emtpy_bowl.from_json("{\"location\":\"kitchen\", \
  \"items\":[{\"title\":\"Lemon\",\"colors\":[]}]}")
 
puts empty_bowl.items.first.title #=> "Lemon"

Discussion Needed!

Having representers on an object level makes the whole thing really clean and versatile. You can test document rendering and parsing without polluting classes. And you can have a FruitRepresenter, a RottenVegetableRepresenter and what else you like for your orange instance. BTW, oranges ain’t no vegetables.

We still discuss about the API and need your opinion. Right now, we got this

collection :items, :as => Fruit,
    :extend => FruitRepresenter

Here, :as describes the class that encapsulates the contained data. The :extend option helps representable to find the correct module for the nested item. Now, what about this?

collection :items, :class => Fruit,
    :module => FruitRepresenter

Does that make sense? These options really just describe what’s on the right side of the hash rocket. Thanks to Scott for discussion here. Now, tell us your story!

Representable gem released: Improve your REST representation code!

Monday, May 9th, 2011

The 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.