File Coverage

blib/lib/Hook/LexWrap.pm
Criterion Covered Total %
statement 80 82 97.5
branch 46 48 95.8
condition 16 17 94.1
subroutine 18 20 90.0
pod 1 1 100.0
total 161 168 95.8


line stmt bran cond sub pod time code
1 2     2   23180 use strict;
  2         4  
  2         98  
2 2     2   9 use warnings;
  2         4  
  2         146  
3             package Hook::LexWrap;
4             # git description: v0.24-8-gd2290ba
5             $Hook::LexWrap::VERSION = '0.25';
6             # ABSTRACT: Lexically scoped subroutine wrappers
7              
8 2     2   13 use Carp;
  2         4  
  2         211  
9              
10             {
11 2     2   12 no warnings 'redefine';
  2         2  
  2         481  
12             *CORE::GLOBAL::caller = sub (;$) {
13 86   100 86   385 my ($height) = ($_[0]||0);
14 86         94 my $i=1;
15 86         109 my $name_cache;
16 86         137 while (1) {
17 402 100       3154 my @caller = CORE::caller($i++) or return;
18 390 100       884 $caller[3] = $name_cache if $name_cache;
19 390 100       780 $name_cache = $caller[0] eq 'Hook::LexWrap' ? $caller[3] : '';
20 390 100 100     1980 next if $name_cache || $height-- != 0;
21 74 100       1504 return wantarray ? @_ ? @caller : @caller[0..2] : $caller[0];
    100          
22             }
23             };
24             }
25              
26 2     2   15 sub import { no strict 'refs'; *{caller()."::wrap"} = \&wrap }
  2     2   2  
  2         353  
  2         18  
  2         613  
27              
28             sub wrap (*@) { ## no critic Prototypes
29 28     28 1 479 my ($typeglob, %wrapper) = @_;
30 28 100 100     207 $typeglob = (ref $typeglob || $typeglob =~ /::/)
31             ? $typeglob
32             : caller()."::$typeglob";
33 28         32 my $original;
34             {
35 2     2   13 no strict 'refs';
  2         3  
  2         289  
  28         36  
36 28   66     284 $original = ref $typeglob eq 'CODE' && $typeglob
37             || *$typeglob{CODE}
38             || croak "Can't wrap non-existent subroutine ", $typeglob;
39             }
40 54 100       331 croak "'$_' value is not a subroutine reference"
41 27         55 foreach grep {$wrapper{$_} && ref $wrapper{$_} ne 'CODE'}
42             qw(pre post);
43 2     2   11 no warnings 'redefine';
  2         4  
  2         926  
44 25         53 my ($caller, $unwrap) = *CORE::GLOBAL::caller{CODE};
45             my $imposter = sub {
46 52 100   52   223 if ($unwrap) { goto &$original }
  24         45  
47 28         37 my ($return, $prereturn);
48 28 100       59 if (wantarray) {
    100          
49 11         27 $prereturn = $return = [];
50 11 100       46 () = $wrapper{pre}->(@_,$return) if $wrapper{pre};
51 11 100 100     142 if (ref $return eq 'ARRAY' && $return == $prereturn && !@$return) {
      100        
52 7         16 $return = [ &$original ];
53 7 100       86 () = $wrapper{post}->(@_, $return)
54             if $wrapper{post};
55             }
56 11 100       129 return ref $return eq 'ARRAY' ? @$return : ($return);
57             }
58             elsif (defined wantarray) {
59 4     4   25 $return = bless sub {$prereturn=1}, 'Hook::LexWrap::Cleanup';
  4         31  
60 4 100       20 my $dummy = $wrapper{pre}->(@_, $return) if $wrapper{pre};
61 4 100       35 unless ($prereturn) {
62 3         7 $return = &$original;
63 3 50       21 $dummy = scalar $wrapper{post}->(@_, $return)
64             if $wrapper{post};
65             }
66 4         34 return $return;
67             }
68             else {
69 13     13   68 $return = bless sub {$prereturn=1}, 'Hook::LexWrap::Cleanup';
  13         68  
70 13 100       55 $wrapper{pre}->(@_, $return) if $wrapper{pre};
71 13 50       63 unless ($prereturn) {
72 13         20 &$original;
73 13 100       124 $wrapper{post}->(@_, $return)
74             if $wrapper{post};
75             }
76 13         77 return;
77             }
78 25         153 };
79 25 100       91 ref $typeglob eq 'CODE' and return defined wantarray
    100          
80             ? $imposter
81             : carp "Uselessly wrapped subroutine reference in void context";
82             {
83 2     2   12 no strict 'refs';
  2         3  
  2         505  
  23         25  
84 23         27 *{$typeglob} = $imposter;
  23         92  
85             }
86 23 100       82 return unless defined wantarray;
87 10     10   79 return bless sub{ $unwrap=1 }, 'Hook::LexWrap::Cleanup';
  10         41  
88             }
89              
90             package Hook::LexWrap::Cleanup;
91             # git description: v0.24-8-gd2290ba
92             $Hook::LexWrap::Cleanup::VERSION = '0.25';
93              
94 27     27   918 sub DESTROY { $_[0]->() }
95             use overload
96 6     6   58 q{""} => sub { undef },
97 0     0   0 q{0+} => sub { undef },
98 0     0   0 q{bool} => sub { undef },
99 2     2   3423 q{fallback}=>1; #fallback=1 - like no overloading for other operations
  2         2865  
  2         29  
