-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 =pod Something people often want to do is add methods to a specific instance of a class. For example, if you have a web request, you might want to introspect it and add a "deserialize" method if it's a REST request with serialized data. However, you don't want other instances of the Request class to have that method. In this article, we'll see how such a thing is possible. Let's start with a simple (and contrived) example: lang:Perl package Foo; use Moose; has 'value' => (is => 'ro', isa => 'Any'); 1; You can use the class like this: my $foo = Foo->new( value => 'Hello' ); say $foo->value; # Hello It sure would be nice to say C<< $foo->say >> though. Let's write a role that implements the C method: package Say; use Moose::Role; sub say { say shift->value } 1; Right now, this role doesn't do much: $foo->can('say'); # undef $foo->does('Say'); # undef $foo->say; # dies To make C<$foo> do C, we need to apply the Role. If we were applying the role to every instance of the Foo class, we would say: package Foo; with 'Say'; ... Then C<< Foo->new(value => '...')->say >> would work. We only want to apply C to a specific instance, however. Fortunately, that is possible: Say->meta->apply($foo); $foo->say; # Hello $foo->can('say'); # CODE(0xc0ffee) $foo->does('Say'); # true my $bar = Foo->new; $bar->can('say'); # no $bar->does('say'): # false That's all there is to it. Applying C to every instance is also possible at runtime: Say->meta->apply(Foo->meta); Let's look at a more complex example: package Integer; use Moose; has 'value' => (is => 'ro', isa => 'Int'); sub one_less { my $self = shift; return (blessed $self)->new( value => $self->value - 1 ) } sub multiply_by { my ($self, $other) = @_; return (blessed $self)->new( value => $self->value * $other->value ) } sub is_zero { shift->value == 0 } sub say { say $_[0]->value; $_[0] } Here we have a basic Integer class. You can do things like: my $two = Integer->new(value => 2)->say; # 2 my $four = $two->multiply_by($two)->say; # 4 my $zero = $two->one_less->one_less->say; # 0 say "is zero" if $zero->is_zero; # is zero Straightforward. Now let's say we want to add a C method. We again begin by creating a Factorial role: package Factorial; use Moose::Role; requires 'is_zero'; requires 'multiply_by'; requires 'one_less'; sub factorial { my $number = shift; return Integer->new( value => 1 ) if $number->is_zero; return $number->multiply_by($number->one_less->factorial); } Notice that this time we make sure we have all the methods that C requires by using the C keyword. This is checked when we apply the role. Let's do that: my $ten = Integer->new(value => 10); eval { say $ten->factorial } or say "dies"; # dies Factorial->meta->apply($ten); $ten->does('Factorial'); $ten->factorial->say; # 362880 my $ten2 = Integer->new(value => 10); $ten2->factorial->say; # dies It works just like you'd expect. However, we are doing something tricky. Let's take a look at the C method. It takes the current value, subtracts 1, and then wraps up the result in another C object. But, you'll note that instead of C<< Integer->new >>, we say C<< (blessed $self)->new >>. That's because to calculate the factorial, the result of C also needs to C. But, role application only affects one instance, so just saying C<< Integer->new >> would fail. The tricky bit is in the implementation of runtime role application. Basically, when you apply a role at runtime, this happens: sub apply_role_to($invocant) { { package Some::Dynamic::Package::1; use Moose; extends 'The::Original::Class'; with 'The::Role::You::Just::Applied'; } $invocant = bless $class, 'Some::Dynamic::Package::1'; } That's not valid Perl, but the idea holds. Whatever you passed to C is reblessed into a dynamically generated package that isa the original class. When we say: (blessed $self)->new( value => ... ); instead of: Integer->new( value => ... ); we're making sure that the Integer we return is in that dynamically-generated class with the role applied to it. That way if we call C on a class that does Factorial, the instance we return can also do Factorial. The key thing to take away is that it's possible, easy, and clean to add methods (via roles) to specific instances of classes. However, you do need to make sure your class isn't working against, like our Integer class would be if we just returned C<< Integer->new >>. =head2 Footnote 1 Incidentally, did you know you can modify a method's invocant? Try this sometime: package Foo; sub go { say 'Foo::go'; $_[0] = 'Bar'; } package Bar; sub go { say 'Bar::go'; $_[0] = 'Foo'; } my $class = 'Foo'; $class->go; # Foo::go $class->go; # Bar::go $class->go; # Foo::go This is a great way to ensure that your code is completely unmaintainable. But it's also good for implementing runtime role application. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFHsL3V2rw+dVvzZm0RAuuJAJ4nZ11txQRGKw/zekHdukS0K7YsEACeP2fT ah7Osu1qgbnQEq+I0g3wF/4= =QMDI -----END PGP SIGNATURE-----