Object-to-Object Transformations with Representable

Many people use the Representable gem to render documents from Ruby objects, or to parse incoming documents into an object graph.

This is great for implementing document APIs with JSON or XML. Since Representable does both ways, rendering and deserializing, it gives you a great tool to cover huge parts of your API code.

What many people do not know is that Representable is also useful when transforming objects to other objects. This is particularly marvelous when decorating object graphs or customizing objects.

For example, we do that a lot in Reform, since a form object is mainly data transformations and pushing objects in different representations back and fourth.

Transforming Objects.

In versions before 1.2.6, we used to first transform the source object to a hash, and then apply that hash to the target object.

Consider the following object graph.

source = OpenStruct.new(
  name: "30 Years Live", 
  songs: [
    OpenStruct.new(id: 1, title: "Dear Beloved"), 
    OpenStruct.new(id: 2, title: "Fuck Armageddon")
])

For simplicity, I’m using OpenStructs to implement an album composed of songs. Let’s assume we need to translate this object graph into objects that look like this.

Album = Struct.new(:name, :songs)
Song = Struct.new(:title)

It is needless to say that the target classes could be ActiveRecord derivates or whatever you fancy. Here, Struct will help us to focus on what we need to do: Transform a graph of OpenStructs into Structs.

The Clumsy Way. Oh, and Slow.

In older versions, transformation to a differing object graph worked via an intermediate hash, using a representer.

class AlbumRepresenter < Representable::Decorator
  include Representable::Hash

  property :name
  collection :songs, class: Song do
    property :title
  end
end

Here’s the transformation.

target = Album.new
hash   = AlbumRepresenter.new(source).to_hash
AlbumRepresenter.new(target).from_hash(hash)

This will transform the OpenStruct graph into a tree of an Album instance holding two Song instances. Of course, this transformation doesn’t really make sense, but I hope to proof my point: This is incredible clumsy and slow, as this need two representers.

Representable::Object Helps!

I am surprised that I didn’t come up with that solution earlier, but here’s how it works now.

target = Album.new
AlbumRepresenter.new(target).from_object(source)

Just one call to from_object is required. Speaking of requirements: here’s how the representer changes.

require "representable/object"

class AlbumRepresenter < Representable::Decorator
  include Representable::Object

  # .. the same as above.
end

Note how the Hash engine of Representable got replaced with Object. And now, check out the simple transformation.

When running the representer, the exact same thing as above will happen, resulting in a target object graph as follows.

#<struct Album
 name="30 Years Live",
 songs=
  [#,
   #]>

The representer will simply traverse the source object (the OpenStructs), deserialize necessary composite objects and map (“copy”) properties to the target instance.

This Is Not The End.

My example was simple, probably too simple, but please keep in mind that the transformation can use all kinds of options as :instance, renaming properties using :as and allows an unlimited number of nestings. Also, runtime options like :exclude and friends will work as well.

The new Object representer engine’s a great tool and I started using it heavily in Reform and Disposable as it simplifies the code and speeds up about 20%. If you want to play with it, here’s the above code.
:exclude

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s