package Class::Method::Modifiers::Fast; use strict; use warnings; use Data::Util; our $VERSION = '0.041'; use base 'Exporter'; our @EXPORT = qw(before after around); our @EXPORT_OK = (@EXPORT, 'install_modifier'); our %EXPORT_TAGS = ( moose => [qw(before after around)], all => \@EXPORT_OK, ); use Carp 'confess'; sub _install_modifier; # -w *_install_modifier = \&install_modifier; sub install_modifier { my $into = shift; my $type = shift; my $modifier = pop; my @names = @_; foreach my $name (@names) { my $method = Data::Util::get_code_ref( $into, $name ); if ( !$method || !Data::Util::subroutine_modifier($method) ) { unless ($method) { $method = $into->can($name) or confess "The method '$name' is not found in the inheritance hierarchy for class $into"; } $method = Data::Util::modify_subroutine( $method, $type => [$modifier] ); no warnings 'redefine'; Data::Util::install_subroutine( $into, $name => $method ); } else { Data::Util::subroutine_modifier( $method, $type => $modifier ); } } return; } sub before { _install_modifier( scalar(caller), 'before', @_ ); } sub after { _install_modifier( scalar(caller), 'after', @_ ); } sub around { _install_modifier( scalar(caller), 'around', @_ ); } 1; __END__ =head1 NAME Class::Method::Modifiers::Fast - provides Moose-like method modifiers =head1 SYNOPSIS package Child; use parent 'Parent'; use Class::Method::Modifiers::Fast; sub new_method { } before 'old_method' => sub { carp "old_method is deprecated, use new_method"; }; around 'other_method' => sub { my $orig = shift; my $ret = $orig->(@_); return $ret =~ /\d/ ? $ret : lc $ret; }; =head1 DESCRIPTION Method modifiers are a powerful feature from the CLOS (Common Lisp Object System) world. C provides three modifiers: C, C, and C. C and C are run just before and after the method they modify, but can not really affect that original method. C is run in place of the original method, with a hook to easily call that original method. See the C section for more details on how the particular modifiers work. =head1 MODIFIERS =head2 before method(s) => sub { ... } C is called before the method it is modifying. Its return value is totally ignored. It receives the same C<@_> as the the method it is modifying would have received. You can modify the C<@_> the original method will receive by changing C<$_[0]> and friends (or by changing anything inside a reference). This is a feature! =head2 after method(s) => sub { ... } C is called after the method it is modifying. Its return value is totally ignored. It receives the same C<@_> as the the method it is modifying received, mostly. The original method can modify C<@_> (such as by changing C<$_[0]> or references) and C will see the modified version. If you don't like this behavior, specify both a C and C, and copy the C<@_> during C for C to use. =head2 around method(s) => sub { ... } C is called instead of the method it is modifying. The method you're overriding is passed in as the first argument (called C<$orig> by convention). Watch out for contextual return values of C<$orig>. You can use C to: =over 4 =item Pass C<$orig> a different C<@_> around 'method' => sub { my $orig = shift; my $self = shift; $orig->($self, reverse @_); }; =item Munge the return value of C<$orig> around 'method' => sub { my $orig = shift; ucfirst $orig->(@_); }; =item Avoid calling C<$orig> -- conditionally around 'method' => sub { my $orig = shift; return $orig->(@_) if time() % 2; return "no dice, captain"; }; =back =head1 AUTHOR Takatoshi Kitano Ekitano.tk@gmail.comE gfx =head1 SEE ALSO L =head1 LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut