File Coverage

blib/lib/Dist/Zilla/Plugin/MetaProvides/Package.pm
Criterion Covered Total %
statement 128 132 96.9
branch 34 38 89.4
condition 2 3 66.6
subroutine 22 22 100.0
pod 1 1 100.0
total 187 196 95.4


line stmt bran cond sub pod time code
1 9     9   14134819 use 5.008; # open scalar
  9         1618  
2 9     9   36 use strict;
  9         1631  
  9         197  
3 9     9   1886 use warnings;
  9         11  
  9         2218  
4              
5             package Dist::Zilla::Plugin::MetaProvides::Package;
6              
7             our $VERSION = '2.004001'; # TRIAL
8              
9             # ABSTRACT: Extract namespaces/version from traditional packages for provides
10              
11             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
12              
13 9     9   34 use Carp qw( croak );
  9         10  
  9         583  
14 9     9   507 use Moose qw( with has around );
  9         284184  
  9         54  
15 9     9   35705 use MooseX::LazyRequire;
  9         15659  
  9         68  
16 9     9   24243 use MooseX::Types::Moose qw( HashRef Str );
  9         32544  
  9         81  
17 9     9   35155 use Dist::Zilla::MetaProvides::ProvideRecord 1.14000000;
  9         550940  
  9         384  
18 9     9   5287 use Data::Dump 1.16 ();
  9         33822  
  9         229  
19 9     9   3496 use Safe::Isa;
  9         2930  
  9         1029  
20              
21              
22              
23              
24              
25              
26              
27              
28              
29              
30              
31              
32              
33              
34              
35              
36              
37              
38 9     9   45 use namespace::autoclean;
  9         14  
  9         78  
