Representable 2.0 With Better Inheritance, Filters And Automatic Collections!

Representable 2.0 comes with a bunch of cool new features. I’d like to thank the community for coming up with ideas, working with me on features and trying out new stuff. Speaking of new stuff – have you seen my favicon? It’s new, and I’m a bit proud of it.

Let’s quickly go through the changes.

Changes?

Well, if you’ve been a cautious person and were only using representable’s public API, nothing will break! Even when you’re one of the “power users”, you shouldn’t have too much trouble getting up-to-date, again.

Partial Inheritance.

Inheriting properties from other representer has been working fine for a long time, either by including modules into other modules or decorator classes, or by deriving decorators.

It is now possible to partially override inherited properties.

Suppose we have the following base representer (this could be a module or a class, doesn’t matter). For simplicity, I leave away the noisy includes.

module SongRepresenter
  property :title, as: :Title

  property :band do
    property :name
  end
end

Very simple. Now imagine a HitRepresenter needed to inherit all the above plus add a property to the inline band block. This is where :inherit enters the stage.

module HitRepresenter
  include SongRepresenter

  property :band, inherit: true do
    property :location
  end

This will add the location property to the band’s representer without destroying the original representer.

You can also use this directive with scalar properties that don’t have a block. This will add the options you provide.

property :title, inherit: true, type: String

The coercion option :type will be added to the existing options, preserving :as and whatever else you specified.

Having this new inheritance “fine tuning” makes representable really powerful and maximises reusability of representers across your project.

Modules In Decorators.

One thing I found not working and fixed in representable 2.0 was when including modules with inline representers into decorators – this works all fine now.

class SongDecorator
  include SongRepresenter

  property :label
end

This will work as expected and inherit all properties from SongRepresenter into the decorator – and add the label.

Automatic Collections.

Many users have been asking for this for a long time, and @timoschilling was the power user who submitted a draft more than a year ago! Sorry for the delay. I was busy drinking Australian Beer™.

Representable comes with a feature called lonely collection which allows you to represent an array of objects.

module SongsRepresenter
  include Representable::JSON::Collection

  items extend: SongRepresenter, class: Song
end

As you can see, every item will be represented (rendered or parsed) with the specified SongRepresenter (which could also be inline, BTW).

[song, song].extend(SongsRepresenter).to_json

Now, this is explicit and needs to be defined for every collection. I personally am a big fan of explicit code, but lots of users don’t want that boilerplate code.

That’s why you can now use ::for_collection to let representable create this lovely lonely collection for you.

module SongRepresenter
  property :title
end

Assuming you use this simple singular representer to render and parse songs, here’s how you’d render collections with it.

[song, song].extend(SongRepresenter.for_collection).
  to_json

Note how I use the singular representer! Representable will internally create a lonely collection representer and render the collection.

For parsing, it needs to know a bit know, e.g. the class to deserialize the songs to. You need to specify that in the singular representer, again.

module SongRepresenter
  property :title

  collection_representer class: Song
end

::collection_representer lets you configure the – surprise! – collection representer. Everything you pass to this method will be forwarded to the ::items call when creating the collection representer.

[].extend(SongRepresenter.for_collection).
  from_json("[{title: ..]")

Automaticer Collections.

If that’s still too much code for you, go ::represent fo’ real!!

SongRepresenter.represent(song).to_json       #=> 1
SongRepresenter.represent([song, ..]).to_json #=> n

This method figures out if it’s working with an array or a singular object. It’s a matter of days until this will make its way into Roar and roar-rails and simplify your user code there.

Filters!

If you ever needed to massage a string before it gets rendered, or a fragment after it got parsed… you’re lucky. Representable now gives you :render_filter and :parse_filter.

property :title, render_filter: 
  lambda { |value, doc, *args| value.markdown }

This is run right before the fragment is inserted into the document.

property :title, parse_filter: 
  lambda { |value, doc, *args| Markdown.parse(value) }

And this lambda is invoked just after the fragment got picked from the incoming document when parsing.

In case you need multiple filters: those two options are implemented using Representable::Pipeline, you can add lambdas later, the results will be passed on from filter to filter.

Apparently, this feature already made its way into grape-roar.

Ditch Lambdas For Callable.

I always disliked the verbose, clumsy lambda syntax in my properties. Representable 2.0 lets you specify “callable” objects in favour of lambdas.

property :title, render_filter: Sanitizer.new

Now, how does it know that Sanitizer instance is invokable? Here’s the trick.

class Sanitizer
  include Uber::Callable

  def call(representer, fragment, doc, *args)
    fragment.sanitize
  end
end

All the object needs is a #call method that gets – admittedly – a bunch of parameters. In that, you can do whatever you need. Including Uber::Callable marks this instance as a callable one – no magic here.

I Need More Data!

Another new option helps you retrieving all the possible information you might need in your lambdas (or, cooler, callable objects). Let’s say you need to know the property options, whether :extend is set, or not. :pass_options can help.

property :title, 
  pass_options: true,
  parse_filter: 
    lambda { |value, doc, options|
      options.binding[:extend]
      options.user_options
      options.represented
    }

The options object keeps references to all stakeholder objects involved at that point of representing. This :pass_options is definitely an advanced option, but some peeps might find it helpful. I use it all across my gems.

Enjoy your day and keep smiling!

Advertisement

One thought on “Representable 2.0 With Better Inheritance, Filters And Automatic Collections!

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 )

Connecting to %s