The tiny representable gem got some improvements recently. Not only did we refactor the internals to make it easier adding new representation engines, we also used that to give you a new format: YAML!

YAML, Who Needs YAML?

I use YAML very rarely. But sometimes clients happen to require it, or an API might be based on this format. Let’s make the world a better place and support it in representable!

Consider the following domain class.

require 'ostruct'
class Song < OpenStruct
end
 
song = Song.new(:title => "Scarified", :track => 4)

To render this into YAML a simple representer is needed.

module SongRepresenter
  include Representable::YAML
 
  property :title
  property :track
end

Note the usage of the Representable::YAML module. After injecting, the domain object is fully equipped with #to_yaml and #from_yaml.

puts song.extend(SongRepresenter).to_yaml
 
# => "
---
title: Scarified
track: 4"

As always, this works also with parsing.

song = Song.new.extend(SongRepresenter)
song.from_yaml("---
title: Jackhammer
track: 5
")
 
puts song.inspect
 
#=> #<Song title="Jackhammer", track=5>

Nested Models? Not A Big Deal.

Often you may want to represent nested setups – where models contain models. This has always been a key feature in representable.

class Album < OpenStruct
end
 
module AlbumRepresenter
  include Representable::YAML
 
  property :name
  collection :songs, :extend => SongRepresenter, 
                      :class  => Song
end

Since an Album contains a list of Songs we wanna represent those embedded objects with the SongRepresenter we already wrote.

Seting up a demo album, first.

album = Album.new(:songs => [
  Song.new(:title => "Scarified",  :track => 4),
  Song.new(:title => "Jackhammer", :track => 5)
])

The YAML representer will now create a proper YAML document for the nested objects.

album.extend(AlbumRepresenter).to_yaml
#=>
"---
songs:
- title: Scarified
  track: 4
- title: Jackhammer
  track: 5"

See how easy that is?

And Finally: Flow-Style Lists.

Another cool feature is the ability to declare collections as flow-style which is an official part of the YAML specification but only very poorly implemented in available YAML gems.

Usually, lists are rendered in the block style.

class HotBands < OpenStruct
end
 
module HotBandsRepresenter
  include Representable::YAML
 
  property :for
  collection :names
end

After creating and rendering one of those HotBands, the names list is rendered in block style – the default in all YAML gems.

HotBands.new(:for => "Nick", :names => [
  "Bad Religion", 
  "Van Halen", 
  "Mozart"
]).extend(HotBandsRepresenter).to_yaml
 
#=> 
"---
for: Nick
names:
- Bad Religion
- Van Halen
- Mozart"

Now, if you don’t like that style where one list item is displayed per line, you might want to switch to the oh-so-flowing flow style.

module HotBandsRepresenter
  include Representable::YAML
 
  property :for
  collection :names, :style => :flow
end

By using the :style => :flow option in the collection you can achieve just that!

hotties.to_yaml
#=> 
"---
for: Nick
names: [Bad Religion, Van Halen, Mozart]"

Check how it flows. That is just awesome.

Introducing The Hash Representer.

We did a little bit of internal refactorings cleaning up representable. What came out is the new hash representer which gives basic functionality to most of the other representer engines. It can also be helpful if you want to parse incoming form data or use the params hash from Rails or Sinatra to fill your objects.

Consider this representer.

require 'representable/hash'
module BandRepresenter
  include Representable::Hash
 
  property :name
  property :label
end

This representer can read and write plain Ruby hashes. Sounds stupid? It is! However, what if you wanna use Rails’ params hash to update a model?

params = {"band" => 
  {"name" => "Paul Gilbert", "label" => "n/a"}}

This could be a form Rails (or Sinatra) automatically parsed for you. Now let’s use that on a new Band instance.

band = Band.new.extend(BandRepresenter).
  from_hash(params["band"])

Note that this could be an existing Band instance, too, as from Band.find(params[:id]).

Inspecting the result of our “parsing” we see that it actually works.

band.inspect
#=> #<Band name="Paul Gilbert", label="n/a">

This new hash representer is especially helpful for using representers with forms.

One Module, Multiple Representers

Another nice little cleanup lets you include different representer engines into a single module, so you can transform to and from different media types.

module BandRepresenter
  include Representable::JSON
  include Representable::XML

This would give your objects the power of both XML and JSON.

More To Come.

In the next release we’re planning readable/writeable policy support, polymorphic collections, letting you pass arguments to the represented accessors if you need that and more stuff I already forgot. Let us know if you need anything more, and ┬íSalud!

Leave a Reply