It has been a while since I last blogged about representable – Ruby’s mapping gem that helps you rendering and parsing representations. To be precise, it has been more than 4 ½ months of reflecting, testing and refactoring, and I am happy to finally announce great new features.

Inline Representers

When nesting representations, you have to tell representable about which nested representer to use.

module AlbumRepresenter
  include Representable::JSON
 
  property :title
  collection :songs, extend: SongRepresenter
end

This happens using the :extend option. While this provides a great modularity for the SongRepresenter, it can feel clumsy when you don’t intend to reuse it anywhere else.

You can now define it inline.

module AlbumRepresenter
  include Representable::JSON
 
  property :title
 
  collection :songs do
    property :name
    property :track
  end
end

Just pass the nested representer in a block.

Note that you still have to supply :class when you use the representer for parsing.

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

And, even better, you can still use :extend with the inline declaration to inherit from a base module.

  property :cover_song, extend: SongRepresenter do
    property :original_composer
  end

This will inherit SongRepresenter’s properties into the inline block.

Inline representers work with both ::property and of course ::collection.

PUT Semantics: Sync Models When Parsing

Representers can also parse documents and create nested objects.

Let’s use the representer we just discussed.

module AlbumRepresenter
  include Representable::JSON
 
  collection :songs, class: Song do
    property :title
  end
end

Now, representable gives us parsing for free, as long as we provide the :class option.

album = Album.new.extend(AlbumRepresenter)
album.from_json('{songs: [{title: "Eruption"}]}')
 
album.songs.first.title #=> "Eruption"

Internally, what happens is that representable will create a Song instance for each element in the collection.

It does the following per parsed song.

Song.new.extend(SongRepresenter).
  from_json('{title: ..}')

What if you wanna update an existing Song instead of creating a new? Representable now comes with :parse_strategy which allows exactly that.

module AlbumRepresenter
  include Representable::JSON
 
  collection :songs, parse_strategy: :sync Song do
    property :title
  end
end

As we provide :sync, representable will no longer create an object but call from_json on the existing item.

album = Album.find(1)
album.songs.first #=> #<Song:0x999 title: "Panama">

Note that the Album instance contains one song already.

album.extend(AlbumRepresenter).
  from_json('{songs: [{title: "Eruption"}]}')
 
album.songs.first #=> #<Song:0x999 title: "Eruption">

What happened is that representable used the existing song instance when parsing, resulting in the song being renamed from “Panama” to “Eruption”. Both great songs.

This behaviour roughly implements PUT semantics in a REST service when updating an existing resource. And it works with properties and collections.

Predictable Coercion

You can use the virtus gem with representable to have coercion when representing objects.

module SongRepresenter
  include Representable::JSON
  include Representable::Coercion
 
  property :title, type: String
  property :track, type: Integer

We used to mix in Virtus directly into the represented object, which gave us virtus’ accessors for free, but that also resulted in unpredictable behaviour due to virtus’ dynamic nature.

Coercion is now handled in a separate object and only happens inside to_/from_ invocations. Also, you have to add accessors to your properties manually.

class Song
  attr_accessor :title, :track
end

This is a bit more work for you but greatly reduces confusion in the representable gem (and virtus) and makes it predictable – which is what a good gem should be.

What Happened On The Inside?

The Binding class got way to big and static, I had to copy+paste code to make those features work, so I extracted ObjectDeserializer and its brother ObjectSerializer, and some more classes.

Also, a lot of methods from the Representable module itself got moved into a separate Mapper class.

This makes the entire architecture a lot more cleaner, simpler to follow through and easier to replace parts of it. The refactoring of representable will be a part of my upcoming talk at Rubyshift in the Ukraine this year.

You should come, it’s an awesome conf!

Update!

I totally forgot, so I have to add it now: Representable 1.7 also allows overriding properties in inheriting representers.

module CoverSongRepresenter
  include Representable::JSON
 
  # defines property :title
  include SongRepresenter
 
  # overrides that definition.
  property :title, as: :known_as
end

As you can see, consecutively calling property :title will override the former definition. That’s exactly how “proper” inheritance with methods work.

2 Responses to “Representable 1.7 Is Out With Syncing Support, Inline Representers And More!”

  1. IINM your code example for updating an existing song is slightly off.

    collection :songs, parse_strategy: :sync Song do

    should be

    collection :songs, parse_strategy: :sync, class: Song do

    yes?

    Also, thanks for the post. It just so happens I was looking for a solution like this for an API I am building out, so this release / post is quite timely!


  2. nick

    Soulcutter: No!!!1one The whole point about parse_strategy: :sync is not having to define the class for deserialization as nothing is instantiated! Representable just grabs the existing objects and updates them.

Leave a Reply