100              
101             1;
102              
103             __END__
104              
105             =pod
106              
107             =encoding UTF-8
108              
109             =head1 NAME
110              
111             Hook::LexWrap - Lexically scoped subroutine wrappers
112              
113             =head1 VERSION
114              
115             version 0.25
116              
117             =head1 SYNOPSIS
118              
119             use Hook::LexWrap;
120              
121             sub doit { print "[doit:", caller, "]"; return {my=>"data"} }
122              
123             SCOPED: {
124             wrap doit =>
125             pre => sub { print "[pre1: @_]\n" },
126             post => sub { print "[post1:@_]\n"; $_[1]=9; };
127              
128             my $temporarily = wrap doit =>
129             post => sub { print "[post2:@_]\n" },
130             pre => sub { print "[pre2: @_]\n "};
131              
132             @args = (1,2,3);
133             doit(@args); # pre2->pre1->doit->post1->post2
134             }
135              
136             @args = (4,5,6);
137             doit(@args); # pre1->doit->post1
138              
139             =head1 DESCRIPTION
140              
141             Hook::LexWrap allows you to install a pre- or post-wrapper (or both)
142             around an existing subroutine. Unlike other modules that provide this
143             capacity (e.g. Hook::PreAndPost and Hook::WrapSub), Hook::LexWrap
144             implements wrappers in such a way that the standard C<caller> function
145             works correctly within the wrapped subroutine.
146              
147             To install a prewrappers, you write:
148              
149             use Hook::LexWrap;
150              
151             wrap 'subroutine_name', pre => \&some_other_sub;
152              
153             #or: wrap *subroutine_name, pre => \&some_other_sub;
154              
155             The first argument to C<wrap> is a string containing the name of the
156             subroutine to be wrapped (or the typeglob containing it, or a
157             reference to it). The subroutine name may be qualified, and the
158             subroutine must already be defined. The second argument indicates the
159             type of wrapper being applied and must be either C<'pre'> or
160             C<'post'>. The third argument must be a reference to a subroutine that
161             implements the wrapper.
162              
163             To install a post-wrapper, you write:
164              
165             wrap 'subroutine_name', post => \&yet_another_sub;
166              
167             #or: wrap *subroutine_name, post => \&yet_another_sub;
168              
169             To install both at once:
170              
171             wrap 'subroutine_name',
172             pre => \&some_other_sub,
173             post => \&yet_another_sub;
174              
175             or:
176              
177             wrap *subroutine_name,
178             post => \&yet_another_sub, # order in which wrappers are
179             pre => \&some_other_sub; # specified doesn't matter
180              
181             Once they are installed, the pre- and post-wrappers will be called before
182             and after the subroutine itself, and will be passed the same argument list.
183              
184             The pre- and post-wrappers and the original subroutine also all see the same
185             (correct!) values from C<caller> and C<wantarray>.
186              
187             =head2 Short-circuiting and long-circuiting return values
188              
189             The pre- and post-wrappers both receive an extra argument in their @_
190             arrays. That extra argument is appended to the original argument list
191             (i.e. is can always be accessed as $_[-1]) and acts as a place-holder for
192             the original subroutine's return value.
193              
194             In a pre-wrapper, $_[-1] is -- for obvious reasons -- C<undef>. However,
195             $_[-1] may be assigned to in a pre-wrapper, in which case Hook::LexWrap
196             assumes that the original subroutine has been "pre-empted", and that
197             neither it, nor the corresponding post-wrapper, nor any wrappers that
198             were applied I<before> the pre-empting pre-wrapper was installed, need
199             be run. Note that any post-wrappers that were installed after the
200             pre-empting pre-wrapper was installed I<will> still be called before the
201             original subroutine call returns.
202              
203             In a post-wrapper, $_[-1] contains the return value produced by the
204             wrapped subroutine. In a scalar return context, this value is the scalar
205             return value. In an list return context, this value is a reference to
206             the array of return values. $_[-1] may be assigned to in a post-wrapper,
207             and this changes the return value accordingly.
208              
209             Access to the arguments and return value is useful for implementing
210             techniques such as memoization:
211              
212             my %cache;
213             wrap fibonacci =>
214             pre => sub { $_[-1] = $cache{$_[0]} if $cache{$_[0]} },
215             post => sub { $cache{$_[0]} = $_[-1] };
216              
217             or for converting arguments and return values in a consistent manner:
218              
219             # set_temp expects and returns degrees Fahrenheit,
220             # but we want to use Celsius
221             wrap set_temp =>
222             pre => sub { splice @_, 0, 1, $_[0] * 1.8 + 32 },
223             post => sub { $_[-1] = ($_[0] - 32) / 1.8 };
224              
225             =head2 Lexically scoped wrappers
226              
227             Normally, any wrappers installed by C<wrap> remain attached to the
228             subroutine until it is undefined. However, it is possible to make
229             specific wrappers lexically bound, so that they operate only until
230             the end of the scope in which they're created (or until some other
231             specific point in the code).
232              
233             If C<wrap> is called in a I<non-void> context:
234              
235             my $lexical = wrap 'sub_name', pre => \&wrapper;
236              
237             it returns a special object corresponding to the particular wrapper being
238             placed around the original subroutine. When that object is destroyed
239             -- when its container variable goes out of scope, or when its
240             reference count otherwise falls to zero (e.g. C<undef $lexical>), or
241             when it is explicitly destroyed (C<$lexical-E<gt>DESTROY>) --
242             the corresponding wrapper is removed from around
243             the original subroutine. Note, however, that all other wrappers around the
244             subroutine are preserved.
245              
246             =head2 Anonymous wrappers
247              
248             If the subroutine to be wrapped is passed as a reference (rather than by name
249             or by typeglob), C<wrap> does not install the wrappers around the
250             original subroutine. Instead it generates a new subroutine which acts
251             as if it were the original with those wrappers around it.
252             It then returns a reference to that new subroutine. Only calls to the original
253             through that wrapped reference invoke the wrappers. Direct by-name calls to
254             the original, or calls through another reference, do not.
255              
256             If the original is subsequently wrapped by name, the anonymously wrapped
257             subroutine reference does not see those wrappers. In other words,
258             wrappers installed via a subroutine reference are completely independent
259             of those installed via the subroutine's name (or typeglob).
260              
261             For example:
262              
263             sub original { print "ray" }
264              
265             # Wrap anonymously...
266             my $anon_wrapped = wrap \&original, pre => sub { print "do..." };
267              
268             # Show effects...
269             original(); # prints "ray"
270             $anon_wrapped->(); # prints "do..ray"
271              
272             # Wrap nonymously...
273             wrap *original,
274             pre => sub { print "fa.." },
275             post => sub { print "..mi" };
276              
277             # Show effects...
278             original(); # now prints "fa..ray..mi"
279             $anon_wrapped->(); # still prints "do...ray"
280              
281             =head1 DIAGNOSTICS
282              
283             =over
284              
285             =item C<Can't wrap non-existent subroutine %s>
286              
287             An attempt was made to wrap a subroutine that was not defined at the
288             point of wrapping.
289              
290             =item C<'pre' value is not a subroutine reference>
291              
292             The value passed to C<wrap> after the C<'pre'> flag was not
293             a subroutine reference. Typically, someone forgot the C<sub> on
294             the anonymous subroutine:
295              
296             wrap 'subname', pre => { your_code_here() };
297              
298             and Perl interpreted the last argument as a hash constructor.
299              
300             =item C<'post' value is not a subroutine reference>
301              
302             The value passed to C<wrap> after the C<'post'> flag was not
303             a subroutine reference.
304              
305             =item C<Uselessly wrapped subroutine reference in void context> (warning only)
306              
307             When the subroutine to be wrapped is passed as a subroutine reference,
308             C<wrap> does not install the wrapper around the original, but instead
309             returns a reference to a subroutine which wraps the original
310             (see L<Anonymous wrappers>).
311              
312             However, there's no point in doing this if you don't catch the resulting
313             subroutine reference.
314              
315             =back
316              
317             =head1 BLAME
318              
319             Schwern made me do this (by implying it wasn't possible ;-)
320              
321             =head1 BUGS
322              
323             There are undoubtedly serious bugs lurking somewhere in code this funky :-)
324              
325             Bug reports and other feedback are most welcome.
326              
327             =head1 SEE ALSO
328              
329             Sub::Prepend
330              
331             =head1 AUTHOR
332              
333             Damian Conway <damian@conway.org>
334              
335             =head1 COPYRIGHT AND LICENSE
336              
337             This software is copyright (c) 2001 by Damian Conway.
338              
339             This is free software; you can redistribute it and/or modify it under
340             the same terms as the Perl 5 programming language system itself.
341              
342             =head1 CONTRIBUTORS
343              
344             =for stopwords Alexandr Ciornii Karen Etheridge Father Chrysostomos
345              
346             =over 4
347              
348             =item *
349              
350             Alexandr Ciornii <alexchorny@gmail.com>
351              
352             =item *
353              
354             Karen Etheridge <ether@cpan.org>
355              
356             =item *
357              
358             Father Chrysostomos <sprout@cpan.org>
359              
360             =back
361              
362             =cut