line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Hook::Queue; |
2
|
1
|
|
|
1
|
|
786
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
38
|
|
3
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
38
|
|
4
|
1
|
|
|
1
|
|
1746
|
use Devel::Peek qw(CvGV); |
|
1
|
|
|
|
|
581
|
|
|
1
|
|
|
|
|
6
|
|
5
|
|
|
|
|
|
|
our $VERSION = 1.21; |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 NAME |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
Hook::Queue - define a queue of handlers |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 SYNOPSIS |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
# define a Liar class which always claims to be what you're asking |
14
|
|
|
|
|
|
|
# about |
15
|
|
|
|
|
|
|
package Liar; |
16
|
|
|
|
|
|
|
use Hook::Queue 'UNIVERSAL::isa' => sub { |
17
|
|
|
|
|
|
|
my $what = shift; |
18
|
|
|
|
|
|
|
my $class = shift; |
19
|
|
|
|
|
|
|
return 1 if (ref $what || $what) eq "Liar"; |
20
|
|
|
|
|
|
|
# it's not my call, pass it down the chain |
21
|
|
|
|
|
|
|
return Hook::Queue->defer; |
22
|
|
|
|
|
|
|
}; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=head1 DESCRIPTION |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
Hook::Queue provides a mechanism for stacking global handlers in a |
27
|
|
|
|
|
|
|
queue of routines that will take an attempt at answering the |
28
|
|
|
|
|
|
|
subroutine call addressed to it. |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
For each subroutine that joins the queue, it can either return a |
31
|
|
|
|
|
|
|
canonical answer, or indicate that it's deferring along the queue by |
32
|
|
|
|
|
|
|
calling the Cdefer> method and returning. |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
When you say C |
35
|
|
|
|
|
|
|
head, and as such your position may very, depending on compilation |
36
|
|
|
|
|
|
|
order of the Perl program. As such you should remember to C |
37
|
|
|
|
|
|
|
even if your testing shows you to be at the end of the queue in test |
38
|
|
|
|
|
|
|
circumstances. |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=cut |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
my ($Defer, %Hooks); |
43
|
1
|
|
|
1
|
0
|
7
|
sub defer { $Defer = 1 } |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub import { |
46
|
1
|
|
|
1
|
|
360
|
my $class = shift; |
47
|
1
|
|
|
|
|
4
|
my %hooks = @_; |
48
|
1
|
|
|
|
|
4
|
for my $hook (keys %hooks) { |
49
|
1
|
|
|
1
|
|
176
|
my $hooked = do { no strict 'refs'; \&$hook }; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
164
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
50
|
1
|
50
|
|
|
|
7
|
if (CvGV($hooked) ne $hook) { |
51
|
|
|
|
|
|
|
# something already lives there, save at the head of the |
52
|
|
|
|
|
|
|
# queue and install |
53
|
1
|
|
|
|
|
2
|
unshift @{ $Hooks{$hook} }, $hooked; |
|
1
|
|
|
|
|
5
|
|
54
|
|
|
|
|
|
|
my $sub = sub { |
55
|
2
|
|
|
2
|
|
6
|
for my $segment (@{ $Hooks{ $hook } }) { |
|
2
|
|
|
|
|
5
|
|
56
|
3
|
|
|
|
|
4
|
$Defer = 0; |
57
|
3
|
|
|
|
|
8
|
my $ret = $segment->( @_ ); |
58
|
3
|
100
|
|
|
|
15
|
next if $Defer; |
59
|
2
|
|
|
|
|
9
|
return $ret; |
60
|
|
|
|
|
|
|
} |
61
|
0
|
|
|
|
|
0
|
die "Deferred past the end of the queue of $hook!"; |
62
|
1
|
|
|
|
|
5
|
}; |
63
|
|
|
|
|
|
|
|
64
|
1
|
|
|
1
|
|
5
|
no strict 'refs'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
35
|
|
65
|
1
|
|
|
1
|
|
5
|
no warnings 'redefine'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
89
|
|
66
|
1
|
|
|
|
|
4
|
*$hook = $sub; |
67
|
|
|
|
|
|
|
} |
68
|
1
|
|
|
|
|
2
|
unshift @{ $Hooks{$hook} }, $hooks{ $hook }; |
|
1
|
|
|
|
|
5
|
|
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
1; |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
__END__ |