delegating a property to a private member of the represented object

96 views
Skip to first unread message

JP Slavinsky

unread,
Dec 11, 2013, 7:45:20 PM12/11/13
to roar...@googlegroups.com
Howdy - 

I've come across a situation where I have (decorator) Representers with properties that need to get their values not directly from the represented object but instead from a ("private") object contained in that represented object.

In my rails app I have some models that extend a base class, but instead of using single table inheritance, I'm having the base class polymorphically belong to instances of the subclasses (I didn't want to have all the disparate data from the subclasses in one giant table).

Let me set the stage using a Zoo example (not my real application of course :-)

class Animal < ActiveRecord::Base
  belongs_to :species, polymorphic: true
  # has fields like :weight
end

class Monkey < ActiveRecord::Base
  has_one :animal, as: :species
  # has unique fields like :iq, :banana_preference, :favorite_cymbals, etc
end

class Dolphin < ActiveRecord::Base
  has_one :animal, as: species
  # has unique fields like :fin_height, :fish_preference, etc
end

I consider the way I implement inheritance to be a internal thing for my code, and it isn't something that I want to expose in my API.  In my API, I want to have JSON schemas (and representers) for Monkeys and Dolphins.

Right now I have:

class AnimalRepresenter < Roar::Decorator
  include Roar::Representer::JSON
  property :weight
end

class MonkeyRepresenter < AnimalRepresenter
  property :iq
  property :banana_preference
  ...
end

class DolphinRepresenter < AnimalRepresenter
  property :fin_height
  property :fish_preference
  ...
end

In my roar/representer code, I'm actually spitting these out inside a ZooRepresenter:

class ZooRepresenter < Roar::Decorator
  include Roar::Representer::JSON
  collection :animals,
                 class: lambda { # chooses Monkey or Dolphin },
                 decorator: lambda { # chooses MonkeyRepresenter or DolphinRepresenter },
                 ... 
end

Of course, my representers as written don't work: the animals collection in the ZooRepresenter gets a list of Animals, and the MonkeyRepresenter and DolphinRepresenter fail because animal objects don't have "iq" and "fin_height" properties.

Per the documentation, the way to go looks to be to add custom getters and setters for the monkey and dolphin representer properties, e.g.

class MonkeyRepresenter < AnimalRepresenter
  property :iq, decorator_scope: true
   def iq
    represented.species.iq
  end
  def iq=(value)
    represented.species.iq=value
  end 
 
  ...
end

This works, it is just a lot of repeated typing.  So the question is, have Nick or other Roar/Representer gurus ever considerd adding a "delegate_to_someone_else" option for the property definition?  So that I could instead say:

class MonkeyRepresenter < AnimalRepresenter
  property :iq, decorator_scope: true, delegate_to: :species

Or is there even a better way of doing what I want?

Still loving these tools -- thanks guys.

JP

Nick Sutterer

unread,
Dec 11, 2013, 8:05:36 PM12/11/13
to roar...@googlegroups.com
Awesome JP, you work in a zoo? Amazing!

I would strongly recommend to add the delegation to the decorator for now (as you do it).

You wanna have something like

class MonkeyRepresenter < AnimalRepresenter
   delegate :iq, to: "represented.species"

Of course, not like that (hate the string) but somehow like that.

Again, representers are stupid per design. Ideally, you'd pass in a decorated object that already provides all those accessors to the representer. However, we had the same "problem" in reform and what I did is I introduced the Composition module that pretty much does the decoration for you. We could do the same for representable - let me think about it for a minute.


--
You received this message because you are subscribed to the Google Groups "Roar" group.
To unsubscribe from this group and stop receiving emails from it, send an email to roar-talk+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Nick Sutterer

unread,
Dec 11, 2013, 8:09:20 PM12/11/13
to roar...@googlegroups.com
Actually, both reform and roar sit on representable and could benefit from Composition and Delegator modules :)

JP Slavinsky

unread,
Dec 11, 2013, 8:26:44 PM12/11/13
to roar...@googlegroups.com
There have been times when I have definitely felt like I was working in a zoo :-)

You received this message because you are subscribed to a topic in the Google Groups "Roar" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/roar-talk/7FtyTW8VimQ/unsubscribe.
To unsubscribe from this group and all its topics, send an email to roar-talk+...@googlegroups.com.

JP Slavinsky

unread,
Dec 11, 2013, 9:24:17 PM12/11/13
to roar...@googlegroups.com
FWIW, for the time being I'll probably use inline getter's and setter's instead of defining a bunch of helper methods, e.g.:

property :id, getter: lambda { |*| species.iq }, setter: lambda { |v| species.iq = v }

Fairly short, all in all.
Reply all
Reply to author
Forward
0 new messages