Time to catch up with some improvements in the representable gem and it’s bigger brother roar. Most of the work was done on the sunny island Fuerteventura in a surf camp *grin.

Readable And Writeable Properties.

To make it a little bit easier to provide assignment security we added :readable and :writeable switches to #property. Both are true per default.

I’ll use a song to demonstrate.

song = Song.new(:title => "You're Wrong", :track => 4)

For simplicity I’ll use the Hash representer. However, the following code works just fine with JSON, XML, and YAML.

module SongRepresenter
  include Representable::Hash
 
  property :title, :readable => false
  property :track
end

Since the title property is not readable – from an outer perspective – it is not serialized anymore.

song.extend(SongRepresenter).to_hash
#=> {"track"=>4}

Vice-versa, properties can be read-only, too.

module SongRepresenter
  include Representable::Hash
 
  property :title, :writeable => false
  property :track
end

The title property is now ignored when consuming (or parsing) a document as it is not writeable.

song = Song.new.extend(SongRepresenter)
song.from_hash({:title => "Fallout", :track => 1})
puts song #=> #<Song @track=1>

Those two switches allow a declarative access control for properties without having to override accessors. BTW, they also replace the mass-assignment problem and its poor successor strong_parameters in Rails.

Polymorphic Extend.

Finally, something fuckin’ awesome. Until today, representable allowed you to specify the representer modules for parsing and rendering using the :extend option.

  property :song, :extend => SongRepresenter

To make this dynamic, both :class and :extend now also accept a lambda to compute the class or module at runtime.

Let’s assume we not only have original songs but also cover songs.

class CoverSong < Song
end

Now we set up a composition where the Album instance contains one Song and one CoverSong instance.

songs = [
 Song.new(title: "Weirdo", track: 5),
 CoverSong.new(title: "Truth Hits Everybody", track: 6, 
    copyright: "The Police")
]
album = Album.new(name: "Incognito", songs: songs)

Note that the covered song also points to the amazing band that originally wrote this song.

Two simple representers are needed to properly represent those songs.

module SongRepresenter
  include Representable::Hash
 
  property :title
  property :track
end
 
module CoverSongRepresenter
  include Representable::Hash
  include SongRepresenter
 
  property :copyright
end

I am using inheritance here – yay!

The album representer needs to distinguish between the different song types. That is why I use a dynamic :extend here.

module AlbumRepresenter
  include Representable::Hash
 
  property :name
  collection :songs, :extend => lambda { |song| 
    song.is_a?(CoverSong) ? 
      CoverSongRepresenter : SongRepresenter }
end

Check out what we get when calling the rendering.

album.extend(AlbumRepresenter).to_hash
#=> {"name"=>"INCOGNITO", "songs"=>[
 {"title"=>"Weirdo", "track"=>5}, 
 {"title"=>"Truth Hits Everybody", "track"=>6, 
  "copyright"=>"The Police"}]}

Representable uses the respective representer module depending on the type of the object passed in. It is important to understand that :extend works both ways and always receives a valid object into the lambda block.

  • When rendering, the object bound to the property (or collection) is extended using the provided module.
  • However, when parsing, representable uses the class from :class, instantiates a brand-new object, passes this into the block and then extends it.

Speaking about :class brings us to the next feature!

Dynamic Class Block For Parsing.

Now what if you need to compute the object’s class at runtime when parsing? Well, use a lambda!

module AlbumRepresenter
 include Representable::Hash
 
 property :name
 collection :songs, 
  :extend => lambda{ |song| ...,
  :class  => lambda{ |hsh| hsh.has_key?("copyright") ?
    CoverSong : Song }
end

The dynamic :class lambda allows you to decide the class right when you’re parsing the document. If you wonder how the hsh argument looks like, this would be something like

hsh #=> {"title"=>"Weirdo", "track"=>5}

Never ever complain about missing polymorphism in representable any more! Ha!

MORE! Dynamic Object Creation For Parsing!

No, I’m not finished, yet! When working on roar and its HAL feature we encountered the problem that we needed to override the object creation when parsing – in an easy way. And here comes :instance.

1
2
3
4
5
6
7
8
9
10
11
12
module AlbumRepresenter
  include Representable::Hash
 
  property :name
  collection :songs, 
    :extend   => lambda { |song| ... },
    :instance => lambda do |hsh| 
      hsh.has_key?("copyright") ? 
        CoverSong.new : 
        Song.new(original: true)
    end
end

Instead of providing a class with the :class option, I create the song instances myself using :instance. The returned value from the block will be extended and passed into the deserialize workflow where representable will call something like from_hash on it.

Note how I assign additional data for the Song object which will survive the parsing (line 10).

Isn’t that fantastic? The dynamic lambdas for :extend, :class and :instance allow you to have simple polymorphic representations without the pain of overriding representable’s internals.

BTW: all lambda blocks are executed in the represented instance context – which is the Song object here.

Last But Not Least: HAL Link Arrays In Roar.

This might sound funny, but we wrote all that in order to provide a 100% complete implementation of the HAL/JSON standard, a cool universal media format for RESTful APIs.

What was missing was support for arrays of links in HAL. Here’s an example of what people wanted.

{"_links":{
 "next":{"href":"http://next"}, 
 "self":[{"lang":"en","href":"http://en.hit"},
         {"lang":"de","href":"http://de.hit"}]
}}

For some strange reasons I still don’t understand why the HAL standard allows having “single links” (no girlfriend) and arrays of links. I am sure Mike Kelly himself will soon explain why we need this – however, here is how roar handles this since 0.11.5.

module SongRepresenter
  include Roar::Representer::JSON::HAL
 
  link :next do
    "http://next"
  end
 
  links :self do
    [{:lang => "en", :href => "http://en.hit"}, 
     {:lang => "de", :href => "http://de.hit"}]
  end
end

You can see, we still handle “single links” with the ::link class method. Arrays of links can be specified using the new ::links method which receives the rel attribute and a block. The block should return an array of link attribute hashes.

As always, that works for rendering and parsing. Be careful when you work with a link array.

song.from_json(..)
song.links[:next] #=> #<Hyperlink>
song.links[:self] #=> [#<Hyperlink>, #<Hyperlink>]

Links defined with ::links will naturally contain an array of Hyperlink instances after parsing. Does that help you, guys?

More To Come.

That was quite some work. And there’s more coming. Let me know if anything could be improved or added. But now it’s time for a surf. Keep it real!

One Response to “Released Roar 0.11.5 With HAL Link Arrays and Representable 1.2.9 With Polymorphism Support!”

  1. Very good source code with OOP concept and worked with JSON, XML, and YAML.

Leave a Reply