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-12-05'; # DATE
6             our $DIST = 'Require-HookChain'; # DIST
7             our $VERSION = '0.016'; # 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 100 100   100   166 *blessed = sub { my $arg = shift; my $ref = ref $arg; $ref && $ref !~ /\A(SCALAR|ARRAY|HASH|GLOB|Regexp)\z/ };
  100         146  
  100         455  
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   946 my $class = shift;
62              
63             # get early options first (-debug)
64             {
65 12         25 my $i = -1;
  12         27  
66 12         51 while ($i < @_) {
67 13         26 $i++;
68 13 50       85 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         3 $i++;
74 1         3 next;
75             } else {
76 12         34 last;
77             }
78             }
79             }
80              
81 12 50       39 warn "[Require::HookChain] (Re-)installing our own hook at the beginning of \@INC ...\n"
82             if $debug;
83 12 100 66     61 unless (@INC && blessed($INC[0]) && $INC[0] == $our_hook) {
      66        
84 11   33     32 @INC = ($our_hook, grep { !(blessed($_) && $_ == $our_hook) } @INC);
  88         168  
85             }
86              
87             # get the rest of the options and hook
88 12         28 my $end;
89 12         45 while (@_) {
90 13         29 my $el = shift @_;
91 13 100       53 if ($el eq '-end') {
    50          
92 1         2 $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         28 my $pkg = "Require::HookChain::$el";
100 12         116 (my $pkg_pm = "$pkg.pm") =~ s!::!/!g;
101 12 0       41 warn "[Require::HookChain] Installing hook $el to the ".($end ? "end":"beginning")." of \@INC, args (".join(",", @_).") ...\n"
    50          
102             if $debug;
103 12         185 require $pkg_pm;
104 12         2257 my $c_hook = $pkg->new(@_);
105 12 100       135 if ($end) {
106 1         3 push @INC, $c_hook;
107             } else {
108             # install the hook after uss
109 11         36 splice @INC, 1, 0, $c_hook;
110             }
111 12         57 last;
112             }
113             }
114             }
115              
116             package Require::HookChain::r;
117              
118             sub new {
119 24     24   86 my ($class, %args) = @_;
120 24         86 bless \%args, $class;
121             }
122              
123             sub filename {
124 6     6   100 my $self = shift;
125 6         87 $self->{filename};
126             }
127              
128             sub src {
129 223     223   401 my $self = shift;
130 223 100       453 if (@_) {
131 23         56 my $old = $self->{src};
132 23         64 $self->{src} = shift;
133 23         61 return $old;
134             } else {
135 200         643 return $self->{src};
136             }
137             }
138              
139             1;
140             # ABSTRACT: Chainable require hooks
141              
142             __END__