diff options
Diffstat (limited to 'lib/Moose/Manual/MethodModifiers.pod')
-rw-r--r-- | lib/Moose/Manual/MethodModifiers.pod | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/lib/Moose/Manual/MethodModifiers.pod b/lib/Moose/Manual/MethodModifiers.pod new file mode 100644 index 0000000..671a250 --- /dev/null +++ b/lib/Moose/Manual/MethodModifiers.pod @@ -0,0 +1,432 @@ +# 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.1405 + +=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 Parent; + 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 Child; + use Moose; + extends 'Parent'; + 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 -MChild -e 'Child->new->rant' + + In Child before + In Child around before calling original + In Parent before + In Parent around before calling original + RANTING! + In Parent around after calling original + In Parent after + In Child around after calling original + In Child 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->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 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.little@iinteractive.com> + +=item * + +Dave Rolsky <autarch@urth.org> + +=item * + +Jesse Luehrs <doy@tozt.net> + +=item * + +Shawn M Moore <code@sartak.org> + +=item * + +יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org> + +=item * + +Karen Etheridge <ether@cpan.org> + +=item * + +Florian Ragwitz <rafl@debian.org> + +=item * + +Hans Dieter Pearcey <hdp@weftsoar.net> + +=item * + +Chris Prather <chris@prather.org> + +=item * + +Matt S Trout <mst@shadowcat.co.uk> + +=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 |