File Coverage

lib/Submodules.pm
Criterion Covered Total %
statement 93 95 97.8
branch 17 26 65.3
condition 7 12 58.3
subroutine 16 16 100.0
pod 1 4 25.0
total 134 153 87.5


line stmt bran cond sub pod time code
1             package Submodules;
2 2     2   143232 use strict;
  2         15  
  2         57  
3 2     2   10 use warnings;
  2         4  
  2         52  
4 2     2   802 use Submodules::Result ();
  2         5  
  2         41  
5 2     2   12 use File::Spec;
  2         4  
  2         39  
6 2     2   8 use File::Find qw();
  2         3  
  2         25  
7 2     2   7 use Carp;
  2         4  
  2         103  
8 2     2   25 use File::Basename;
  2         3  
  2         266  
9             our $VERSION = '1.0015';
10            
11             my $names;
12            
13             sub import {
14 2     2   20 my $self = shift;
15 2         4 my $pack = caller;
16 2         4 my $name = shift;
17 2 100 66     22 if (defined($name) and length $name) {
18 2     2   13 no strict 'refs';
  2         3  
  2         1934  
19 1 50       1 croak "Cannot export symbol '&${pack}::$name' because it's already defined" if defined &{"${pack}::$name"};
  1         8  
20 1         2 *{"${pack}::$name"} = \&submodules;
  1         4  
21 1         10 $names->{$pack} = $name;
22             }
23             }
24            
25             sub submodules (;*) {
26 2     2 0 915 my @caller = caller(0);
27 2         5 my $name = 'submodules';
28 2 50       7 if (exists $names->{$caller[0]}) {
29 2 50       8 $name = $names->{$caller[0]} if length $names->{$caller[0]};
30             }
31 2         6 my $usage = "Usage:\n $name name::space\n";
32 2 50       6 croak $usage if @_ > 1;
33 2 50       7 if (not defined wantarray) {
34 0         0 carp "Useless call of '$name' in void context";
35 0         0 return;
36             }
37 2         4 my $query = shift;
38 2 100       5 $query = $caller[0] unless defined $query;
39 2         79 my $self = {
40             list => [],
41             caller_abs => File::Spec->rel2abs($caller[1]),
42             query => $query,
43             seen_paths => {},
44             };
45 2         8 bless $self, __PACKAGE__;
46 2         12 my @members = split /(?:::|')/, $query;
47 2         5 my @inc;
48 2         5 for my $i (@INC) {
49 24         211 my $abs = File::Spec->rel2abs($i);
50 24 50       55 unless (exists $self->{seen_paths}->{$abs}) {
51 24         34 push @inc, $abs;
52 24         48 $self->{seen_paths}->{$abs} = 1;
53             }
54             }
55 2         18 for my $i (@inc) {
56 24         188 my $inc_dir = File::Spec->rel2abs($i);
57 24 100       560 if (-f (my $file = File::Spec->catfile($inc_dir, @members).'.pm')) {
58 4         15 $file = _get_abs_path($file);
59 4         8 my $caller = _get_abs_path($caller[1]);
60 4 50       18 $self->process_file($inc_dir, $file) unless $file eq $caller;
61             }
62 24 50 33     62 return $self->{list}->[0] if @{$self->{list}} and not wantarray;
  24         96  
63 24 100       527 if (-d (my $path = File::Spec->catfile($inc_dir, @members))) {
64 4         17 $self->process_path($inc_dir, $path);
65             }
66             }
67 2         5 @{$self->{list}};
  2         31  
68             };
69            
70             sub find {
71 1     1 1 101 my $self = shift;
72 1         5 goto &submodules;
73             }
74            
75             sub process_path {
76 4     4 0 6 my $self = shift;
77 4         8 my $path_abs = shift;
78 4         4 my $path = shift;
79             File::Find::find (
80             {
81             no_chdir => 1,
82             preprocess => sub { # Introduced after seeing the directory contents order behave unexpectedly on some file systems
83 8     8   42 my @items = sort {$a cmp $b} @_;
  52         320  
84             },
85             wanted => sub {
86 28     28   83 $self->process_file($path_abs, $File::Find::name)
87             },
88             },
89 4         342 $path,
90             );
91             }
92            
93             sub process_file {
94 32     32 0 37 my $self = shift;
95 32         36 my $path_abs = shift;
96 32         42 my $file = shift;
97 32 50 66     1144 return unless $file =~ /(\.pm)$/i and -f $file and my $extension = $1;
      66        
98 24         258 my $file_abs = File::Spec->rel2abs($file);
99 24         1381 my $file_rel = File::Spec->abs2rel($file_abs);
100 24         849 my $filename = basename $file_abs;
101 24         109 my @parts_path = File::Spec->splitdir($path_abs);
102 24         81 my @parts = File::Spec->splitdir($file_abs);
103 24         49 splice @parts, 0, scalar @parts_path;
104 24         53 $parts[$#parts] = substr $parts[$#parts], 0, - length $extension;
105 24         49 my $code_path = join '::', @parts;
106 24         47 my $perl_path = join('/', @parts).$extension;
107 24         38 my $name = $parts[$#parts];
108 24         120 push @{$self->{list}}, Submodules::Result->new (
109             Name => $name,
110             AbsPath => $file_abs,
111             RelPath => $file_rel,
112             Path => $perl_path,
113 24         27 Clobber => $self->{seen_paths}->{$name},
114             Module => $code_path,
115             );
116 24         251 $self->{seen_paths}->{$name} = $file_abs;
117             }
118            
119             sub _get_abs_path {
120 8     8   14 my $path = shift;
121 8         40 my @parts = File::Spec->splitdir($path);
122 8         52 my $file = File::Spec->catfile(@parts);
123 8         109 File::Spec->rel2abs($file);
124             }
125            
126             1;
127            
128             __END__