39             with 'Dist::Zilla::Role::MetaProvider::Provider';
40             with 'Dist::Zilla::Role::PPI';
41             with 'Dist::Zilla::Role::ModuleMetadata';
42              
43             has '+meta_noindex' => ( default => sub { 1 } );
44              
45              
46              
47              
48              
49              
50              
51              
52              
53              
54              
55             sub provides {
56 27     27 1 497600 my $self = shift;
57 27         50 my (@records);
58 27         37 for my $file ( @{ $self->_found_files() } ) {
  27         94  
59 25         130 push @records, $self->_packages_for($file);
60             }
61 24         123 return $self->_apply_meta_noindex(@records);
62             }
63              
64             has '_package_blacklist' => (
65             isa => HashRef [Str],
66             traits => [ 'Hash', ],
67             is => 'rw',
68             default => sub {
69             return { map { $_ => 1 } qw( main DB ) };
70             },
71             handles => { _blacklist_contains => 'exists', },
72             );
73              
74             # ->_packages_for( file ) => List[Dist::Zilla::MetaProvides::ProvideRecord]
75             sub _packages_for {
76 25     25   43 my ( $self, $file ) = @_;
77              
78 25 100       109 if ( not $file->$_does('Dist::Zilla::Role::File') ) {
79 1         151 $self->log_fatal('API Usage Invalid: _packages_for() takes only a file object');
80 0         0 croak('packages_for() takes only a file object');
81             }
82              
83 24         1715 my $meta = $self->module_metadata_for_file($file);
84 24 50       21900 return unless $meta;
85              
86             $self->log_debug(
87             'Version metadata from ' . $file->name . ' : ' . Data::Dump::dumpf(
88             $meta,
89             sub {
90 296 100   296   33031 if ( $_[1]->$_isa('version') ) {
91 6         76 return { dump => $_[1]->stringify };
92             }
93 290         1952 return { hide_keys => ['pod_headings'], };
94             },
95 24         76 ),
96             );
97              
98             ## no critic (ProhibitArrayAssignARef)
99 24         10403 my @out;
100              
101 24         38 my $seen_blacklisted = {};
102 24         35 my $seen = {};
103              
104 24         132 for my $namespace ( $meta->packages_inside() ) {
105 53 100       3695 if ( $self->_blacklist_contains($namespace) ) {
106              
107             # note: these ones don't count as namespaces
108             # at all for "did you forget a namespace" purposes
109 24         112 $self->log_debug( "Skipping bad namespace: $namespace in " . $file->name );
110 24         6077 next;
111             }
112              
113 29 100       155 if ( not $self->_can_index($namespace) ) {
114              
115             # These count for "You had a namespace but you hid it"
116 10         43 $self->log_debug( "Skipping private(underscore) namespace: $namespace in " . $file->name );
117 10         2479 $seen_blacklisted->{$namespace} = 1;
118 10         18 $seen->{$namespace} = 1;
119 10         22 next;
120             }
121              
122 19         71 my $v = $meta->version($namespace);
123              
124 19 100       201 my (%struct) = (
125             module => $namespace,
126             file => $file->name,
127             ( ref $v ? ( version => $v->stringify ) : ( version => undef ) ),
128             parent => $self,
129             );
130              
131             $self->log_debug(
132             'Version metadata for namespace ' . $namespace . ' in ' . $file->name . ' : ' . Data::Dump::dumpf(
133             \%struct,
134             sub {
135 76     76   4690 return { hide_keys => ['parent'] };
136             },
137 19         808 ),
138             );
139 19         5528 $seen->{$namespace} = 1;
140 19         696 push @out, Dist::Zilla::MetaProvides::ProvideRecord->new(%struct);
141             }
142 24         1698 for my $namespace ( @{ $self->_all_packages_for($file) } ) {
  24         103  
143 48 100       7100 next if $seen->{$namespace};
144 19         109 $self->log_debug("Found hidden namespace: $namespace");
145 19         5115 $seen_blacklisted->{$namespace} = 1;
146             }
147              
148 24 100       443 if ( not @out ) {
149 8 100       70 if ( not keys %{$seen_blacklisted} ) {
  8         30  
150 2         8 $self->log( 'No namespaces detected in file ' . $file->name );
151             }
152             else {
153 6         25 $self->log_debug( 'Only hidden namespaces detected in file ' . $file->name );
154             }
155 8         2107 return ();
156             }
157 16         187 return @out;
158             }
159              
160              
161              
162              
163              
164              
165              
166              
167              
168              
169              
170              
171              
172              
173              
174             has 'include_underscores' => ( is => 'ro', lazy => 1, default => sub { 0 } );
175              
176             sub _can_index {
177 29     29   155 my ( $self, $namespace ) = @_;
178 29 100       1021 return 1 if $self->include_underscores;
179             ## no critic (RegularExpressions::RequireLineBoundaryMatching)
180 23 50       149 return if $namespace =~ qr/\A_/sx;
181 23 100       131 return if $namespace =~ qr/::_/sx;
182 13         36 return 1;
183             }
184              
185             sub _all_packages_for {
186 24     24   29 my ( $self, $file ) = @_;
187 24         3400 require PPI::Document;
188 24         599971 my $document = $self->ppi_document_for_file($file);
189 24         54632 my $packages = $document->find('PPI::Statement::Package');
190 24 100       35682 return [] unless ref $packages;
191 22         30 return [ map { $_->namespace } @{$packages} ];
  48         776  
  22         48  
192             }
193              
194             around dump_config => sub {
195             my ( $orig, $self, @args ) = @_;
196             my $config = $orig->( $self, @args );
197             my $payload = $config->{ +__PACKAGE__ } = {};
198              
199             $payload->{finder} = $self->finder if $self->has_finder;
200             $payload->{include_underscores} = $self->include_underscores;
201              
202             for my $plugin ( @{ $self->_finder_objects } ) {
203             my $object_config = {};
204             $object_config->{class} = $plugin->meta->name if $plugin->can('meta') and $plugin->meta->can('name');
205             $object_config->{name} = $plugin->plugin_name if $plugin->can('plugin_name');
206             $object_config->{version} = $plugin->VERSION if $plugin->can('VERSION');
207             if ( $plugin->can('dump_config') ) {
208             my $finder_config = $plugin->dump_config;
209             $object_config->{config} = $finder_config if keys %{$finder_config};
210             }
211             push @{ $payload->{finder_objects} }, $object_config;
212             }
213              
214             # Inject only when inherited.
215             $payload->{ q[$] . __PACKAGE__ . '::VERSION' } = $VERSION unless __PACKAGE__ eq ref $self;
216             return $config;
217             };
218              
219              
220              
221              
222              
223              
224              
225              
226              
227              
228              
229              
230              
231              
232              
233              
234              
235             has finder => (
236             isa => 'ArrayRef[Str]',
237             is => ro =>,
238             lazy_required => 1,
239             predicate => has_finder =>,
240             );
241              
242             has _finder_objects => (
243             isa => 'ArrayRef',
244             is => ro =>,
245             lazy => 1,
246             init_arg => undef,
247             builder => _build_finder_objects =>,
248             );
249              
250             sub _vivify_installmodules_pm_finder {
251 6     6   11 my ($self) = @_;
252 6         165 my $name = $self->plugin_name;
253 6         47 $name .= '/AUTOVIV/:InstallModulesPM';
254 6 50       155 if ( my $plugin = $self->zilla->plugin_named($name) ) {
255 0         0 return $plugin;
256             }
257 6         1989 require Dist::Zilla::Plugin::FinderCode;
258             my $plugin = Dist::Zilla::Plugin::FinderCode->new(
259             {
260             plugin_name => $name,
261             zilla => $self->zilla,
262             style => 'grep',
263             code => sub {
264 42     42   2841 my ( $file, $self ) = @_;
265 42         140 local $_ = $file->name;
266             ## no critic (RegularExpressions)
267 42 100 66     1768 return 1 if m{\Alib/} and m{\.(pm)$};
268 21 50       467 return 1 if $_ eq $self->zilla->main_module;
269 21         9302 return;
270             },
271             },
272 6         154 );
273 6         598 push @{ $self->zilla->plugins }, $plugin;
  6         160  
274 6         327 return $plugin;
275             }
276              
277             sub _build_finder_objects {
278 10     10   18 my ($self) = @_;
279 10 100       369 if ( $self->has_finder ) {
280 4         6 my @out;
281 4         7 for my $finder ( @{ $self->finder } ) {
  4         126  
282 4         97 my $plugin = $self->zilla->plugin_named($finder);
283 4 100       534 if ( not $plugin ) {
284 1         8 $self->log_fatal("no plugin named $finder found");
285 0         0 croak("no plugin named $finder found");
286             }
287 3 100       14 if ( not $plugin->does('Dist::Zilla::Role::FileFinder') ) {
288 1         41 $self->log_fatal("plugin $finder is not a FileFinder");
289 0         0 croak("plugin $finder is not a FileFinder");
290             }
291 2         207 push @out, $plugin;
292             }
293 2         67 return \@out;
294             }
295 6         24 return [ $self->_vivify_installmodules_pm_finder ];
296             }
297              
298             sub _found_files {
299 27     27   38 my ($self) = @_;
300 27         38 my %by_name;
301 27         36 for my $plugin ( @{ $self->_finder_objects } ) {
  27         1094  
302 25         31 for my $file ( @{ $plugin->find_files } ) {
  25         111  
303 25         737 $by_name{ $file->name } = $file;
304             }
305             }
306 25         850 return [ values %by_name ];
307             }
308              
309             around mvp_multivalue_args => sub {
310             my ( $orig, $self, @rest ) = @_;
311             return ( 'finder', $self->$orig(@rest) );
312             };
313              
314             __PACKAGE__->meta->make_immutable;
315 9     9   11035 no Moose;
  9         13  
  9         68  
