File Coverage

blib/lib/Dist/Zilla/Plugin/Prereqs/Soften.pm
Criterion Covered Total %
statement 92 94 97.8
branch 10 18 55.5
condition n/a
subroutine 16 16 100.0
pod 0 3 0.0
total 118 131 90.0


line stmt bran cond sub pod time code
1 7     7   12993397 use 5.006;
  7         22  
2 7     7   32 use strict;
  7         9  
  7         167  
3 7     7   28 use warnings;
  7         18  
  7         539  
4              
5             package Dist::Zilla::Plugin::Prereqs::Soften;
6              
7             our $VERSION = '0.006003';
8              
9             # ABSTRACT: Downgrade listed dependencies to recommendations if present.
10              
11             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
12              
13 7     7   693 use Moose qw( with has around );
  7         390785  
  7         60  
14 7     7   31265 use MooseX::Types::Moose qw( ArrayRef HashRef Str Bool );
  7         44282  
  7         72  
15             with 'Dist::Zilla::Role::PrereqSource';
16              
17              
18              
19              
20              
21              
22              
23             has 'modules' => (
24             is => ro =>,
25             isa => ArrayRef [Str],
26             lazy => 1,
27             default => sub { [] },
28             );
29              
30              
31              
32              
33              
34              
35              
36              
37              
38              
39              
40              
41              
42              
43              
44              
45              
46              
47              
48 7     7   25509 use Moose::Util::TypeConstraints qw(enum);
  7         12  
  7         65  
49              
50             has 'to_relationship' => (
51             is => ro =>,
52             isa => enum( [qw(none requires recommends suggests conflicts)] ),
53             lazy => 1,
54             default => sub { 'recommends' },
55             );
56              
57 7     7   2248 no Moose::Util::TypeConstraints;
  7         10  
  7         31  
58              
59              
60              
61              
62              
63              
64              
65              
66              
67              
68              
69              
70              
71              
72              
73              
74              
75              
76              
77              
78              
79              
80              
81              
82              
83              
84              
85              
86              
87              
88              
89              
90             has 'copy_to' => (
91             is => 'ro',
92             isa => ArrayRef [Str],
93             lazy => 1,
94             default => sub { [] },
95             );
96              
97             has '_copy_to_extras' => (
98             is => 'ro',
99             isa => ArrayRef [HashRef],
100             lazy => 1,
101             builder => '_build__copy_to_extras',
102             );
103              
104              
105              
106              
107              
108              
109              
110              
111              
112              
113              
114              
115              
116              
117              
118              
119              
120              
121              
122              
123             has 'modules_from_features' => (
124             is => ro =>,
125             isa => Bool,
126             lazy => 1,
127             default => sub { return },
128             );
129              
130             has '_modules_hash' => (
131             is => ro =>,
132             isa => HashRef,
133             lazy => 1,
134             builder => _build__modules_hash =>,
135             );
136              
137             around dump_config => sub {
138             my ( $orig, $self, @args ) = @_;
139             my $config = $self->$orig(@args);
140             my $payload = $config->{ +__PACKAGE__ } = {};
141              
142             $payload->{modules} = $self->modules;
143             $payload->{to_relationship} = $self->to_relationship;
144             $payload->{copy_to} = $self->copy_to;
145             $payload->{modules_from_features} = $self->modules_from_features;
146              
147             # inject $VERSION when subclassed
148             $payload->{ q[$] . __PACKAGE__ . q[::VERSION] } = $VERSION unless __PACKAGE__ eq ref $self;
149             return $config;
150             };
151              
152             __PACKAGE__->meta->make_immutable;
153 7     7   2178 no Moose;
  7         9  
  7         30  
