File Coverage

blib/lib/Module/EnforceLoad.pm
Criterion Covered Total %
statement 79 81 97.5
branch 15 18 83.3
condition 2 3 66.6
subroutine 15 15 100.0
pod 0 3 0.0
total 111 120 92.5


line stmt bran cond sub pod time code
1             package Module::EnforceLoad;
2              
3             my %LOAD_TREE;
4             my %RELOADS;
5             our $DEBUG = 0;
6              
7             BEGIN {
8 1     1   540 our $MOD;
9              
10             sub file_to_mod {
11 621     621 0 546 my $mod = shift;
12 621         1096 $mod =~ s{/}{::}g;
13 621         1511 $mod =~ s{.pm$}{};
14 621         944 return $mod;
15             }
16              
17             *CORE::GLOBAL::require = sub {
18 529     529   86266 my $file = shift;
19 529 100       1861 return CORE::require($file) if $file =~ m/^[0-9\.]+$/;
20              
21 528         685 my $mod = file_to_mod($file);
22              
23 528         805 my @stack = ($mod);
24 528         965 while (my $m = shift @stack) {
25 956         1025 $RELOADS{$m}++;
26 956         721 push @stack => keys %{$LOAD_TREE{$m}};
  956         2988  
27             }
28 528         704 $LOAD_TREE{$mod} = {};
29 528 100       1431 $LOAD_TREE{$MOD}->{$mod} = $LOAD_TREE{$mod} if $MOD;
30 528         608 local $MOD = $mod;
31 528         45649 CORE::require($file);
32 1         31 };
33             }
34              
35 1     1   2 use strict;
  1         1  
  1         20  
36 1     1   1 use warnings;
  1         1  
  1         34  
37 1     1   1 use Sub::Util qw/prototype set_prototype subname/;
  1         50  
  1         54  
38 1     1   2 use List::Util qw/first/;
  1         0  
  1         102  
39              
40             our $VERSION = '0.000002';
41              
42             our $ENFORCE = 0;
43             my %OVERRIDE = (
44             __PACKAGE__, 1,
45             'UNIVERSAL' => 1,
46             'CORE' => 1,
47             'CORE::GLOBAL' => 1,
48             );
49              
50             sub import {
51 1     1   5 my $class = shift;
52 1         2 my $caller = caller;
53 1     1   3 no strict 'refs';
  1         0  
  1         130  
54 1         1 *{"$caller\::enforce"} = \&enforce;
  1         25  
55             }
56              
57             sub enforce {
58 1     1 0 381 %RELOADS = ();
59 1         4 replace_subs(scalar caller);
60 1         33 replace_subs(file_to_mod($_)) for keys %LOAD_TREE;
61 1         18 $ENFORCE = 1;
62             }
63              
64             sub replace_subs {
65 94     94 0 75 my $mod = shift;
66 94 50       194 return if $OVERRIDE{$mod}++;
67 94         78 local $ENFORCE = 0;
68              
69 94         59 my $stash;
70             {
71 1     1   2 no strict 'refs';
  1         1  
  1         246  
  94         61  
72 94         68 $stash = \%{"$mod\::"};
  94         236  
73             }
74              
75 94         766 for my $i (keys %$stash) {
76 3335 100       8913 my $orig = $mod->can($i) or next;
77 2622 50       5696 next if $OVERRIDE{"$mod\::$i"}++;
78 2622 100       3071 next if $i eq 'DESTROY';
79 2616 100       2686 next if $i eq 'can';
80 2580         2066 my $prototype = prototype($orig);
81              
82             my $new = sub {
83 96 100 66 96   1416 if ($ENFORCE && !$RELOADS{$mod}) {
84 1         2 $ENFORCE = 0;
85              
86 1         3 my ($pkg, $file, $line) = caller;
87 1         10 my $name = subname($orig);
88 1         6 my $pname = $name =~ s/::[^:]+$//r;
89 1         4 my $str = "Tried to use $name without loading $pname at $file line $line.\n";
90 1         2 my $l = 1;
91 1         9 while (my @caller = caller($l++)) {
92 1         9 $str .= " $caller[3] called at $caller[1] line $caller[2].\n";
93             }
94              
95 1 50       3 if ($DEBUG) {
96 0         0 require Data::Dumper;
97 0         0 $str .= Data::Dumper::Dumper({
98             LOAD_TREE => \%LOAD_TREE,
99             RELOADS => \%RELOADS,
100             });
101             }
102              
103 1         6 die $str;
104             }
105 95         1594 goto &$orig;
106 2580         7250 };
107 2580         2510 set_prototype($prototype, $new);
108              
109 1     1   2 no strict 'refs';
  1         0  
  1         26  
110 1     1   12 no warnings 'redefine';
  1         1  
  1         73  
111 2580         1431 *{"$mod\::$i"} = $new;
  2580         6320  
112             }
113             }
114              
115             1;
116              
117             __END__