File Coverage

blib/lib/Dist/Zilla/Plugin/Prereqs/Recommend/MatchInstalled.pm
Criterion Covered Total %
statement 87 91 95.6
branch 10 18 55.5
condition 3 5 60.0
subroutine 20 20 100.0
pod 0 3 0.0
total 120 137 87.5


line stmt bran cond sub pod time code
1 2     2   2182137 use 5.008; # utf8
  2         5  
2 2     2   8 use strict;
  2         2  
  2         39  
3 2     2   14 use warnings;
  2         2  
  2         52  
4 2     2   504 use utf8;
  2         10  
  2         12  
5              
6             package Dist::Zilla::Plugin::Prereqs::Recommend::MatchInstalled;
7              
8             our $VERSION = '0.003002';
9              
10             # ABSTRACT: Advertise versions of things you have as soft dependencies
11              
12             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
13              
14 2     2   560 use Moose qw( with has around );
  2         283702  
  2         10  
15 2     2   8513 use MooseX::Types::Moose qw( HashRef ArrayRef Str );
  2         37835  
  2         18  
16 2     2   7649 use Dist::Zilla::Util::ConfigDumper qw( config_dumper );
  2         1691  
  2         8  
17             with 'Dist::Zilla::Role::PrereqSource';
18              
19              
20              
21              
22              
23              
24              
25              
26              
27              
28              
29              
30              
31              
32              
33              
34              
35             has 'applyto_phase' => (
36             is => ro =>,
37             isa => ArrayRef [Str] =>,
38             lazy => 1,
39             default => sub { [qw(build test runtime configure develop)] },
40             );
41              
42              
43              
44              
45              
46              
47              
48              
49              
50              
51              
52              
53              
54              
55              
56              
57              
58              
59              
60              
61             has 'source_relation' => (
62             is => ro =>,
63             isa => Str,
64             lazy => 1,
65             default => sub { 'requires' },
66             );
67              
68              
69              
70              
71              
72              
73              
74              
75              
76              
77              
78              
79              
80              
81              
82              
83              
84              
85              
86              
87              
88             has 'target_relation' => (
89             is => ro =>,
90             isa => Str =>,
91             lazy => 1,
92             default => sub { 'recommends' },
93             );
94              
95              
96              
97              
98              
99              
100              
101              
102              
103              
104              
105              
106              
107              
108              
109              
110              
111              
112              
113              
114              
115             has 'applyto_map' => (
116             is => ro =>,
117             isa => ArrayRef [Str] =>,
118             lazy => 1,
119             builder => _build_applyto_map =>,
120             );
121              
122             sub _mk_phase_entry {
123 5     5   6 my ( $self, $phase ) = @_;
124 5         179 return sprintf q[%s.%s = %s.%s], $phase, $self->source_relation, $phase, $self->target_relation;
125             }
126              
127             sub _build_applyto_map {
128 1     1   2 my ($self) = @_;
129 1         1 my @out;
130 1         1 for my $phase ( @{ $self->applyto_phase } ) {
  1         40  
131 5         10 push @out, $self->_mk_phase_entry($phase);
132             }
133 1         36 return \@out;
134             }
135              
136             has '_applyto_map_hash' => (
137             is => ro =>,
138             isa => ArrayRef [HashRef] =>,
139             lazy => 1,
140             builder => _build__applyto_map_hash =>,
141             );
142              
143             # _Pulp__5010_qr_m_propagate_properly
144             ## no critic (Compatibility::PerlMinimumVersionAndWhy)
145             my $re_phase = qr/configure|build|runtime|test|develop/msx;
146             my $re_relation = qr/requires|recommends|suggests|conflicts/msx;
147              
148             my $combo = qr/(?:$re_phase)[.](?:$re_relation)/msx;
149              
150             sub _parse_map_token {
151 10     10   6 my ( $self, $token ) = @_;
152 10         9 my ( $phase, $relation );
153 10 50       75 if ( ( $phase, $relation ) = $token =~ /\A($re_phase)[.]($re_relation)/msx ) {
154             return {
155 10         33 phase => $phase,
156             relation => $relation,
157             };
158             }
159 0         0 return $self->log_fatal( [ '%s is not in the form <phase.relation>', $token ] );
160              
161             }
162              
163             sub _parse_map_entry {
164 5     5   5 my ( $self, $entry ) = @_;
165 5         4 my ( $source, $target );
166 5 50       121 if ( ( $source, $target ) = $entry =~ /\A\s*($combo)\s*=\s*($combo)\s*\z/msx ) {
167             return {
168 5         8 source => $self->_parse_map_token($source),
169             target => $self->_parse_map_token($target),
170             };
171             }
172 0         0 return $self->log_fatal( [ '%s is not a valid entry for applyto_map', $entry ] );
173             }
174              
175             sub _build__applyto_map_hash {
176 1     1   1 my ($self) = @_;
177 1         7 my @out;
178 1         2 for my $line ( @{ $self->applyto_map } ) {
  1         40  
179 5         8 push @out, $self->_parse_map_entry($line);
180             }
181 1         67 return \@out;
182             }
183              
184             has 'modules' => (
185             is => ro =>,
186             isa => ArrayRef [Str],
187             lazy => 1,
188             default => sub { [] },
189             );
190              
191             has _modules_hash => (
192             is => ro =>,
193             isa => HashRef,
194             lazy => 1,
195             builder => _build__modules_hash =>,
196             );
197              
198             sub _build__modules_hash {
199 1     1   2 my $self = shift;
200 1         1 return { map { ( $_, 1 ) } @{ $self->modules } };
  1         38  
  1         37  
201             }
202              
203             sub _user_wants_upgrade_on {
204 1     1   2 my ( $self, $module ) = @_;
205 1         46 return exists $self->_modules_hash->{$module};
206             }
207              
208 1     1 0 184 sub mvp_multivalue_args { return qw(applyto_map applyto_phase modules) }
209 1     1 0 122 sub mvp_aliases { return { 'module' => 'modules' } }
210              
211             sub _current_version_of {
212 1     1   3 my ( undef, $package ) = @_;
213 1 50       5 if ( 'perl' eq $package ) {
214              
215             # Thats not going to work, Dave.
216 0         0 return $];
217             }
218 1         509 require Module::Data;
219 1         4084 my $md = Module::Data->new($package);
220 1 50       1046 return if not $md;
221 1 50       4 return if not -e $md->path;
222 1 50       7169 return if -d $md->path;
223 1         50 return $md->_version_emulate;
224             }
225              
226             around dump_config => config_dumper( __PACKAGE__, qw( applyto_phase applyto_map modules source_relation target_relation ) );
227              
228             sub _register_applyto_map_entry {
229 5     5   5 my ( $self, $applyto, $prereqs ) = @_;
230 5         3 my ( $phase, $rel );
231 5         7 $phase = $applyto->{source}->{phase};
232 5         6 $rel = $applyto->{source}->{relation};
233             my $targetspec = {
234             phase => $applyto->{target}->{phase},
235             type => $applyto->{target}->{relation},
236 5         12 };
237 5         18 $self->log_debug( [ 'Processing %s.%s => %s.%s', $phase, $rel, $applyto->{target}->{phase}, $applyto->{target}->{relation} ] );
238 5 100 66     241 if ( not exists $prereqs->{$phase} or not exists $prereqs->{$phase}->{$rel} ) {
239 4         12 $self->log_debug( [ 'Nothing in %s.%s', $phase, $rel ] );
240 4         152 return;
241             }
242 1         5 my $reqs = $prereqs->{$phase}->{$rel}->as_string_hash;
243              
244 1         50 for my $module ( keys %{$reqs} ) {
  1         4  
245 1 50       3 next unless $self->_user_wants_upgrade_on($module);
246 1         4 my $latest = $self->_current_version_of($module);
247 1 50       10502 if ( defined $latest ) {
248 1         37 $self->zilla->register_prereqs( $targetspec, $module, $latest );
249 1         264 next;
250             }
251              
252             $self->log(
253 0         0 [ q[You asked for the installed version of %s,] . q[ and it is a dependency but it is apparently not installed], $module, ],
254             );
255             }
256 1         5 return $self;
257             }
258              
259             sub register_prereqs {
260 1     1 0 69442 my ($self) = @_;
261 1         31 my $zilla = $self->zilla;
262 1         23 my $prereqs = $zilla->prereqs;
263 1   50     25 my $guts = $prereqs->cpan_meta_prereqs->{prereqs} || {};
264              
265 1         7 for my $applyto ( @{ $self->_applyto_map_hash } ) {
  1         65  
266 5         12 $self->_register_applyto_map_entry( $applyto, $guts );
267             }
268 1         4 return $prereqs;
269             }
270              
271              
272              
273              
274              
275              
276              
277              
278              
279             __PACKAGE__->meta->make_immutable;
280 2     2   1826 no Moose;
  2         4  
  2         10  
