NAME
    Hook::WrapSub - wrap subs with pre- and post-call hooks

SYNOPSIS
      use Hook::WrapSub qw( wrap_subs unwrap_subs );

      wrap_subs \&before, 'some_func', 'another_func', \&after;

      unwrap_subs 'some_func';


DESCRIPTION
    wrap_subs

    This function enables intercepting a call to any named
    function; handlers may be added both before and after the
    call to the intercepted function.

    For example:

      wrap_subs \&before, 'some_func', \&after;

    In this case, whenever the sub named 'some_func' is called,
    the &before sub is called first, and the &after sub is
    called afterwards.  These are both optional.  If you only
    want to intercept the call beforehand:

      wrap_subs \&before, 'some_func';

    You may pass more than one sub name:

      wrap_subs \&before, 'foo', 'bar', 'baz', \&after;

    and each one will have the same hooks applied.

    The sub names may be qualified.  Any unqualified names are
    assumed to reside in the package of the caller.

    The &before sub and the &after sub are both passed the
    argument list which is destined for the wrapped sub.  This
    can be inspected, and even altered, in the &before sub:

      sub before {
        ref($_[1]) && $_[1] =~ /\bARRAY\b/
          or croak "2nd arg must be an array-ref!";
        @_ or @_ = qw( default values );
        # if no args passed, insert some default values
      }

    The &after sub is also passed this list.  Modifications to
    it will (obviously) not be seen by the wrapped sub, but the
    caller will see the changes, if it happens to be looking.

    Here's an example that causes a certain method call to be
    redirected to a specific object.  (Note, we use splice to
    change $_[0], because assigning directly to $_[0] would cause
    the change to be visible to the caller, due to the magical
    aliasing nature of @_.)

      my $handler_object = new MyClass;

      Hook::WrapSub::wrap_subs
        sub { splice @_, 0, 1, $handler_object },
        'MyClass::some_method';

      my $other_object = new MyClass;
      $other_object->some_method;

      # even though the method is invoked on
      # $other_object, it will actually be executed
      # with a 0'th argument = $handler_obj,
      # as arranged by the pre-call hook sub.


    Package Variables

    There are some Hook::WrapSub package variables defined,
    which the &before and &after subs may inspect.

    $Hook::WrapSub::name
        This is the fully qualified name of the wrapped sub.

    @Hook::WrapSub::caller
        This is a list which strongly resembles the result of a
        call to the built-in function caller; it is provided
        because calling caller will in fact produce confusing
        results; if your sub is inclined to call caller, have it
        look at this variable instead.

    @Hook::WrapSub::result
        This contains the result of the call to the wrapped sub.
        It is empty in the &before sub.  In the &after sub, it
        will be empty if the sub was called in a void context,
        it will contain one value if the sub was called in a
        scalar context; otherwise, it may have any number of
        elements.  Note that the &after function is not
        prevented from modifying the contents of this array; any
        such modifications will be seen by the caller!

    This simple example shows how Hook::WrapSub can be used to
    log certain subroutine calls:

      sub before {
        print STDERR <<"    EOF";
          About to call $Hook::WrapSub::name( @_ );
          Wantarray=$Hook::WrapSub::caller[5]
        EOF
      }

      sub after {
        print STDERR <<"    EOF";
          Called $Hook::WrapSub::name( @_ );
          Result=( @Hook::WrapSub::result )
        EOF
        @Hook::WrapSub::result
          or @Hook::WrapSub::result = qw( default return );
        # if the sub failed to return something...
      }

    Much more elaborate uses are possible.  Here's one one way
    it could be used with database operations:

      my $dbh; # initialized elsewhere.

      wrap_subs
        sub {
          $dbh->checkpoint
        },

        'MyDb::update',
        'MyDb::delete',

        sub {
          # examine result of sub call:
          if ( $Hook::WrapSub::result[0] ) {
            # success
            $dbh->commit;
          }
          else {
            # failure
            $dbh->rollback;
          }
        };


    unwrap_subs

    This removes the most recent wrapping of the named subs.

    NOTE: Any given sub may be wrapped an unlimited number of
    times.  A "stack" of the wrappings is maintained internally.
    wrap_subs "pushes" a wrapping, and unwrap_subs "pops".

AUTHOR
    [email protected] (John Porter)

COPYRIGHT
    This is free software.  This software may be modified and/or
    distributed under the same terms as Perl itself.