NAME
Wrap::Sub - Object Oriented subroutine wrapper with pre and post hooks, and more.
SYNOPSIS
use Wrap::Sub;
Basic functionality example
my $wrapper = Wrap::Sub->new;
# create the wrapped sub object
my $foo_obj = $wrapper->wrap(
'My::Module::foo',
pre => sub { print "$Wrap::Sub::name in pre\n"; },
post => sub { print "$Wrap::Sub::name in post\n"; },
);
# add/change/remove a routine via a method call (send in undef to remove)
$foo_obj->pre(sub { print "changed pre\n"; } );
$foo_obj->post(sub { print "changed post\n"; } );
# name of sub
$foo_obj->name;
# unwrap and rewrap
$foo_obj->unwrap;
$foo_obj->rewrap;
# wrapped or not?
my $is_wrapped = $foo_obj->is_wrapped;
# list all subs wrapped under the current wrap object
my @wrapped_subs = $wrapper->wrapped_subs;
# retrieve all wrapped sub objects
my @sub_objects = $wrapper->wrapped_objects;
Here's an example that shows how you can get elapsed time of a sub (this example requires Time::HiRes)
my $pre_cref = sub {
return gettimeofday();
};
my $post_cref = sub {
my $pre_return = shift;
my $time = gettimeofday() - $pre_return->[0];
print "$Wrap::Sub::name finished in $time seconds\n";
};
$foo_obj->pre($pre_cref);
$foo_obj->post($post_cref);
Manipulate the return from the original subroutine, take action, then return the modified results
my $post_cref = sub {
my ($pre_return, $sub_return) = @_;
if ($sub_return->[0] != 1){
die "$Wrap::Sub::name returned an error\n";
}
else {
$sub_return->[0] = 100;
return $sub_return->[0];
}
};
$foo_obj->post($post_cref, post_return => 1);
Get an ordered list (array of array references) of the last expression evaluated in all sub object pre()
and post()
functions
my @pre_results = $wrapper->pre_results;
my @post_results = $wrapper->post_results;
for my $sub_post_result (@post_results){
print "$sub_post_result->[0]\n";
}
Wrap all subs in a module (does not include imported subs), and have them all perform the same actions
my $wrapper = Wrap::Sub->new(
pre => sub { print "start $Wrap::Sub::name\n"; },
post => sub { print "finish $Wrap::Sub::name\n"; },
);
# wrapping a module returns an href with the sub name as the key,
# and the value being the wrapped sub object
my $module_subs = $wrapper->wrap('My::Module');
# unwrap them all, and confirm
for my $sub_name (keys %$module_subs){
my $sub_obj = $module_subs->{$sub_name};
print "$sub_name is wrapped\n" if $sub_obj->is_wrapped;
$sub_obj->unwrap;
if ($sub_obj->is_wrapped) {
die "$sub_name not unwrapped!"
}
}
DESCRIPTION
This module allows you to wrap subroutines with pre and post hooks while keeping track of your wrapped subs. It also allows you to easily wrap all subs within a module (while filtering out the subs that are imported).
Thanks to code taken out of Hook::LexWrap, caller()
works properly within the wrapped subs.
There are other modules that do this, see "SEE ALSO". I wrote it out of sheer curiosity and experience, not because I didn't check the CPAN for existing modules that do the same thing.
WRAP OBJECT METHODS
new(%opts)
Instantiates and returns a new Wrap::Sub
object, ready to be used to start creating wrapped sub objects.
Options (note that if these are set in new()
, all subs wrapped with this object will exhibit the same behaviour. Set in wrap()
or use pre()
and post()
methods to individualize wrapped subroutine behaviour).
pre => $cref
-
A code reference containing actions that will be executed prior to executing the sub that's wrapped. Receives the parameters that are sent into the wrapped sub (
@_
). Has access to$Wrap::Sub::name
parameter, which holds the name of the original wrapped sub. post => $cref
-
A code reference containing actions that will be executed after the wrapped sub is executed. Has access to
$Wrap::Sub::name
parameter, which holds the name of the original wrapped sub.Receives an array reference with two elements, each another array reference. The first inner reference contains the return value(s) from the
pre
hook, and the second contains the return value(s) from the wrapped sub. If neitherpre
or the actual sub have return values, the respective inner array reference will be empty.Returns whatever you decide you want it to. See
post_return
parameter for further details on returning values. post_return => Bool
-
Set this to true if you want your
post()
hook to return its results, and false if you want the return value(s) from the actual wrapped sub instead. Disabled by default (ie. you'll get the return from the original sub).
wrap('sub', %opts)
Instantiates and returns a new wrapped sub object on each call. sub
is the name of the subroutine to wrap (requires full package name if the sub isn't in main::
).
Options:
See new()
for a description of the parameters. Setting them here allows you to individualize behaviour of the hooks for each wrapped subroutine.
wrapped_subs
Returns a list of all the names of the subs that are currently wrapped under the parent wrap object.
wrapped_objects
Returns a list of all sub objects underneath the parent wrap object, regardless if its sub is currently wrapped or not.
is_wrapped('Sub::Name')
Returns 1 if the sub currently under the parent wrap object is wrapped or not, and 0 if not. Croaks if there hasn't been a child sub object created with this sub name.
pre_results
As each wrapped sub is called where a pre()
method is set, we'll stash the last expression evaluated in it, and push the results to an array. This method will fetch that array.
Each entry is an array reference per pre()
call, containing the returned data.
post_results
As each wrapped sub is called where a post()
method is set, we'll stash the last expression evaluated in it, and push the results to an array. This method will fetch that array.
Each entry is an array reference per post()
call, containing the returned data.
WRAPPED SUB OBJECT METHODS
These methods are for the children wrapped sub objects returned from the parent wrap object. See "WRAP OBJECT METHODS" for methods related to the parent wrap object.
name
Returns the name of the sub represented by this sub object.
pre($cref)
Send in a code reference containing actions that you want to have performed prior to the wrapped sub being executed. Has access to $Wrap::Sub::name
parameter, which holds the name of the original wrapped sub.
post($cref, post_return => Bool)
A code reference containing actions that will be executed after the wrapped sub is executed. Has access to $Wrap::Sub::name
parameter, which holds the name of the original wrapped sub.
The code supplied receives an array reference containing an array reference holding the return values from pre()
and a second array reference containing the return values from the actual wrapped sub. If neither pre
or the actual sub have return values, the respective array reference will be empty.
The optional parameter post_return
specifies that you want the return value(s) from this function instead of the return value(s) generated by the wrapped sub. Disabled by default.
unwrap
Restores the original functionality back to the sub, and runs reset()
on the object.
rewrap
Re-wraps the sub within the object after calling unwrap
on it.
reset
Resets the functional parameters (pre
, post
and post_return
), along with called()
and called_with
back to undef/false. Does not restore the sub back to its original state.
is_wrapped
Returns true (1) if the sub the object refers to is currently wrapped, and false (0) if not.
called
Returns true (1) if the sub being wrapped has been called, and false (0) if not.
called_with
Returns an array of the parameters sent to the subroutine. croak()s
if we're called before the wrapped sub has been called.
NOTES
This module has a backwards parent-child relationship. To use, you create a wrap object using "WRAP OBJECT METHODS" new
and wrap
methods, thereafter, you use the returned wrapped sub object "WRAPPED SUB OBJECT METHODS" to perform the work.
SEE ALSO
This module was created for my own sheer curiosity and experience. There are quite a few like it. I've never used any of them (this list was taken from Hook::WrapSub):
Hook::LexWrap provides a similar capability to Hook::WrapSub
, but has the benefit that the caller()
function works correctly within the wrapped subroutine.
Sub::Prepend lets you provide a sub that will be called before a named sub. The caller()
function works correctly in the wrapped sub.
Sub::Mage provides a number of related functions. You can provide pre- and post-call hooks, you can temporarily override a function and then restore it later, and more.
Class::Hook lets you add pre- and post-call hooks around any methods called by your code. It doesn't support functions.
Hook::Scope lets you register callbacks that will be invoked when execution leaves the scope they were registered in.
Hook::PrePostCall provides an OO interface for wrapping a function with pre- and post-call hook functions. Last updated in 1997, and marked as alpha.
Hook::Heckle provides an OO interface for wrapping pre- and post-call hooks around functions or methods in a package. Not updated sinc 2003, and has a 20% failed rate on CPAN Testers.
Class::Wrap provides the wrap()
function, which takes a coderef and a package name. The coderef is invoked every time a method in the package is called.
Sub::Versive lets you stack pre- and post-call hooks. Last updated in 2001.
AUTHOR
Steve Bertrand, <steveb at cpan.org>
BUGS
Please report any bugs or requests at https://github.com/stevieb9/wrap-sub/issues
REPOSITORY
https://github.com/stevieb9/wrap-sub
BUILD RESULTS
CPAN Testers: http://matrix.cpantesters.org/?dist=Wrap-Sub
SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Wrap::Sub
LICENSE AND COPYRIGHT
Copyright 2016 Steve Bertrand.
This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.
See http://dev.perl.org/licenses/ for more information.