I'm trying to stick to the published API to dynamically modify methods in Moo, and have not come up with a general solution.
First off, some code:
package R1 {
use Moo::Role;
sub r1 { say __PACKAGE__ }
}
package C1 {
use Moo;
sub c1 { say __PACKAGE__ }
}
use Scalar::Util qw[ blessed ];
use Moo::Role ();
my $c = C1->new;
Moo::Role->apply_roles_to_object( $c, 'R1' );
The role application will illustrate a failure in one approach.
I've tried two approaches.
The first uses Class::Method::Modifiers:
use Class::Method::Modifiers qw[ install_modifier ];
install_modifier( blessed( $c ),
before => r1 =>
sub { say "BEFORE r1" }
);
$c->r1;
and works fine:
% perl test.pl
BEFORE r1
R1
The code for Moo's internal _install_modifier subroutine is very similar, but also performs additional Moo specific actions, so this approach is not exactly equivalent.
The next approach I tried was to directly use the before modifier available to $c, and thus get the additional Moo special sauce:
$c->can('before')->( r1 => sub { say "BEFORE r1" } );
$c->r1;
But...
% perl test.pl
The method 'r1' is not found in the inheritance hierarchy for class C1 at [...]/lib/site_perl/5.28.0/Class/Method/Modifiers.pm line 42.
Class::Method::Modifiers::install_modifier("C1", "before", "r1") called at /[...]/lib/site_perl/5.28.0/Moo/_Utils.pm line 44
Moo::_Utils::_install_modifier("C1", "before", "r1", CODE(0x5590bb800360)) called at [...]/lib/site_perl/5.28.0/Moo.pm line 84
Moo::before("r1", CODE(0x5590bb800360)) called at test.pl line 25
It seems that modifiers were generated for the original C1 class, and not updated when the R1 role was applied. The following egregious hack "fixes" that:
use Import::Into;
Moo->import::into( blessed $c );
$c->can('before')->( r1 => sub { say "BEFORE r1" } );
$c->r1;
which results in:
% perl test.pl
BEFORE r1
R1
So, is there a means of achieving my goal using only the published Moo API?
Thanks!
You can modify methods by just applying another role (it doesn't even have to be Moo::Role unless you are dealing with attributes):
Just make sure you apply this role after the one that composes the r1 method, or include a dummy
sub r1 {}in the role (if one already exists it will be ignored).