File Coverage

blib/lib/fatfinger.pm
Criterion Covered Total %
statement 71 71 100.0
branch 18 26 69.2
condition 9 15 60.0
subroutine 11 11 100.0
pod n/a
total 109 123 88.6


line stmt bran cond sub pod time code
1 1     1   25576 use strict;
  1         2  
  1         24  
2 1     1   6 use warnings;
  1         1  
  1         43  
3              
4             package fatfinger;
5             $fatfinger::VERSION = '0.000001';
6 1     1   5 use File::Spec;
  1         2  
  1         21  
7 1     1   901 use Path::Iterator::Rule;
  1         11014  
  1         32  
8 1     1   737 use Text::Levenshtein::Damerau qw( edistance );
  1         3513  
  1         825  
9              
10             sub import {
11             push @INC, sub {
12 6     6   5137 shift;
13 6         12 my $name = shift;
14              
15 6 50       22 return if $name eq 'prefork.pm';
16              
17             # Don't recurse through directories if we're called inside an eval
18             # Unfortunately, that's the interferes with our tests, so we'll
19             # allow it when run under a test harness.
20              
21 6 50 66     26 return if !$ENV{FF_HARNESS_ACTIVE} && $^S;
22              
23 6         41 my @caller = caller(1);
24             return
25             if !$ENV{FF_HARNESS_ACTIVE}
26 6 50 33     61 && ( ( $caller[3] && $caller[3] =~ m{eval} )
      66        
27             || ( $caller[1] && $caller[1] =~ m{eval} ) );
28              
29 5         15 my $module = _maybe_find_module_in_INC($name);
30 5   100     3486 $module ||= _maybe_find_module_on_disk($name);
31 5 100       125 return unless $module;
32              
33 4         17 $name =~ s{\.pm\z}{};
34 4         8 $module =~ s{\.pm\z}{};
35              
36 4         14 my $msg = <<"EOF";
37              
38             ----------
39             The module "$name" could not be found. Perhaps you meant to "use $module"?
40             ----------
41              
42             EOF
43 4         83 die $msg;
44 1     1   1887 };
45             }
46              
47             sub _maybe_find_module_in_INC {
48 5     5   7 my $module = shift;
49 5         10 $module =~ s{::}{/}g;
50 5         103 for my $file ( keys %INC ) {
51 349 100       636884 if ( edistance( lc($file), lc($module) ) <= 2 ) {
52 1         539 $file =~ s{/}{::}g;
53 1 50       4 return $file if $file;
54             }
55             }
56             }
57              
58             sub _maybe_find_module_on_disk {
59 4     4   9 my $module = shift;
60              
61 4         49 my $rule = Path::Iterator::Rule->new( depth_first => 1 );
62 4         42 $rule->perl_module;
63              
64 4         783 my @module_parts = File::Spec->splitdir($module);
65 4         8 my $module_depth = @module_parts;
66              
67             # don't iterate over any @INC hooks
68 4         9 my @dirs = grep { !ref $_ } @INC;
  44         90  
69              
70 4         9 foreach my $inc_dir (@dirs) {
71 13         79804 _debug($inc_dir);
72 13         54 my $this_rule = $rule->clone;
73              
74             $this_rule->and(
75             sub {
76 1242     1242   1521107 my $path = shift;
77 1242         2065 my $file = shift;
78              
79 1242         2644 _debug($path);
80              
81 1242 50       3161 return \0 if $file =~ m{\A\.};
82              
83 1242         7137 $path =~ s{^$inc_dir/}{};
84              
85             # top level directory?
86 1242 50       3223 return 0 if $path eq q{};
87              
88 1242         7108 my @path_parts = grep { m{\w} } File::Spec->splitdir($path);
  3355         11420  
89 1242 50 33     7827 shift @path_parts if @path_parts && $path_parts[0] eq 'auto';
90              
91 1242         1980 my $path_depth = @path_parts;
92 1242 100       2856 return \0 if $path_depth > $module_depth;
93 1198 100       4809 return 0 if $path_depth < $module_depth;
94              
95 182         428 my $joined_path = join( '/', @path_parts );
96 182         786 my $distance = edistance( lc($joined_path), lc($module) );
97              
98 182 100       979245 return $distance <= 2 ? \1 : 0;
99             }
100 13         512 );
101              
102 13         266 my $next = $this_rule->iter($inc_dir);
103 13         1405 while ( defined( my $file = $next->() ) ) {
104 3         145 $file =~ s{^$inc_dir/}{}g;
105              
106 3         16 my @parts = grep { m{\w} } File::Spec->splitdir($file);
  6         20  
107 3         7 $file = join '::', @parts;
108 3         11 $file =~ s{\.pm\z}{};
109 3         120 return $file;
110             }
111             }
112             }
113              
114             sub _debug {
115 1255     1255   2110 my $msg = shift;
116 1255 50       4323 print "$msg\n" if $ENV{FF_DEBUG};
117             }
118              
119             1;
120              
121             # ABSTRACT: Catch typos in module names
122              
123             __END__