This commit is contained in:
449
CPAN/Moose/Manual/MethodModifiers.pod
Normal file
449
CPAN/Moose/Manual/MethodModifiers.pod
Normal file
@@ -0,0 +1,449 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user