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