Programming with Moose/Syntax/before, after and around
before
, after
, and around
are method modifiers. They give control over the chain of events around the actual call to a method. This allows the user to do simple things, easily. Two of the keywords before
, and after
, do nothing more than trigger code. While around
permits dynamic rewriting of the arguments.
These features are documented only in Class::MOP
.
Modifiers
editbefore
editpackage Jerk;
use Moose;
sub fart {
print '\me farts';
}
before 'fart' => sub {
print '\me laughs';
};
package main;
my $m = Jerk->new;
$m->fart;
after
editpackage Manners;
use Moose;
sub fart {
print '\me farts';
}
after 'fart' => sub {
print 'excuse me';
};
package main;
my $m = Manners->new;
$m->fart;
around
edit This section is a stub. You can help Wikibooks by expanding it. |
# return an array ref, de-referenced.
around foo => sub {
my $sub = shift;
my $ret = $sub->(@_);
@{ $ret }
}
# auto-instantiate an empty array for a hash value
around foo_lookup => sub {
my ($next, $this, $key) = @_;
my $arrayref = $this->$next($key);
$arrayref ||= $this->foo_hashref->{$key} = [];
return $arrayref;
};
Infinite recursion
editIt might be tempting to call the function that the modifier is attached to within the modifier's sub. This is bad; observe the following and never do it:
has 'foo' => ( isa => 'Int', is => 'ro' );
after 'foo' => sub {
my $self = shift;
say 'Got ' . $self->foo;
}
The result is a bad infinite loop:
->foo()
- The after modifier executes and calls
->foo
->foo()
- The after modifier executes and calls
->foo
- ad infinitum
Execution Order
editArgument rewriting
editWhat not to do
editThis is an example of what most people think will work, but it doesn't: the hackish solution that it seems everyone must attempt once. Observe what you should never do!![1]:
package Foobar;
use Data::Dumper;
use Moose;
sub method {
my ( $self, $ref ) = @_;
print Dumper [ 'method', $ref ];
};
sub modifier {
my ( $self, $ref ) = @_;
print Dumper [ 'modifier before', $ref ];
$ref->[0] = 'hax3d';
print Dumper [ 'modifier after', $ref ];
}
before 'method' => \&modifier;
package main;
my $m = Foobar->new;
$m->method( ['foo'] );
which returns:
$VAR1 = [ 'modifier before', [ 'foo' ] ]; $VAR1 = [ 'modifier after', [ 'hax3d' ] ]; $VAR1 = [ 'method', [ 'hax3d' ] ];
Here you might figure this works, because your able to hack a method of modification into method. but be aware, employing this technique will yield a drastic difference when you pass something other than a reference.
package Foobar;
use Data::Dumper;
use Moose;
sub method {
my ( $self, $scalar ) = @_;
print Dumper [ 'method', $scalar ];
};
sub modifier {
my ( $self, $scalar ) = @_;
print Dumper [ 'modifier before', $scalar ];
$scalar = 'bar';
print Dumper [ 'modifier after', $scalar ];
}
before 'method' => \&modifier;
package main;
my $m = Foobar->new;
$m->method( 'foo' );
$VAR1 = [ 'modifier before', 'foo' ]; $VAR1 = [ 'modifier after', 'bar' ]; $VAR1 = [ 'method', 'foo' ];
Almost surely not what you were looking for?[2] Don't worry, now for what you should do.
What to do
editThe Way of the Moose™ is simple, use the more suited around
. Moose makes it very direct and clear what you intend to do. Using around
your modifier will get: (a) the name of the desired method, (b) a reference to self, and (c) the arguments sent to the function. You can then $self->$sub( @args )
if you wish to dispatch the function or totally rewrite or halt the call.
package Foobar;
use Data::Dumper;
use Moose;
sub method {
my ( $self, $scalar ) = @_;
print Dumper [ 'method', $scalar ];
};
sub modifier {
my ( $sub, $self, @args ) = @_;
print Dumper [ 'modifier', $sub, \@args ];
$self->$sub( @args );
}
around 'method' => \&modifier;
package main;
my $m = Foobar->new;
$m->method( 'foo' );
returns the following:
$VAR1 = [ 'modifier', sub { "DUMMY" }, [ 'foo' ] ]; $VAR1 = [ 'method', 'foo' ];
Footnotes
edit- ^ Unless you are writing comprehensive tutorials on both what to do, and not do.
- ^ For the more astute coder, the
my ( $self, $scalar ) = @_;
isn't the culprit here. Direct access on$_[1]
will result in a runtime death.@_
is actually a constant. - ^ Not to suggest Moose ever does things that regular perl would not allow... just that here is one of the many areas people wish it would..