File Coverage

blib/lib/Catmandu/Importer/Modules.pm
Criterion Covered Total %
statement 24 24 100.0
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 32 32 100.0


line stmt bran cond sub pod time code
1              
2             use Catmandu::Sane;
3 2     2   930  
  2         6  
  2         13  
4             our $VERSION = '1.2018';
5              
6             use Module::Info;
7 2     2   414 use File::Spec;
  2         5576  
  2         45  
8 2     2   9 use Path::Iterator::Rule;
  2         3  
  2         55  
9 2     2   1616 use Moo;
  2         16546  
  2         53  
10 2     2   12 use Catmandu::Util qw(array_split pod_section read_file);
  2         5  
  2         11  
11 2     2   647 use namespace::clean;
  2         4  
  2         117  
12 2     2   14  
  2         4  
  2         25  
13             with 'Catmandu::Importer';
14              
15             has inc => (
16             is => 'ro',
17             lazy => 1,
18             default => sub {[@INC]},
19             coerce => \&array_split,
20             );
21              
22             has namespace =>
23             (is => 'ro', default => sub {[""]}, coerce => \&array_split,);
24              
25             has max_depth => (is => 'ro', predicate => 1,);
26              
27             has pattern => (is => 'ro',);
28              
29             has primary => (is => 'ro',);
30              
31             has about => (is => 'ro', default => sub {1});
32              
33             my ($self) = @_;
34              
35             sub {
36             state $pattern = $self->pattern;
37             state $files = {};
38             state $names = {};
39              
40             # array of [ $directory => $namespace ]
41             state $search = [
42             map {
43             my $ns = $_;
44             my $parts = [map {grep length, split(/::/, $_)} $ns];
45             map {[File::Spec->catdir($_, @$parts) => $ns]} @{$self->inc};
46             } @{$self->namespace}
47             ];
48              
49             state $cur = shift(@$search) // return;
50              
51             state $iter = do {
52             my $rule = Path::Iterator::Rule->new;
53             $rule->file->name('*.pm');
54             $rule->max_depth($self->max_depth) if $self->has_max_depth;
55             $rule->iter($cur->[0], {depthfirst => 1});
56             };
57              
58             while (1) {
59             my ($dir, $ns) = @$cur;
60              
61             if (defined(my $file = $iter->())) {
62             my $path = File::Spec->abs2rel($file, $dir);
63             my $name = join('::', File::Spec->splitdir($path));
64             $name =~ s/\.pm$//;
65             $name = join('::', $ns, $name) if $ns;
66              
67             next if defined $pattern && $name !~ $pattern;
68              
69             my $info = Module::Info->new_from_file($file);
70             my $file = File::Spec->rel2abs($file);
71              
72             next if $files->{$file};
73             $files->{$file} = 1;
74              
75             if ($self->primary) {
76             next if $names->{$name};
77             $names->{$name} = 1;
78             }
79              
80             my $data = {file => $file, name => $name, path => $dir,};
81              
82             if (defined $info->version && $info->version ne 'undef') {
83             $data->{version} = "" . $info->version;
84 2     2   1469 }
  2         4  
  2         12  
85             elsif (open(my $fh, '<:encoding(UTF-8)', $file)) {
86             while (my $line = <$fh>) {
87             if (my ($version)
88             = $line
89             =~ /^\s*our\s+\$VERSION\s*=\s*['"]([^'"]+)['"]\s*;/
90             )
91             {
92             $data->{version} = $version;
93             last;
94             }
95             }
96             close($fh);
97             }
98              
99             if ($self->about) {
100             my $about = pod_section($file, 'NAME');
101             $about =~ s/[^-]+(\s*-?\s*)?//;
102             $about =~ s/\n/ /mg;
103             $about =~ s/ *$//;
104             $data->{about} = $about if $about ne '';
105             }
106              
107             return $data;
108             }
109             else {
110             $cur = shift(@$search) // return;
111             my $rule = Path::Iterator::Rule->new;
112             $rule->file->name('*.pm');
113             $rule->max_depth($self->max_depth) if $self->has_max_depth;
114             $iter = $rule->iter($cur->[0], {depthfirst => 1});
115             }
116             }
117             };
118             }
119              
120             1;
121              
122              
123             =pod
124              
125             =head1 NAME
126              
127             Catmandu::Importer::Modules - list installed perl modules in a given namespace
128              
129             =head1 DESCRIPTION
130              
131             This L<Catmandu::Importer> list perl modules from all perl library paths with
132             their C<name>, C<version>, absolute C<file>, library C<path>, and short
133             description (C<about>).
134              
135             =head1 CONFIGURATION
136              
137             =over
138              
139             =item file
140              
141             Read input from a local file given by its path. Alternatively a scalar
142             reference can be passed to read from a string.
143              
144             =item fh
145              
146             Read input from an L<IO::Handle>. If not specified, L<Catmandu::Util::io> is used to
147             create the input stream from the C<file> argument or by using STDIN.
148              
149             =item encoding
150              
151             Binmode of the input stream C<fh>. Set to C<:utf8> by default.
152              
153             =item fix
154              
155             An ARRAY of one or more fixes or file scripts to be applied to imported items.
156              
157             =item namespace
158              
159             Namespace(s) for the modules to list, given as array or comma-separated list
160              
161             =item inc
162              
163             List of library paths (defaults to C<@INC>)
164              
165             =item max_depth
166              
167             Maximum depth to recurse into the namespace e.g. if the namespace is
168             Catmandu::Fix then Catmandu::Fix::add_field has a depth of 1 and
169             Catmandu::Fix::Condition::exists a depth of 2
170              
171             =item pattern
172              
173             Filter modules by the given regex pattern
174              
175             =item primary
176              
177             Filter modules to the first module of each name
178              
179             =item about
180              
181             Include short description as given in the NAME section of each module's
182             documentation. Enabled by default.
183              
184             =back
185              
186             =head1 METHODS
187              
188             Every L<Catmandu::Importer> is a L<Catmandu::Iterable> all its methods are
189             inherited.
190              
191             =head1 SEE ALSO
192              
193             L<Catmandu::Importer::CPAN>, L<Catmandu::Cmd::info>
194              
195             =cut