450 lines
12 KiB
Plaintext
450 lines
12 KiB
Plaintext
# PODNAME: Moose::Manual::MethodModifiers
|
|
# ABSTRACT: Moose's method modifiers
|
|
|
|
__END__
|
|
|
|
=pod
|
|
|
|
=encoding UTF-8
|
|
|
|
=head1 NAME
|
|
|
|
Moose::Manual::MethodModifiers - Moose's method modifiers
|
|
|
|
=head1 VERSION
|
|
|
|
version 2.2207
|
|
|
|
=head1 WHAT IS A METHOD MODIFIER?
|
|
|
|
Moose provides a feature called "method modifiers". You can also think
|
|
of these as "hooks" or "advice".
|
|
|
|
It's probably easiest to understand this feature with a few examples:
|
|
|
|
package Example;
|
|
|
|
use Moose;
|
|
|
|
sub foo {
|
|
print " foo\n";
|
|
}
|
|
|
|
before 'foo' => sub { print "about to call foo\n"; };
|
|
after 'foo' => sub { print "just called foo\n"; };
|
|
|
|
around 'foo' => sub {
|
|
my $orig = shift;
|
|
my $self = shift;
|
|
|
|
print " I'm around foo\n";
|
|
|
|
$self->$orig(@_);
|
|
|
|
print " I'm still around foo\n";
|
|
};
|
|
|
|
Now if I call C<< Example->new->foo >> I'll get the following output:
|
|
|
|
about to call foo
|
|
I'm around foo
|
|
foo
|
|
I'm still around foo
|
|
just called foo
|
|
|
|
You probably could have figured that out from the names "before",
|
|
"after", and "around".
|
|
|
|
Also, as you can see, the before modifiers come before around
|
|
modifiers, and after modifiers come last.
|
|
|
|
When there are multiple modifiers of the same type, the before and
|
|
around modifiers run from the last added to the first, and after
|
|
modifiers run from first added to last:
|
|
|
|
before 2
|
|
before 1
|
|
around 2
|
|
around 1
|
|
primary
|
|
around 1
|
|
around 2
|
|
after 1
|
|
after 2
|
|
|
|
=head1 WHY USE THEM?
|
|
|
|
Method modifiers have many uses. They are often used in roles to alter the
|
|
behavior of methods in the classes that consume the role. See
|
|
L<Moose::Manual::Roles> for more information about roles.
|
|
|
|
Since modifiers are mostly useful in roles, some of the examples below
|
|
are a bit artificial. They're intended to give you an idea of how
|
|
modifiers work, but may not be the most natural usage.
|
|
|
|
=head1 BEFORE, AFTER, AND AROUND
|
|
|
|
Method modifiers can be used to add behavior to methods without modifying the definition of those methods.
|
|
|
|
=head2 Before and after Modifiers
|
|
|
|
Method modifiers can be used to add behavior to a method that Moose
|
|
generates for you, such as an attribute accessor:
|
|
|
|
has 'size' => ( is => 'rw' );
|
|
|
|
before 'size' => sub {
|
|
my $self = shift;
|
|
|
|
if (@_) {
|
|
Carp::cluck('Someone is setting size');
|
|
}
|
|
};
|
|
|
|
Another use for the before modifier would be to do some sort of
|
|
prechecking on a method call. For example:
|
|
|
|
before 'size' => sub {
|
|
my $self = shift;
|
|
|
|
die 'Cannot set size while the person is growing'
|
|
if @_ && $self->is_growing;
|
|
};
|
|
|
|
This lets us implement logical checks that don't make sense as type
|
|
constraints. In particular, they're useful for defining logical rules
|
|
about an object's state changes.
|
|
|
|
Similarly, an after modifier could be used for logging an action that
|
|
was taken.
|
|
|
|
Note that the return values of both before and after modifiers are
|
|
ignored.
|
|
|
|
=head2 Around modifiers
|
|
|
|
An around modifier is more powerful than either a before or
|
|
after modifier. It can modify the arguments being passed to the
|
|
original method, and you can even decide to simply not call the
|
|
original method at all. You can also modify the return value with an
|
|
around modifier.
|
|
|
|
An around modifier receives the original method as its first argument,
|
|
I<then> the object, and finally any arguments passed to the method.
|
|
|
|
around 'size' => sub {
|
|
my $orig = shift;
|
|
my $self = shift;
|
|
|
|
return $self->$orig()
|
|
unless @_;
|
|
|
|
my $size = shift;
|
|
$size = $size / 2
|
|
if $self->likes_small_things();
|
|
|
|
return $self->$orig($size);
|
|
};
|
|
|
|
=head2 Wrapping multiple methods at once
|
|
|
|
C<before>, C<after>, and C<around> can also modify multiple methods
|
|
at once. The simplest example of this is passing them as a list:
|
|
|
|
before [qw(foo bar baz)] => sub {
|
|
warn "something is being called!";
|
|
};
|
|
|
|
This will add a C<before> modifier to each of the C<foo>, C<bar>,
|
|
and C<baz> methods in the current class, just as though a separate
|
|
call to C<before> was made for each of them. The list can be passed
|
|
either as a bare list, or as an arrayref. Note that the name of the
|
|
function being modified isn't passed in in any way; this syntax is
|
|
only intended for cases where the function being modified doesn't
|
|
actually matter. If the function name does matter, use something like this:
|
|
|
|
for my $func (qw(foo bar baz)) {
|
|
before $func => sub {
|
|
warn "$func was called!";
|
|
};
|
|
}
|
|
|
|
=head2 Using regular expressions to select methods to wrap
|
|
|
|
In addition, you can specify a regular expression to indicate the
|
|
methods to wrap, like so:
|
|
|
|
after qr/^command_/ => sub {
|
|
warn "got a command";
|
|
};
|
|
|
|
This will match the regular expression against each method name
|
|
returned by L<Class::MOP::Class/get_method_list>, and add a modifier
|
|
to each one that matches. The same caveats apply as above.
|
|
|
|
Using regular expressions to determine methods to wrap is quite a bit more
|
|
powerful than the previous alternatives, but it's also quite a bit more
|
|
dangerous. Bear in mind that if your regular expression matches certain Perl
|
|
and Moose reserved method names with a special meaning to Moose or Perl, such
|
|
as C<meta>, C<new>, C<BUILD>, C<DESTROY>, C<AUTOLOAD>, etc, this could cause
|
|
unintended (and hard to debug) problems and is best avoided.
|
|
|
|
=head2 Execution order of method modifiers and inheritance
|
|
|
|
When both a superclass and an inheriting class have the same method modifiers,
|
|
the method modifiers of the inheriting class are wrapped around the method
|
|
modifiers of the superclass, as the following example illustrates:
|
|
|
|
Here is the parent class:
|
|
|
|
package Superclass;
|
|
use Moose;
|
|
sub rant { printf " RANTING!\n" }
|
|
before 'rant' => sub { printf " In %s before\n", __PACKAGE__ };
|
|
after 'rant' => sub { printf " In %s after\n", __PACKAGE__ };
|
|
around 'rant' => sub {
|
|
my $orig = shift;
|
|
my $self = shift;
|
|
printf " In %s around before calling original\n", __PACKAGE__;
|
|
$self->$orig;
|
|
printf " In %s around after calling original\n", __PACKAGE__;
|
|
};
|
|
1;
|
|
|
|
And the child class:
|
|
|
|
package Subclass;
|
|
use Moose;
|
|
extends 'Superclass';
|
|
before 'rant' => sub { printf "In %s before\n", __PACKAGE__ };
|
|
after 'rant' => sub { printf "In %s after\n", __PACKAGE__ };
|
|
around 'rant' => sub {
|
|
my $orig = shift;
|
|
my $self = shift;
|
|
printf " In %s around before calling original\n", __PACKAGE__;
|
|
$self->$orig;
|
|
printf " In %s around after calling original\n", __PACKAGE__;
|
|
};
|
|
1;
|
|
|
|
And here's the output when we call the wrapped method (C<< Child->rant >>):
|
|
|
|
% perl -MSubclass -e 'Subclass->new->rant'
|
|
|
|
In Subclass before
|
|
In Subclass around before calling original
|
|
In Superclass before
|
|
In Superclass around before calling original
|
|
RANTING!
|
|
In Superclass around after calling original
|
|
In Superclass after
|
|
In Subclass around after calling original
|
|
In Subclass after
|
|
|
|
=head1 INNER AND AUGMENT
|
|
|
|
Augment and inner are two halves of the same feature. The augment
|
|
modifier provides a sort of inverted subclassing. You provide part of
|
|
the implementation in a superclass, and then document that subclasses
|
|
are expected to provide the rest.
|
|
|
|
The superclass calls C<inner()>, which then calls the C<augment>
|
|
modifier in the subclass:
|
|
|
|
package Document;
|
|
|
|
use Moose;
|
|
|
|
sub as_xml {
|
|
my $self = shift;
|
|
|
|
my $xml = "<document>\n";
|
|
$xml .= inner();
|
|
$xml .= "</document>\n";
|
|
|
|
return $xml;
|
|
}
|
|
|
|
Using C<inner()> in this method makes it possible for one or more
|
|
subclasses to then augment this method with their own specific
|
|
implementation:
|
|
|
|
package Report;
|
|
|
|
use Moose;
|
|
|
|
extends 'Document';
|
|
|
|
augment 'as_xml' => sub {
|
|
my $self = shift;
|
|
|
|
my $xml = " <report>\n";
|
|
$xml .= inner();
|
|
$xml .= " </report>\n";
|
|
|
|
return $xml;
|
|
};
|
|
|
|
When we call C<as_xml> on a Report object, we get something like this:
|
|
|
|
<document>
|
|
<report>
|
|
</report>
|
|
</document>
|
|
|
|
But we also called C<inner()> in C<Report>, so we can continue
|
|
subclassing and adding more content inside the document:
|
|
|
|
package Report::IncomeAndExpenses;
|
|
|
|
use Moose;
|
|
|
|
extends 'Report';
|
|
|
|
augment 'as_xml' => sub {
|
|
my $self = shift;
|
|
|
|
my $xml = ' <income>' . $self->income . '</income>';
|
|
$xml .= "\n";
|
|
$xml .= ' <expenses>' . $self->expenses . '</expenses>';
|
|
$xml .= "\n";
|
|
|
|
$xml .= inner() || q{};
|
|
|
|
return $xml;
|
|
};
|
|
|
|
Now our report has some content:
|
|
|
|
<document>
|
|
<report>
|
|
<income>$10</income>
|
|
<expenses>$8</expenses>
|
|
</report>
|
|
</document>
|
|
|
|
What makes this combination of C<augment> and C<inner()> special is
|
|
that it allows us to have methods which are called from parent (least
|
|
specific) to child (most specific). This inverts the normal
|
|
inheritance pattern.
|
|
|
|
Note that in C<Report::IncomeAndExpenses> we call C<inner()> again. If the
|
|
object is an instance of C<Report::IncomeAndExpenses> then this call is a
|
|
no-op, and just returns false. It's a good idea to always call C<inner()> to
|
|
allow for future subclassing.
|
|
|
|
=head1 OVERRIDE AND SUPER
|
|
|
|
Finally, Moose provides some simple sugar for Perl's built-in method
|
|
overriding scheme. If you want to override a method from a parent
|
|
class, you can do this with C<override>:
|
|
|
|
package Employee;
|
|
|
|
use Moose;
|
|
|
|
extends 'Person';
|
|
|
|
has 'job_title' => ( is => 'rw' );
|
|
|
|
override 'display_name' => sub {
|
|
my $self = shift;
|
|
|
|
return super() . q{, } . $self->job_title();
|
|
};
|
|
|
|
The call to C<super()> is almost the same as calling C<<
|
|
$self->SUPER::display_name >>. The difference is that the arguments
|
|
passed to the superclass's method will always be the same as the ones
|
|
passed to the method modifier, and cannot be changed.
|
|
|
|
All arguments passed to C<super()> are ignored, as are any changes
|
|
made to C<@_> before C<super()> is called.
|
|
|
|
=head1 SEMI-COLONS
|
|
|
|
Because all of these method modifiers are implemented as Perl
|
|
functions, you must always end the modifier declaration with a
|
|
semi-colon:
|
|
|
|
after 'foo' => sub { };
|
|
|
|
=head1 EXCEPTIONS AND STACK TRACES
|
|
|
|
An exception thrown in a C<before> modifier will prevent the method it
|
|
modifies from being called at all. An exception in an C<around> modifier may
|
|
prevent the modified method from being called, depending on how the C<around>
|
|
modifier is structured. An exception in an C<after> modifier obviously cannot
|
|
prevent the method it wraps from being called.
|
|
|
|
Both C<override> and C<augment> are similar to C<around> in that they can
|
|
decide whether or not to call the method they modify before or after throwing
|
|
an exception.
|
|
|
|
From the caller's perspective, an exception in a method modifier will look
|
|
like the method it called threw an exception. However, method modifiers are
|
|
just standard Perl subroutines. This means that they end up on the stack in
|
|
stack traces as an additional frame.
|
|
|
|
=head1 CAVEATS
|
|
|
|
These method modification features do not work well with multiple inheritance,
|
|
due to how method resolution is performed in Perl. Experiment with a test
|
|
program to ensure your class hierarchy works as expected, or more preferably,
|
|
don't use multiple inheritance (roles can help with this)!
|
|
|
|
=head1 AUTHORS
|
|
|
|
=over 4
|
|
|
|
=item *
|
|
|
|
Stevan Little <stevan@cpan.org>
|
|
|
|
=item *
|
|
|
|
Dave Rolsky <autarch@urth.org>
|
|
|
|
=item *
|
|
|
|
Jesse Luehrs <doy@cpan.org>
|
|
|
|
=item *
|
|
|
|
Shawn M Moore <sartak@cpan.org>
|
|
|
|
=item *
|
|
|
|
יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org>
|
|
|
|
=item *
|
|
|
|
Karen Etheridge <ether@cpan.org>
|
|
|
|
=item *
|
|
|
|
Florian Ragwitz <rafl@debian.org>
|
|
|
|
=item *
|
|
|
|
Hans Dieter Pearcey <hdp@cpan.org>
|
|
|
|
=item *
|
|
|
|
Chris Prather <chris@prather.org>
|
|
|
|
=item *
|
|
|
|
Matt S Trout <mstrout@cpan.org>
|
|
|
|
=back
|
|
|
|
=head1 COPYRIGHT AND LICENSE
|
|
|
|
This software is copyright (c) 2006 by Infinity Interactive, Inc.
|
|
|
|
This is free software; you can redistribute it and/or modify it under
|
|
the same terms as the Perl 5 programming language system itself.
|
|
|
|
=cut
|