316             1;
317              
318             __END__
319              
320             =pod
321              
322             =encoding UTF-8
323              
324             =head1 NAME
325              
326             Dist::Zilla::Plugin::MetaProvides::Package - Extract namespaces/version from traditional packages for provides
327              
328             =head1 VERSION
329              
330             version 2.004001
331              
332             =head1 SYNOPSIS
333              
334             In your C<dist.ini>:
335              
336             [MetaProvides::Package]
337              
338             ; This is the (optional) default: This forces any package versions
339             ; added in the "provides" metadata to use the 'version'
340             ; specified by dzil.
341             ;
342             ; Set it to 0 to force packages own versions to be respected. ( You probably don't want this )
343             inherit_version = 1
344              
345             ; This is also the (optional) default: This forces any package without
346             ; a version declaration to use the 'version' specified by default.
347             ;
348             ; Set it to 0 to allow packages to have no versions
349             inherit_missing = 1
350              
351             ; This is the (optional) default: This being true discovers any [MetaNoIndex]
352             ; plugins to also further exclude packages from the provides map.
353             ;
354             ; Set it to 0 if for some weird reason you don't want this.
355             meta_noindex = 1
356              
357             ; This is the (optional) default: Setting this to true will enable indexing
358             ; of packages like _Foo::Bar or Foo::_Bar
359             include_underscores = 1
360              
361             =head1 DESCRIPTION
362              
363             This is a L<< C<Dist::Zilla>|Dist::Zilla >> Plugin that populates the C<provides>
364             property of C<META.json> and C<META.yml> by absorbing it from your shipped modules,
365             in a manner similar to how C<PAUSE> itself does it.
366              
367             This allows you to easily create an authoritative index of what module provides what
368             version in advance of C<PAUSE> indexing it, which C<PAUSE> in turn will take verbatim.
369              
370             =head1 CONSUMED ROLES
371              
372             =head2 L<Dist::Zilla::Role::MetaProvider::Provider>
373              
374             =head1 ROLE SATISFYING METHODS
375              
376             =head2 C<provides>
377              
378             A conformant function to the L<Dist::Zilla::Role::MetaProvider::Provider> Role.
379              
380             =head3 signature: $plugin->provides()
381              
382             =head3 returns: Array of L<Dist::Zilla::MetaProvides::ProvideRecord>
383              
384             =head1 ATTRIBUTES
385              
386             =head2 C<include_underscores>
387              
388             This attribute controls automatic skipping of packages.
389              
390             By default, packages matching the following regular expression are skipped:
391              
392             qr/(\A|::)_/
393              
394             And this skips all packages with a leading C<_> at any token.
395              
396             This feature was added in C<2.004001-TRIAL>
397              
398             =head2 C<finder>
399              
400             This attribute, if specified will
401              
402             =over 4
403              
404             =item * Override the C<FileFinder> used to find files containing packages
405              
406             =item * Inhibit autovivification of the C<.pm> file finder
407              
408             =back
409              
410             This parameter may be specified multiple times to aggregate a list of finders
411              
412             =begin MetaPOD::JSON v1.1.0
413              
414             {
415             "namespace":"Dist::Zilla::Plugin::MetaProvides::Package",
416             "interface":"class",
417             "inherits":"Moose::Object",
418             "does":"Dist::Zilla::Role::MetaProvider::Provider"
419             }
420              
421              
422             =end MetaPOD::JSON
423              
424             =head1 OPTIONS INHERITED FROM L<Dist::Zilla::Role::MetaProvider::Provider>
425              
426             =head2 L<< C<inherit_version>|Dist::Zilla::Role::MetaProvider::Provider/inherit_version >>
427              
428             How do you want existing versions ( Versions hard-coded into files before running this plug-in )to be processed?
429              
430             =over 4
431              
432             =item * DEFAULT: inherit_version = 1
433              
434             Ignore anything you find in a file, and just probe C<< DZIL->version() >> for a value. This is a sane default and most will want this.
435              
436             =item * inherit_version = 0
437              
438             Use this option if you actually want to use hard-coded values in your files and use the versions parsed out of them.
439              
440             =back
441              
442             =head2 L<< C<inherit_missing>|Dist::Zilla::Role::MetaProvider::Provider/inherit_missing >>
443              
444             In the event you are using the aforementioned C<< L</inherit_version> = 0 >>, this determines how to behave when encountering a
445             module with no version defined.
446              
447             =over 4
448              
449             =item * DEFAULT: inherit_missing = 1
450              
451             When a module has no version, probe C<< DZIL->version() >> for an answer. This is what you want if you want to have some
452             files with fixed versions, and others to just automatically be maintained by Dist::Zilla.
453              
454             =item * inherit_missing = 0
455              
456             When a module has no version, emit a versionless record in the final metadata.
457              
458             =back
459              
460             =head2 L<< C<meta_noindex>|Dist::Zilla::Role::MetaProvider::Provider/meta_noindex >>
461              
462             This is a utility for people who are also using L<< C<MetaNoIndex>|Dist::Zilla::Plugin::MetaNoIndex >>,
463             so that its settings can be used to eliminate items from the 'provides' list.
464              
465             =over 4
466              
467             =item * meta_noindex = 0
468              
469             With this set, any C<MetaNoIndex> plugins are ignored.
470              
471             =item * DEFAULT: meta_noindex = 1
472              
473             When a module meets the criteria provided to L<< C<MetaNoIndex>|Dist::Zilla::Plugin::MetaNoIndex >>,
474             eliminate it from the metadata shipped to L<Dist::Zilla>.
475              
476             =back
477              
478             =head1 SEE ALSO
479              
480             =over 4
481              
482             =item * L<Dist::Zilla::Plugin::MetaProvides>
483              
484             =back
485              
486             =head1 AUTHOR
487              
488             Kent Fredric <kentnl@cpan.org>
489              
490             =head1 COPYRIGHT AND LICENSE
491              
492             This software is copyright (c) 2016 by Kent Fredric <kentfredric@gmail.com>.
493              
494             This is free software; you can redistribute it and/or modify it under
495             the same terms as the Perl 5 programming language system itself.
496              
497             =cut