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