154              
155 6     6 0 1056 sub mvp_multivalue_args { return qw(modules copy_to) }
156 6     6 0 777 sub mvp_aliases { return { 'module' => 'modules' } }
157              
158             sub _build__copy_to_extras {
159 6     6   9 my $self = shift;
160 6         16 my $to = [];
161 6         9 for my $copy ( @{ $self->copy_to } ) {
  6         217  
162 3 100       34 if ( my ( $copy_phase, $copy_rel ) = $copy =~ /\A([^.]+)[.](.+)\z/msx ) {
163 2         4 push @{$to}, { phase => $copy_phase, relation => $copy_rel };
  2         9  
164 2         8 next;
165             }
166 1         6 return $self->log_fatal( [ 'copy_to contained value not in form: phase.relation, got %s', $copy ] );
167             }
168 5         170 return $to;
169             }
170              
171             sub _get_feature_modules {
172 1     1   2 my ($self) = @_;
173 1         2 my $hash = {};
174 1         23 my $meta = $self->zilla->distmeta;
175 1 50       36520 if ( not exists $meta->{optional_features} ) {
176 0         0 $self->log('No optional_features detected');
177 0         0 return $hash;
178             }
179 1         2 for my $feature_name ( keys %{ $meta->{optional_features} } ) {
  1         4  
180 1         2 my $feature = $meta->{optional_features}->{$feature_name};
181 1         2 for my $rel_name ( keys %{ $feature->{prereqs} } ) {
  1         4  
182 1         2 my $rel = $feature->{prereqs}->{$rel_name};
183 1         1 for my $phase_name ( keys %{$rel} ) {
  1         3  
184 1         1 my $phase = $rel->{$phase_name};
185 1         2 for my $module ( keys %{$phase} ) {
  1         3  
186 1         11 $self->log_debug("Got module ${module} from ${feature}.${rel_name}.${phase}");
187 1         434 $hash->{$module} = 1;
188             }
189             }
190             }
191             }
192 1         1 return keys %{$hash};
  1         6  
193             }
194              
195             sub _build__modules_hash {
196 6     6   11 my ($self) = @_;
197 6         18 my $hash = {};
198 6         12 $hash->{$_} = 1 for @{ $self->modules };
  6         519  
199 6 100       239 return $hash unless $self->modules_from_features;
200 1         4 $hash->{$_} = 1 for $self->_get_feature_modules;
201 1         34 return $hash;
202             }
203              
204             sub _user_wants_softening_on {
205 5     5   10 my ( $self, $module ) = @_;
206 5         177 return exists $self->_modules_hash->{$module};
207             }
208              
209             sub _soften_prereqs {
210 15     15   20 my ( $self, $conf ) = @_;
211 15         411 my $prereqs = $self->zilla->prereqs;
212              
213 15         437 my $source_reqs = $prereqs->requirements_for( $conf->{from_phase}, $conf->{from_relation} );
214              
215 15         901 my @target_reqs;
216              
217 15         17 for my $target ( @{ $conf->{to} } ) {
  15         36  
218 21 100       344 next if 'none' eq $target->{relation};
219             push @target_reqs,
220             {
221             phase => $target->{phase},
222             relation => $target->{relation},
223 18         64 reqs => $prereqs->requirements_for( $target->{phase}, $target->{relation} ),
224             };
225             }
226              
227 15         959 for my $module ( $source_reqs->required_modules ) {
228 5 50       39 next unless $self->_user_wants_softening_on($module);
229 5         44 $self->log_debug("$conf->{from_phase}.$conf->{from_relation} contains $module");
230 5         1257 my $reqstring = $source_reqs->requirements_for_module($module);
231 5         318 $self->log_debug( [ ' - %-20s %s = %s', "$conf->{from_phase}.$conf->{from_relation}", $module, $reqstring ] );
232 5         1365 $source_reqs->clear_requirement($module);
233 5         125 for my $target (@target_reqs) {
234 6         391 my $old_string = $target->{reqs}->requirements_for_module($module);
235 6         51 $target->{reqs}->add_string_requirement( $module, $reqstring );
236 6         558 my $new_string = $target->{reqs}->requirements_for_module($module);
237              
238             # Calcluating the nature of the change for log debugging
239 6 0       200 my $change =
    0          
    50          
    50          
240             ( defined $old_string )
241             ? ( $old_string eq $new_string )
242             ? ( $new_string eq $reqstring )
243             ? " (was already \"$new_string\")"
244             : " (unchanged from \"$old_string\")"
245             : " (was \"$old_string\", now \"$new_string\")"
246             : ( defined $new_string ) ? ' (new requirement)'
247             : ' (Possible Error, undef after assignment)';
248              
249 6         81 $self->log_debug( [ ' + %-20s %s = %s%s', "$target->{phase}.$target->{relation}", $module, $reqstring, $change ] );
250              
251             }
252             }
253 15         1525 return $self;
254             }
255              
256             sub register_prereqs {
257 6     6 0 939301 my ($self) = @_;
258              
259 6         19 $self->log_debug( [ 'Softening modules: %s', join q[, ], sort keys %{ $self->_modules_hash } ] );
  6         248  
260 6         1691 for my $phase (qw( build test runtime )) {
261 16         26 for my $relation (qw( requires )) {
262             $self->_soften_prereqs(
263             {
264             from_phase => $phase,
265             from_relation => $relation,
266 16         605 to => [ { phase => $phase, relation => $self->to_relationship }, @{ $self->_copy_to_extras }, ],
  16         606  
267             },
268             );
269             }
270             }
271 5         48 return;
272             }
273              
274             1;
275              
276             __END__
277              
278             =pod
279              
280             =encoding UTF-8
281              
282             =head1 NAME
283              
284             Dist::Zilla::Plugin::Prereqs::Soften - Downgrade listed dependencies to recommendations if present.
285              
286             =head1 VERSION
287              
288             version 0.006003
289              
290             =head1 SYNOPSIS
291              
292             [Prereqs::Soften]
293             module = Foo
294             module = Bar
295              
296             =head1 DESCRIPTION
297              
298             This module iterates C<build>, C<require> and C<test> dependency lists and migrates dependencies found in C<.requires> and
299             demotes them to C<.recommends>
300              
301             Optionally, it can L<< duplicate softened dependencies to other locations|/copy_to >>
302              
303             =head1 ATTRIBUTES
304              
305             =head2 C<modules>
306              
307             A C<multi-value> argument that specifies a module name to soften in C<prereqs>.
308              
309             =head2 C<to_relationship>
310              
311             The output relationship kind.
312              
313             B<Default:>
314              
315             'recommends'
316              
317             B<Valid Values:>
318              
319             'recommends', 'suggests', 'none', 'requires', 'conflicts'
320              
321             Though the last two are reserved for people with C<< $num_feet > 2 >> or with shotguns that only fire blanks.
322              
323             C<none> is available since C<0.006000> to allow removal of dependencies in conjunction with copying them.
324              
325             =head2 C<copy_to>
326              
327             Additional places to copy the dependency to:
328              
329             B<Default:>
330              
331             []
332              
333             B<Example:>
334              
335             [Prereqs::Soften]
336             copy_to = develop.requires
337             to_relationship = recommends
338             module = Foo
339              
340             This in effect means:
341              
342             remove from: runtime.requires
343             → add to: develop.requires
344             → add to: runtime.recommends
345              
346             remove from: test.requires
347             → add to: develop.requires
348             → add to: test.recommends
349              
350             remove from: build.requires
351             → add to: develop.requires
352             → add to: build.recommends
353              
354             =head2 C<modules_from_features>
355              
356             This is for use in conjunction with L<< C<[OptionalFeature]>|Dist::Zilla::Plugin::OptionalFeature >>, or anything that injects
357             compatible structures into C<distmeta>.
358              
359             Recommended usage as follows:
360              
361             [OptionalFeature / Etc]
362             ...
363              
364             [Prereqs::Soften]
365             modules_from_features = 1
366              
367             In this example, C<copy_to> and C<modules> are both redundant, as C<modules> are propagated from all features,
368             and C<copy_to> is not necessary because L<< C<[OptionalFeature]>|Dist::Zilla::Plugin::OptionalFeature >> automatically adds
369             dependencies to C<develop.requires>
370              
371             =for Pod::Coverage mvp_aliases
372             mvp_multivalue_args
373             register_prereqs
374              
375             =head1 AUTHOR
376              
377             Kent Fredric <kentnl@cpan.org>
378              
379             =head1 COPYRIGHT AND LICENSE
380              
381             This software is copyright (c) 2017 by Kent Fredric <kentfredric@gmail.com>.
382              
383             This is free software; you can redistribute it and/or modify it under
384             the same terms as the Perl 5 programming language system itself.
385              
386             =cut