File Coverage

blib/lib/Hook/Queue.pm
Criterion Covered Total %
statement 37 38 97.3
branch 3 4 75.0
condition n/a
subroutine 9 9 100.0
pod 0 1 0.0
total 49 52 94.2


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 you join the queue at its current
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__