281              
282             1;
283              
284             __END__
285              
286             =pod
287              
288             =encoding UTF-8
289              
290             =head1 NAME
291              
292             Dist::Zilla::Plugin::Prereqs::Recommend::MatchInstalled - Advertise versions of things you have as soft dependencies
293              
294             =head1 VERSION
295              
296             version 0.003002
297              
298             =head1 SYNOPSIS
299              
300             C<[Prereqs::MatchInstalled]> was a good concept, but its application seemed too strong for some things.
301              
302             This is a variation on the same theme, but instead of upgrading dependencies in-place,
303             it propagates the upgrade to a different relation, to produce a softer dependency map.
304              
305             Below shows the defaults expanded by hand.
306              
307             [Prereqs::Recommend::MatchInstalled]
308             applyto_phase = configure
309             applyto_phase = runtime
310             applyto_phase = test
311             applyto_phase = build
312             applyto_phase = develop
313             source_relation = requires
314             target_relation = recommends
315              
316             And add these stanzas for example:
317              
318             modules = Module::Build
319             modules = Moose
320              
321             And you have yourself a distribution that won't needlessly increase the dependencies
322             on either, but will add increased dependencies to the C<recommends> phase.
323              
324             This way, people doing
325              
326             cpanm YourModule
327              
328             Get only what they I<need>
329              
330             While
331              
332             cpanm --with-recommends YourModule
333              
334             Will get more recent things upgraded
335              
336             =head1 ATTRIBUTES
337              
338             =head2 C<applyto_phase>
339              
340             [Prereqs::Recommend::MatchInstalled]
341             applyto_phase = SOMEPHASE
342             applyto_phase = SOMEPHASE
343              
344             This attribute can be specified multiple times.
345              
346             Valuable values are:
347              
348             build test runtime configure develop
349              
350             And those are the default values too.
351              
352             =head2 C<source_relation>
353              
354             [Prereqs::Recommend::MatchInstalled]
355             source_relation = requires
356              
357             This attribute specifies the prerequisites to skim for modules to recommend upgrades on.
358              
359             Valuable values are:
360              
361             requires recommends suggests
362              
363             Lastly:
364              
365             conflicts
366              
367             Will probably do I<something>, but I have no idea if that means anything. If you want to conflict with what you've installed with, ... go right ahead.
368              
369             =head2 C<target_relation>
370              
371             [Prereqs::Recommend::MatchInstalled]
372             target_relation = recommends
373              
374             This attribute specifies the relationship type to inject upgrades into.
375              
376             Valuable values are:
377              
378             requires recommends suggests
379              
380             Lastly:
381              
382             conflicts
383              
384             Will probably do I<something>, but I have no idea if that means anything. If you want to conflict with what you've installed
385             with, ... go right ahead.
386              
387             =head2 C<applyto_map>
388              
389             [Prereqs::Recommend::MatchInstalled]
390             applyto_map = runtime.requires = runtime.recommends
391              
392             This attribute is the advanced internals of the other attributes, and it exists for insane, advanced, and niché applications.
393              
394             General format is:
395              
396             applyto_map = <source_phase>.<source_relation> = <target_phase>.<target_relation>
397              
398             And you can probably do everything with this.
399              
400             You could also conceivably emulate C<[Prereqs::MatchInstalled]> in entirety by using this feature excessively.
401              
402             C<applyto_map> may be declared multiple times.
403              
404             =for Pod::Coverage mvp_aliases mvp_multivalue_args register_prereqs
405              
406             =head1 AUTHOR
407              
408             Kent Fredric <kentnl@cpan.org>
409              
410             =head1 COPYRIGHT AND LICENSE
411              
412             This software is copyright (c) 2017 by Kent Fredric <kentfredric@gmail.com>.
413              
414             This is free software; you can redistribute it and/or modify it under
415             the same terms as the Perl 5 programming language system itself.
416              
417             =cut