Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I compose an existing Moose role into a class at runtime?

Tags:

roles

perl

moose

Say I define an abstract My::Object and concrete role implementations My::Object::TypeA and My::Object::TypeB. For maintainability reasons, I'd like to not have a hardcoded table that looks at the object type and applies roles. As a DWIMmy example, I'm looking for something along these lines in My::Object:

has => 'id' (isa => 'Str', required => 1);

sub BUILD {
  my $self = shift;

  my $type = $self->lookup_type(); ## Returns 'TypeB'
  {"My::Object::$type"}->meta->apply($self);
}

Letting me get a My::Object with My::Object::TypeB role applied by doing the following:

my $obj = My::Object(id = 'foo')

Is this going to do what I want or am I on the entirely wrong track?

Edit: I simplified this too much; I don't want to have to know the type when I instantiate the object, I want the object to determine its type and apply the correct role's methods appropriately. I've edited my question to make this clearer.

like image 646
Oesor Avatar asked Jun 08 '10 19:06

Oesor


2 Answers

Have you tried it?

$perl -Moose -E'
     sub BUILD { my ($self, $p) = @_; my $role = qq[Class::$$p{role}]; $role->meta->apply($self) };
     package Class::A; use Moose::Role; has a => (is => q[ro], default => 1);
     package main; say Class->new(role => q[A])->dump'

Yields:

$VAR1 = bless( {
             'a' => 1
           }, 'Class::MOP::Class::__ANON__::SERIAL::1' );

This appears to be what you want. Cleaned up the code in the oose.pm call is:

package Class; 
use Moose;
sub BUILD { 
    my ($self, $p) = @_;
    my $role = qq[Class::$$p{role}];
    $role->meta->apply($self);
}

package Class::A;
use Moose::Role;

has a => ( is => 'ro', default => 1 );

package main;
Class->new(role => 'A');
like image 93
perigrin Avatar answered Nov 05 '22 17:11

perigrin


Use MooseX::Traits, it will turn your role into a trait that can be applied at runtime, then you just call:

   # Where Class is a class that has `use MooseX::Traits`;
   # And TypeB is a simple role
   Class->new_with_traits( traits => [qw/TypeB/] )

   # or even the new, and now preferred method.
   Class->with_traits('TypeB')->new();
like image 4
NO WAR WITH RUSSIA Avatar answered Nov 05 '22 18:11

NO WAR WITH RUSSIA