File Coverage

lib/Dist/Zilla/Plugin/Prereqs/Soften.pm
Criterion Covered Total %
statement 83 85 97.6
branch 8 10 80.0
condition n/a
subroutine 16 16 100.0
pod 0 3 0.0
total 107 114 93.8


line stmt bran cond sub pod time code
1 7     7   2901651 use 5.006;
  7         20  
2 7     7   28 use strict;
  7         11  
  7         143  
3 7     7   21 use warnings;
  7         13  
  7         420  
4              
5             package Dist::Zilla::Plugin::Prereqs::Soften;
6              
7             our $VERSION = '0.006002';
8              
9             # ABSTRACT: Downgrade listed dependencies to recommendations if present.
10              
11             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
12              
13 7     7   454 use Moose qw( with has around );
  7         286442  
  7         47  
14 7     7   26207 use MooseX::Types::Moose qw( ArrayRef HashRef Str Bool );
  7         32074  
  7         92  
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   23561 use Moose::Util::TypeConstraints qw(enum);
  7         11  
  7         57  
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   2076 no Moose::Util::TypeConstraints;
  7         8  
  7         30  
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   1792 no Moose;
  7         7  
  7         26  
154              
155 6     6 0 993 sub mvp_multivalue_args { return qw(modules copy_to) }
156 6     6 0 732 sub mvp_aliases { return { 'module' => 'modules' } }
157              
158             sub _build__copy_to_extras {
159 6     6   13 my $self = shift;
160 6         10 my $to = [];
161 6         12 for my $copy ( @{ $self->copy_to } ) {
  6         182  
162 3 100       21 if ( my ( $copy_phase, $copy_rel ) = $copy =~ /\A([^.]+)[.](.+)\z/msx ) {
163 2         2 push @{$to}, { phase => $copy_phase, relation => $copy_rel };
  2         6  
164 2         5 next;
165             }
166 1         7 return $self->log_fatal(['copy_to contained value not in form: phase.relation, got %s', $copy ]);
167             }
168 5         148 return $to;
169             }
170              
171             sub _get_feature_modules {
172 1     1   2 my ($self) = @_;
173 1         3 my $hash = {};
174 1         22 my $meta = $self->zilla->distmeta;
175 1 50       36168 if ( not exists $meta->{optional_features} ) {
176 0         0 $self->log('No optional_features detected');
177 0         0 return $hash;
178             }
179 1         3 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         3  
182 1         2 my $rel = $feature->{prereqs}->{$rel_name};
183 1         2 for my $phase_name ( keys %{$rel} ) {
  1         2  
184 1         2 my $phase = $rel->{$phase_name};
185 1         3 for my $module ( keys %{$phase} ) {
  1         2  
186 1         5 $hash->{$module} = 1;
187             }
188             }
189             }
190             }
191 1         1 return keys %{$hash};
  1         5  
