From: Dave Rolsky Date: Fri, 19 Dec 2008 21:17:41 +0000 (+0000) Subject: First draft of method modifiers manual X-Git-Tag: 0.66~27^2~24 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=92cd015f94035ca282bb4b6fe2c3f12e69732e5e;p=gitmo%2FMoose.git First draft of method modifiers manual --- diff --git a/lib/Moose/Manual/MethodModifiers.pm b/lib/Moose/Manual/MethodModifiers.pm new file mode 100644 index 0000000..c8519f7 --- /dev/null +++ b/lib/Moose/Manual/MethodModifiers.pm @@ -0,0 +1,273 @@ +=pod + +=head1 NAME + +Moose::Manual::Attribute - Moose's Method Modifiers + +=head1 WHAT IS A METHOD MODIFIER? + +Moose provides a feature called "method modifiers". Another word for +this feature might be "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. One very common use is in roles. This +lets roles alter the behavior of methods in the classes that use +them. See L for more about roles. + +Modifiers really are at their most useful in roles, so 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 usages. + +=head1 BEFORE, AFTER, AND AROUND + +Method modifiers can also 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 +pre-checking 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 fit well into +constraints. + +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. + +An around modifier is a bit more powerful than either a before or +after modifier. First, it is easy to modify the arguments being passed +onto the original method in an around modifier. Second, you can decide +to simply not call the original method at all, unlike with other +modifiers. Finally, you can modify the return value with an around +modifier. + +An around modifier receives the original method as its first argument, +I 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); + }; + +=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, which then calls the C +modifier in the subclass: + + package Document; + + use Moose; + + sub as_xml { + my $self = shift; + + my $xml = "\n"; + $xml .= inner(); + $xml .= "\n"; + + return $xml; + } + +Using C 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 = "\n"; + $xml .= inner(); + $xml .= "\n"; + + return $xml; + }; + +When we call C on a Report object, we get something like this: + + + + + + +But we also called C in C, 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 = '' . $self->income . ''; + $xml .= "\n"; + my $xml = '' . $self->expenses . ''; + $xml .= "\n"; + + $xml .= inner() || q{}; + + return $xml; + }; + +Now our report has some content: + + + + $10 + $8 + + + +What makes this combination of C and C special is +that it allows us to have methods which are called from ISUPER::method >> to call the parent. + +Note that in C we call C again. If +the object is an instance of C then this +call is a no-op, and just returns false. + +=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: + + 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 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 are ignored, as are any changes +made to C<@_> before C 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 AUTHOR + +Dave Rolsky Eautarch@urth.orgE + +=head1 COPYRIGHT AND LICENSE + +Copyright 2008 by Infinity Interactive, Inc. + +L + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut