File Coverage

blib/lib/Monkey/Patch/Handle.pm
Criterion Covered Total %
statement 71 72 98.6
branch 14 14 100.0
condition 6 8 75.0
subroutine 18 19 94.7
pod 0 8 0.0
total 109 121 90.0


line stmt bran cond sub pod time code
1             package Monkey::Patch::Handle;
2             BEGIN {
3 3     3   81 $Monkey::Patch::Handle::VERSION = '0.03';
4             }
5 3     3   17 use Scalar::Util qw(weaken);
  3         5  
  3         496  
6 3     3   2760 use Sub::Delete;
  3         3575  
  3         12390  
7              
8 3     3   32 use strict;
  3         7  
  3         100  
9 3     3   17 use warnings;
  3         6  
  3         1435  
10              
11             my %handles;
12              
13             # What we're doing here, essentially, is keeping a stack of subroutine
14             # refs for each name (Foo::bar::baz type name). We're doing this so that
15             # the coderef that lives at that name is always the top of the stack, so
16             # the wrappers get uninstalled in a funky order all hell doesn't break
17             # loose. The most recently installed undestroyed wrapper will always get
18             # called, and it will unwind gracefully until we get down to the original
19             # sub (if there was one).
20              
21             sub new {
22 7     7 0 45 my ($class, %args) = @_;
23 7         59 bless \%args, $class;
24             }
25              
26             sub name {
27 21     21 0 34 my $self = shift;
28 21   66     181 $self->{name} ||= "$self->{package}::$self->{subname}";
29             }
30              
31             sub stack {
32 29     29 0 36 my $self = shift;
33 29   100     1134 $self->{stack} ||= $handles{ $self->name } ||= [];
      66        
34             }
35              
36             sub call_previous {
37 15     15 0 19 my $self = shift;
38 15         31 my $stack = $self->stack;
39 15         32 my $wrapper = $self->wrapper;
40 15         176 for my $i (1..$#$stack) {
41 18 100       45 if ($stack->[$i] == $wrapper) {
42 12         14 goto &{ $stack->[$i-1] };
  12         46  
43             }
44             }
45 3         20 $self->call_default(@_);
46             }
47              
48 0     0 0 0 sub call_default {}
49              
50 10     10 0 27 sub should_call_code { 1 }
51              
52             sub wrapper {
53 29     29 0 37 my $self = shift;
54 29 100       67 unless ($self->{wrapper}) {
55 7         23 weaken($self);
56             $self->{wrapper} = sub {
57 16 100   16   1479 if ($self->should_call_code($_[0])) {
58 14         56 unshift @_, sub { $self->call_previous(@_) };
  13         69  
59 14         45 goto $self->{code};
60             }
61             else {
62 2         7 return $self->call_previous(@_);
63             }
64 7         36 };
65             }
66 29         55 return $self->{wrapper};
67             }
68              
69             sub install {
70 7     7 0 14 my $self = shift;
71 7         40 my $name = $self->name;
72 7         42 my $stack = $self->stack;
73              
74 3     3   18 no strict 'refs';
  3         6  
  3         232  
75              
76 7 100       24 unless (@$stack) {
77 5 100       28 if (*$name{CODE}) {
78 2         9 push @$stack, \&$name;
79             }
80             }
81              
82 7         37 my $code = $self->wrapper;
83              
84 3     3   14 no warnings 'redefine';
  3         8  
  3         476  
85 7         27 *$name = $code;
86 7         11 push(@$stack, $code);
87              
88 7         21 return $self;
89             }
90              
91             sub DESTROY {
92 7     7   4992 my $self = shift;
93 7         20 my $stack = $self->stack;
94 7         18 my $wrapper = $self->wrapper;
95 7         24 for my $i (0..$#$stack) {
96 12 100       40 if($stack->[$i] == $wrapper) {
97 7         59 splice @$stack, $i, 1;
98 3     3   45 no strict 'refs';
  3         66  
  3         167  
99 7         16 my $name = $self->name;
100 7 100       23 if(my $top = $stack->[-1]) {
101 3     3   15 no warnings 'redefine';
  3         5  
  3         317  
102 4         14 *$name = $top;
103             }
104             else {
105 3         15 delete_sub $name;
106             }
107 7         476 last;
108             }
109             }
110             }
111              
112             1;
113              
114             =pod
115              
116             =begin Pod::Coverage
117              
118             .*
119              
120             =end Pod::Coverage