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 $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->wrapped_state; # list all subs wrapped under the current wrap object my @wrapped_subs = $wrapper->wrapped_subs; 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 post() calls, even if post_return is false my @post_results = $wrapper->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->wrapped_state; $sub_obj->unwrap; if ($sub_obj->wrapped_state) { 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 has the ability to wrap all subs within entire modules easily (while filtering out ones that are imported). Thanks to code taken out of Hook::LexWrap, caller() works properly within the wrapped sub. 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 cteating 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 mock() 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 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. 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. wrapped_state('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. 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. 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. 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. wrapped_state Returns true (1) if the sub the object refers to is currently wrapped, and false (0) if not. name Returns the name of the sub being wrapped. 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. 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. 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, 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.