On Thu, Oct 08, 2009 at 07:26:40AM -0700, Ovid wrote:
> Hi all,
> 
> This problem is annoying and I'm not sure of the best approach.
> 
> Let's say I have a list of roles and an object and I want to serialize the 
> object as XML.  Imagine the following (this is my actual use case):
> 
>   sub to_xml {
>       my $self = shift;
>       my $xml = ''; # string XML sucks. Just an example :)
>       foreach my $role ($self->meta->roles) { # or whatever it's called
>         next unless $role->meta->does('DoesXML');
>         $xml .= $role->as_xml($self); # method available on the role itself
>       }
>       return $xml;
>   }

I think a lot of this issue comes from issues with the actual design
here. When you compose a role into a class, it's an actual part of the
class, it doesn't really make sense to need to serialize it separately.
If it does, then you really don't want to be using roles, you want to be
using an attribute on your class holding an object of another class, and
delegating methods to that attribute. That way, the delegated-to class
can know how to serialize itself, and this issue just goes away.

One thing I have noticed in general from reading your blog posts and
such (and I'm not trying to be snarky or insulting or anything) is that
you seem to be caught in the mindset of "not inheritance, therefore
roles", when there are more options available than just those two.
nothingmuch makes this case better than I do in his latest blog post(:

> In other words, tying methods and roles specifically to roles and their state 
> might be useful.  Here's a better(?) example, also pseudo-code:
> 
>   package Thing;
>   use Moose;
>   with qw(
>     DoesRobot
>     DoesDrawable
>   );
>  
> Now imagine that both the robot and drawable roles provide a draw() method, 
> but the robot on tries to control a robotic arm with a pencil.  Canonically, 
> you do something like this:
> 
>   package Thing;
>   use Moose;
>   with (
>     DoesRobot => { excludes => 'draw', aliases => { draw => 'draw_with_arm' },
>     'DoesDrawable'
>   );
> 
> Now if another class tries to instantiate Thing->new, it doesn't really know 
> what the draw() method has been renamed to for the Robot role.  You can get 
> to it through reflection (I think), but that's annoying.  What's the easiest 
> way to *not* worry about this method being renamed?  Is it something like 
> this awful pseudo-code?
> 
>   $thing->meta->does_role('DoesRobot')->get_method('draw')->($thing);
> 
> Does that make sense?

Again, if the robot was a delegate rather than a role, you'd have
$thing->draw_with_arm delegated to $thing->robot_arm->draw, but
$thing->robot_arm->draw would still be available to be called
separately:

  package Thing;
  use Moose;
  with 'DoesDrawable';

  has robot_arm => (
      is      => 'ro',
      isa     => 'RobotArm',
      default => { RobotArm->new },
      handles => { draw_with_arm => 'draw' },
  );

Ending up in this situation in general is something of a code smell...
if you aren't using excludes/alias to just provide an implementation of
the method that makes more sense for the *class*, you probably want to
think about if there is another way to be doing what you're doing.
Thinking of roles as separate objects really just confuses things. (Not
that I'm saying that's how you're thinking about things, but that's how
it comes across from your code.)

Anyway, sorry I wasn't more helpful with your actual problems, but
hopefully there's at least some stuff to think about here(:

-doy

Reply via email to