tags:

views:

96

answers:

3

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.

+1  A: 

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();
Evan Carroll
I've edited my question to make my question clearer. As far as I can tell, MooxeX::Traits requires the type to be known at object instantiation. I want the object to determine its type and apply the required role, rather than requiring it to be part of the API.
Oesor
+1  A: 

After the question update I'll give it another crack: with() is just a function call.

package Class;
use Moose;

BEGIN {
  with ( map "MyObject::$_", qw/TypeA TypeB/ );
}

Also, you can reference the current package with the perl constant __PACKAGE__.

Evan Carroll
+5  A: 

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');
perigrin
No, I hadn't. Was more of a best practices question making sure I'd understood the ->meta documentation correctly and that I wasn't going down the wrong path.
Oesor
Moose, and Perl in general is very good about being an experimental language. If you have a question, try it and see what the results are. If you don't understand the results *then* ask … you'll save yourself a lot of time waiting on someone else to confirm/deny your hypothesis.
perigrin