192             }
193              
194             sub _build__modules_hash {
195 5     5   9 my ($self) = @_;
196 5         11 my $hash = {};
197 5         7 $hash->{$_} = 1 for @{ $self->modules };
  5         153  
198 5 100       160 return $hash unless $self->modules_from_features;
199 1         4 $hash->{$_} = 1 for $self->_get_feature_modules;
200 1         39 return $hash;
201             }
202              
203             sub _user_wants_softening_on {
204 5     5   9 my ( $self, $module ) = @_;
205 5         165 return exists $self->_modules_hash->{$module};
206             }
207              
208             sub _soften_prereqs {
209 15     15   19 my ( $self, $conf ) = @_;
210 15         336 my $prereqs = $self->zilla->prereqs;
211              
212 15         361 my $source_reqs = $prereqs->requirements_for( $conf->{from_phase}, $conf->{from_relation} );
213              
214 15         775 my @target_reqs;
215              
216 15         17 for my $target ( @{ $conf->{to} } ) {
  15         29  
217 21 100       196 next if 'none' eq $target->{relation};
218 18         41 push @target_reqs, $prereqs->requirements_for( $target->{phase}, $target->{relation} );
219             }
220              
221 15         817 for my $module ( $source_reqs->required_modules ) {
222 5 50       34 next unless $self->_user_wants_softening_on($module);
223 5         23 my $reqstring = $source_reqs->requirements_for_module($module);
224 5         131 $source_reqs->clear_requirement($module);
225 5         73 for my $target (@target_reqs) {
226 6         88 $target->add_string_requirement( $module, $reqstring );
227             }
228             }
229 15         431 return $self;
230             }
231              
232             sub register_prereqs {
233 6     6 0 798970 my ($self) = @_;
234              
235 6         19 for my $phase (qw( build test runtime )) {
236 16         23 for my $relation (qw( requires )) {
237             $self->_soften_prereqs(
238             {
239             from_phase => $phase,
240             from_relation => $relation,
241 16         529 to => [ { phase => $phase, relation => $self->to_relationship }, @{ $self->_copy_to_extras }, ],
  16         470  
242             },
243             );
244             }
245             }
246 5         43 return;
247             }
248              
249             1;
250              
251             __END__
252              
253             =pod
254              
255             =encoding UTF-8
256              
257             =head1 NAME
258              
259             Dist::Zilla::Plugin::Prereqs::Soften - Downgrade listed dependencies to recommendations if present.
260              
261             =head1 VERSION
262              
263             version 0.006002
264              
265             =head1 SYNOPSIS
266              
267             [Prereqs::Soften]
268             module = Foo
269             module = Bar
270              
271             =head1 DESCRIPTION
272              
273             This module iterates C<build>, C<require> and C<test> dependency lists and migrates dependencies found in C<.requires> and
274             demotes them to C<.recommends>
275              
276             Optionally, it can L<< duplicate softened dependencies to other locations|/copy_to >>
277              
278             =head1 ATTRIBUTES
279              
280             =head2 C<modules>
281              
282             A C<multi-value> argument that specifies a module name to soften in C<prereqs>.
283              
284             =head2 C<to_relationship>
285              
286             The output relationship kind.
287              
288             B<Default:>
289              
290             'recommends'
291              
292             B<Valid Values:>
293              
294             'recommends', 'suggests', 'none', 'requires', 'conflicts'
295              
296             Though the last two are reserved for people with C<< $num_feet > 2 >> or with shotguns that only fire blanks.
297              
298             C<none> is available since C<0.006000> to allow removal of dependencies in conjunction with copying them.
299              
300             =head2 C<copy_to>
301              
302             Additional places to copy the dependency to:
303              
304             B<Default:>
305              
306             []
307              
308             B<Example:>
309              
310             [Prereqs::Soften]
311             copy_to = develop.requires
312             to_relationship = recommends
313             module = Foo
314              
315             This in effect means:
316              
317             remove from: runtime.requires
318             → add to: develop.requires
319             → add to: runtime.recommends
320              
321             remove from: test.requires
322             → add to: develop.requires
323             → add to: test.recommends
324              
325             remove from: build.requires
326             → add to: develop.requires
327             → add to: build.recommends
328              
329             =head2 C<modules_from_features>
330              
331             This is for use in conjunction with L<< C<[OptionalFeature]>|Dist::Zilla::Plugin::OptionalFeature >>, or anything that injects
332             compatible structures into C<distmeta>.
333              
334             Recommended usage as follows:
335              
336             [OptionalFeature / Etc]
337             ...
338              
339             [Prereqs::Soften]
340             modules_from_features = 1
341              
342             In this example, C<copy_to> and C<modules> are both redundant, as C<modules> are propagated from all features,
343             and C<copy_to> is not necessary because L<< C<[OptionalFeature]>|Dist::Zilla::Plugin::OptionalFeature >> automatically adds
344             dependencies to C<develop.requires>
345              
346             =for Pod::Coverage mvp_aliases
347             mvp_multivalue_args
348             register_prereqs
349              
350             =head1 AUTHOR
351              
352             Kent Fredric <kentnl@cpan.org>
353              
354             =head1 COPYRIGHT AND LICENSE
355              
356             This software is copyright (c) 2016 by Kent Fredric <kentfredric@gmail.com>.
357              
358             This is free software; you can redistribute it and/or modify it under
359             the same terms as the Perl 5 programming language system itself.
360              
361             =cut