File Coverage

blib/lib/Monkey/Patch/Action/Handle.pm
Criterion Covered Total %
statement 77 78 98.7
branch 23 24 95.8
condition 8 12 66.6
subroutine 14 15 93.3
pod 0 1 0.0
total 122 130 93.8


line stmt bran cond sub pod time code
1             package Monkey::Patch::Action::Handle;
2              
3 1     1   19 use 5.010;
  1         5  
4 1     1   9 use strict;
  1         3  
  1         35  
5 1     1   8 use warnings;
  1         2  
  1         39  
6              
7 1     1   9 use Scalar::Util qw(weaken);
  1         3  
  1         303  
8 1     1   555 use Sub::Delete;
  1         1114  
  1         327  
9              
10             our $VERSION = '0.05'; # VERSION
11              
12             my %stacks;
13              
14             sub __find_previous {
15 10     10   28 my ($stack, $code) = @_;
16 10     0   20 state $empty = sub {};
17              
18 10         33 for my $i (1..$#$stack) {
19 17 100       54 if ($stack->[$i][1] == $code) {
20 10   66     118 return $stack->[$i-1][2] // $stack->[$i-1][1];
21             }
22             }
23 0         0 $empty;
24             }
25              
26             sub new {
27 17     17 0 92 my ($class, %args) = @_;
28              
29 17         54 my $type = $args{-type};
30 17         50 delete $args{-type};
31              
32 17         36 my $code = $args{code};
33              
34 17         40 my $name = "$args{package}::$args{subname}";
35 17         24 my $stack;
36 17 100       50 if (!$stacks{$name}) {
37 3         8 $stacks{$name} = [];
38 3 100       16 push @{$stacks{$name}}, [sub => \&$name] if defined(&$name);
  1         5  
39             }
40 17         34 $stack = $stacks{$name};
41              
42 17         42 my $self = bless \%args, $class;
43              
44 1     1   10 no strict 'refs';
  1         4  
  1         48  
45 1     1   9 no warnings 'redefine';
  1         3  
  1         481  
46 17 100       62 if ($type eq 'sub') {
    100          
    50          
47 6         18 push @$stack, [$type => $code];
48 6         33 *$name = $code;
49             } elsif ($type eq 'delete') {
50 4     1   15 $code = sub {};
51 4         8 $args{code} = $code;
52 4         10 push @$stack, [$type, $code];
53 4         15 delete_sub $name;
54             } elsif ($type eq 'wrap') {
55 7         33 weaken($self);
56             my $wrapper = sub {
57             my $ctx = {
58             package => $self->{package},
59             subname => $self->{subname},
60             extra => $self->{extra},
61 10     10   11772 orig => __find_previous($stack, $self->{code}),
62             };
63 10         30 unshift @_, $ctx;
64 10         16 goto &{$self->{code}};
  10         41  
65 7         30 };
66 7         20 push @$stack, [$type => $code => $wrapper];
67 7         25 *$name = $wrapper;
68             }
69              
70 17         466 $self;
71             }
72              
73             sub DESTROY {
74 17     17   43395 my $self = shift;
75              
76 17         69 my $name = "$self->{package}::$self->{subname}";
77 17         38 my $stack = $stacks{$name};
78 17         32 my $code = $self->{code};
79              
80 17         62 for my $i (0..$#$stack) {
81 37 100       113 if($stack->[$i][1] == $code) {
82 17 100       52 if ($stack->[$i+1]) {
83             # check conflict
84 2 100 66     21 if ($stack->[$i+1][0] eq 'wrap' &&
      66        
85             ($i == 0 || $stack->[$i-1][0] eq 'delete')) {
86 1         3 my $p = $self->{patcher};
87 1         27 warn "Warning: unapplying patch to $name ".
88             "(applied in $p->[1]:$p->[2]) before a wrapping patch";
89             }
90             }
91              
92 1     1   10 no strict 'refs';
  1         4  
  1         57  
93 17 100       55 if ($i == @$stack-1) {
94 15 100       37 if ($i) {
95 1     1   8 no warnings 'redefine';
  1         3  
  1         193  
96 12 100       47 if ($stack->[$i-1][0] eq 'delete') {
97 2         9 delete_sub $name;
98             } else {
99 10   66     95 *$name = $stack->[$i-1][2] // $stack->[$i-1][1];
100             }
101             } else {
102 3         11 delete_sub $name;
103             }
104             }
105 17         656 splice @$stack, $i, 1;
106 17         126 last;
107             }
108             }
109             }
110              
111             1;
112              
113             __END__