File Coverage

blib/lib/Require/HookChain.pm
Criterion Covered Total %
statement 41 46 89.1
branch 16 22 72.7
condition 5 9 55.5
subroutine 5 5 100.0
pod n/a
total 67 82 81.7


line stmt bran cond sub pod time code
1             ## no critic: TestingAndDebugging::RequireUseStrict
2             package Require::HookChain;
3              
4             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
5             our $DATE = '2023-07-23'; # DATE
6             our $DIST = 'Require-HookChain'; # DIST
7             our $VERSION = '0.015'; # VERSION
8              
9             #IFUNBUILT
10             # use strict;
11             # use warnings;
12             #END IFUNBUILT
13              
14             # be minimalistic, use our own blessed() so we don't have to load any module (in this case, Scalar::Util)
15             unless (defined &blessed) {
16 133 100   133   206 *blessed = sub { my $arg = shift; my $ref = ref $arg; $ref && $ref !~ /\A(SCALAR|ARRAY|HASH|GLOB|Regexp)\z/ };
  133         177  
  133         463  
17             }
18              
19             our $debug;
20              
21             my $our_hook; $our_hook = sub {
22             my ($self, $filename) = @_;
23              
24             warn "[Require::HookChain] require($filename) ...\n" if $debug;
25              
26             my $r = Require::HookChain::r->new(filename => $filename);
27              
28             for my $item (@INC) {
29             my $ref = ref $item;
30              
31             if (!$ref) {
32             # load from ordinary file
33             next if defined $r->src;
34              
35             my $path = "$item/$filename";
36             if (-f $path) {
37             warn "[Require::HookChain] Loading $filename from $path ...\n" if $debug;
38             open my $fh, "<", $path
39             or die "Can't open $path: $!";
40             local $/;
41             $r->src(scalar <$fh>);
42             close $fh;
43             next;
44             }
45             } elsif ($ref =~ /\ARequire::HookChain::(.+)/) {
46             warn "[Require::HookChain] Calling hook $1 ...\n" if $debug;
47             # currently return value is ignored
48             $item->INC($r);
49             }
50             }
51              
52             my $src = $r->src;
53             if (defined $src) {
54             return \$src;
55             } else {
56             die "Can't locate $filename in \@INC";
57             }
58             };
59              
60             sub import {
61 12     12   1201 my $class = shift;
62              
63             # get early options first (-debug)
64             {
65 12         24 my $i = -1;
  12         23  
66 12         41 while ($i < @_) {
67 13         23 $i++;
68 13 50       76 if ($_[$i] eq '-debug') {
    100          
69 0         0 $debug = $_[$i+1];
70 0         0 $i++;
71 0         0 next;
72             } elsif ($_[$i] =~ /\A-/) {
73 1         2 $i++;
74 1         4 next;
75             } else {
76 12         44 last;
77             }
78             }
79             }
80              
81 12 50       35 warn "[Require::HookChain] (Re-)installing our own hook at the beginning of \@INC ...\n"
82             if $debug;
83 12 100 66     52 unless (@INC && blessed($INC[0]) && $INC[0] == $our_hook) {
      66        
84 11   33     25 @INC = ($our_hook, grep { !(blessed($_) && $_ == $our_hook) } @INC);
  121         183  
85             }
86              
87             # get the rest of the options and hook
88 12         33 my $end;
89 12         37 while (@_) {
90 13         27 my $el = shift @_;
91 13 100       46 if ($el eq '-end') {
    50          
92 1         1 $end = shift @_;
93 1         3 next;
94             } elsif ($el eq '-debug') {
95             # we've processed this
96 0         0 shift @_;
97 0         0 next;
98             } else {
99 12         36 my $pkg = "Require::HookChain::$el";
100 12         67 (my $pkg_pm = "$pkg.pm") =~ s!::!/!g;
101 12 0       37 warn "[Require::HookChain] Installing hook $el to the ".($end ? "end":"beginning")." of \@INC, args (".join(",", @_).") ...\n"
    50          
102             if $debug;
103 12         108 require $pkg_pm;
104 12         3219 my $c_hook = $pkg->new(@_);
105 12 100       118 if ($end) {
106 1         3 push @INC, $c_hook;
107             } else {
108             # install the hook after uss
109 11         29 splice @INC, 1, 0, $c_hook;
110             }
111 12         41 last;
112             }
113             }
114             }
115              
116             package Require::HookChain::r;
117              
118             sub new {
119 25     25   80 my ($class, %args) = @_;
120 25         66 bless \%args, $class;
121             }
122              
123             sub filename {
124 6     6   76 my $self = shift;
125 6         72 $self->{filename};
126             }
127              
128             sub src {
129 302     302   464 my $self = shift;
130 302 100       505 if (@_) {
131 24         48 my $old = $self->{src};
132 24         48 $self->{src} = shift;
133 24         55 return $old;
134             } else {
135 278         715 return $self->{src};
136             }
137             }
138              
139             1;
140             # ABSTRACT: Chainable require hooks
141              
142             __END__