File Coverage

lib/Dist/Zilla/Role/Bootstrap.pm
Criterion Covered Total %
statement 24 74 32.4
branch 0 14 0.0
condition n/a
subroutine 8 21 38.1
pod n/a
total 32 109 29.3


line stmt bran cond sub pod time code
1 4     4   2610 use 5.008; # utf8
  4         10  
  4         161  
2 4     4   14 use strict;
  4         5  
  4         104  
3 4     4   14 use warnings;
  4         10  
  4         175  
4 4     4   2313 use utf8;
  4         37  
  4         24  
5              
6             package Dist::Zilla::Role::Bootstrap;
7              
8             our $VERSION = '1.001002';
9              
10             # ABSTRACT: Shared logic for bootstrap things.
11              
12             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
13              
14 4     4   1944 use Moose::Role qw( with has around requires );
  4         362714  
  4         23  
15 4     4   18979 use List::UtilsBy qw( max_by nmax_by );
  4         5054  
  4         292  
16 4     4   1874 use version qw();
  4         6031  
  4         3728  
17              
18              
19              
20              
21              
22              
23              
24              
25              
26              
27              
28              
29              
30             with 'Dist::Zilla::Role::Plugin';
31              
32             around 'dump_config' => sub {
33             my ( $orig, $self, @args ) = @_;
34             my $config = $self->$orig(@args);
35             my $localconf = {};
36             for my $attribute (qw( try_built try_built_method fallback distname )) {
37             my $pred = 'has_' . $attribute;
38             if ( $self->can($pred) ) {
39             next unless $self->$pred();
40             }
41             if ( $self->can($attribute) ) {
42             $localconf->{$attribute} = $self->$attribute();
43             }
44             }
45              
46             $config->{ q{} . __PACKAGE__ } = $localconf;
47             return $config;
48             };
49              
50              
51              
52              
53              
54              
55              
56              
57              
58              
59              
60              
61              
62              
63              
64             has distname => ( isa => 'Str', is => ro =>, lazy_build => 1 );
65              
66             sub _build_distname {
67 0     0     my ($self) = @_;
68 0           return $self->zilla->name;
69             }
70              
71              
72              
73              
74              
75             has _cwd => ( is => ro =>, lazy_build => 1, );
76              
77             sub _build__cwd {
78 0     0     my ($self) = @_;
79 0           require Path::Tiny;
80 0           return Path::Tiny::path( $self->zilla->root );
81             }
82              
83              
84              
85              
86              
87              
88              
89              
90              
91              
92              
93              
94              
95              
96              
97             has try_built => ( isa => 'Bool', is => ro =>, lazy_build => 1, );
98 0     0     sub _build_try_built { return }
99              
100              
101              
102              
103              
104              
105              
106              
107              
108              
109              
110              
111              
112              
113              
114             has fallback => ( isa => 'Bool', is => ro =>, lazy_build => 1 );
115 0     0     sub _build_fallback { return 1 }
116              
117              
118              
119              
120              
121              
122              
123              
124              
125              
126              
127              
128              
129              
130              
131              
132              
133              
134              
135             has try_built_method => ( isa => 'Str', is => ro =>, lazy_build => 1, );
136 0     0     sub _build_try_built_method { return 'mtime' }
137              
138              
139              
140              
141              
142              
143              
144              
145              
146             sub _pick_latest_mtime {
147 0     0     my ( undef, @candidates ) = @_;
148 0     0     return max_by { $_->stat->mtime } @candidates;
  0            
149             }
150              
151              
152              
153              
154              
155              
156              
157              
158              
159              
160              
161             sub _get_candidate_version {
162 0     0     my ( $self, $candidate ) = @_;
163 0           my $distname = $self->distname;
164 0 0         if ( $candidate->basename =~ /\A\Q$distname\E-(.+\z)/msx ) {
165 0           my $version = $1;
166 0           $version =~ s/-TRIAL\z//msx;
167 0           return version->parse($version);
168             }
169              
170             }
171              
172              
173              
174              
175              
176              
177              
178              
179              
180             sub _pick_latest_parseversion {
181 0     0     my ( $self, @candidates ) = @_;
182 0     0     return max_by { $self->_get_candidate_version($_) } @candidates;
  0            
183             }
184              
185             my (%methods) = (
186             mtime => _pick_latest_mtime =>,
187             parseversion => _pick_latest_parseversion =>,
188             );
189              
190              
191              
192              
193              
194              
195              
196              
197              
198             sub _pick_candidate {
199 0     0     my ( $self, @candidates ) = @_;
200 0           my $method = $self->try_built_method;
201 0 0         if ( not exists $methods{$method} ) {
202 0           require Carp;
203 0           Carp::croak("No such candidate picking method $method");
204             }
205 0           $method = $methods{$method};
206 0           return $self->$method(@candidates);
207             }
208              
209              
210              
211              
212              
213              
214              
215              
216              
217              
218              
219             has _bootstrap_root => ( is => ro =>, lazy_build => 1 );
220              
221             sub _build__bootstrap_root {
222 0     0     my ($self) = @_;
223 0 0         if ( not $self->try_built ) {
224 0           return $self->_cwd;
225             }
226 0           my $distname = $self->distname;
227              
228 0           my (@candidates) = grep { $_->basename =~ /\A\Q$distname\E-/msx } grep { $_->is_dir } $self->_cwd->children;
  0            
  0            
229              
230 0 0         if ( 1 == scalar @candidates ) {
231 0           return $candidates[0];
232             }
233 0 0         if ( scalar @candidates < 1 ) {
234 0 0         if ( not $self->fallback ) {
235 0           $self->log( [ 'candidates for bootstrap (%s) == 0, and fallback disabled. not bootstrapping', 0 + @candidates ] );
236 0           return;
237             }
238             else {
239 0           $self->log( [ 'candidates for bootstrap (%s) == 0, fallback to boostrapping <distname>/', 0 + @candidates ] );
240 0           return $self->_cwd;
241             }
242             }
243              
244 0           $self->log_debug( [ '>1 candidates, picking one by method %s', $self->try_built_method ] );
245 0           return $self->_pick_candidate(@candidates);
246             }
247              
248              
249              
250              
251              
252              
253              
254              
255              
256             sub _add_inc {
257 0     0     my ( undef, $import ) = @_;
258 0 0         if ( not ref $import ) {
259 0           require lib;
260 0           return lib->import($import);
261             }
262 0           require Carp;
263 0           return Carp::croak('At this time, _add_inc(arg) only supports scalar values of arg');
264             }
265              
266              
267              
268              
269              
270              
271              
272              
273              
274              
275              
276              
277              
278             requires 'bootstrap';
279              
280             around plugin_from_config => sub {
281             my ( $orig, $plugin_class, $name, $payload, $section ) = @_;
282              
283             my $instance = $plugin_class->$orig( $name, $payload, $section );
284              
285             $instance->bootstrap;
286              
287             return $instance;
288             };
289              
290 4     4   28 no Moose::Role;
  4         15  
  4         33  
291              
292             1;
293              
294             __END__
295              
296             =pod
297              
298             =encoding UTF-8
299              
300             =head1 NAME
301              
302             Dist::Zilla::Role::Bootstrap - Shared logic for bootstrap things.
303              
304             =head1 VERSION
305              
306             version 1.001002
307              
308             =head1 SYNOPSIS
309              
310             For consuming plugins:
311              
312             use Moose;
313             with 'Dist::Zilla::Role::Bootstrap';
314              
315             sub bootstrap {
316             my $bootstrap_root = $_[0]->_bootstrap_root;
317             # Do the actual bootstrap work here
318             $_[0]->_add_inc('./some/path/here');
319             }
320              
321             For users of plugins:
322              
323             [Some::Plugin::Name]
324             try_built = 0 ; # use / as the root to bootstrap
325             try_built = 1 ; # try to use /Dist-Name-.*/ instead of /
326              
327             fallback = 0 ; # don't bootstrap at all if /Dist-Name-.*/ matches != 1 things
328             fallback = 1 ; # fallback to / if /Dist-Name-.*/ matches != 1 things
329              
330             =head1 DESCRIPTION
331              
332             This module is a role that aims to be consumed by plugins that want to perform
333             some very early bootstrap operation that may affect the loading environment of
334             successive plugins, especially with regards to plugins that may wish to build with
335             themselves, either by consuming the source tree itself, or by consuming a previous
336             built iteration.
337              
338             Implementation is quite simple:
339              
340             =over 4
341              
342             =item 1. C<with> this role in your plugin
343              
344             with 'Dist::Zilla::Role::Bootstrap'
345              
346             =item 2. Implement the C<bootstrap> sub.
347              
348             sub bootstrap {
349             my ( $self ) = @_;
350             }
351              
352             =item 3. I<Optional>: Fetch the discovered C<bootstap> root via:
353              
354             $self->_bootstap_root
355              
356             =item 4. I<Optional>: Load some path into C<@INC> via:
357              
358             $self->_add_inc($path)
359              
360             =back
361              
362             =head1 REQUIRED METHODS
363              
364             =head2 C<bootstrap>
365              
366             Any user specified C<bootstrap> method will be invoked during C<plugin_from_config>.
367              
368             This is B<AFTER> C<< ->new >>, B<AFTER> C<< ->BUILD >>, and B<AFTER> C<dzil>'s internal C<plugin_from_config> steps.
369              
370             This occurs within the C<register_component> phase of the plug-in loading and configuration.
371              
372             This also occurs B<BEFORE> C<Dist::Zilla> attaches the plug-in into the plug-in stash.
373              
374             =head1 ATTRIBUTES
375              
376             =head2 C<distname>
377              
378             The name of the distribution.
379              
380             This value is vivified by asking C<< zilla->name >>.
381              
382             Usually this value is populated by C<dist.ini> in the property C<name>
383              
384             However, occasionally, this value is discovered by a C<plugin>.
385              
386             In such a case, that plugin cannot be bootstrapped, because that plugin B<MUST> be loaded prior to bootstrap.
387              
388             =head2 C<try_built>
389              
390             This attribute controls how the consuming C<plugin> behaves.
391              
392             =over 4
393              
394             =item * false B<(default)> : bootstrapping is only done to C<PROJECTROOT/lib>
395              
396             =item * true : bootstrap attempts to try C<< PROJECTROOT/<distname>-<version>/lib >>
397              
398             =back
399              
400             =head2 C<fallback>
401              
402             This attribute is for use in conjunction with C<try_built>
403              
404             =over 4
405              
406             =item * C<false> : When C<< PROJECTROOT/<distname>-<version> >> does not exist, don't perform any bootstrapping
407              
408             =item * C<true> B<(default)> : When C<< PROJECTROOT/<distname>-<version> >> does not exist, bootstrap to C<< PROJECTROOT/lib >>
409              
410             =back
411              
412             =head2 C<try_built_method>
413              
414             This attribute controls how C<try_built> behaves when multiple directories exist that match C<< PROJECTROOT/<distname>-.* >>
415              
416             Two valid options at this time:
417              
418             =over 4
419              
420             =item * C<mtime> B<(default)> : Pick the directory with the most recent C<mtime>
421              
422             =item * C<parseversion> : Attempt to parse versions on all candidate directories and use the one with the largest version.
423              
424             =back
425              
426             Prior to C<0.2.0> this property did not exist, and default behavior was to assume C<0 Candidates> and C<2 or more Candidates> were the same problem.
427              
428             =head1 PRIVATE ATTRIBUTES
429              
430             =head2 C<_cwd>
431              
432             =head2 C<_bootstrap_root>
433              
434             Internal: This is the real legwork, and resolves the base directory using the bootstrap resolution protocol.
435              
436             It should always return a project root of some kind, whether it be a source tree, or built source tree.
437              
438             It can also return C<undef> if discovery concludes that no bootstrap can or should be performed.
439              
440             =head1 PRIVATE METHODS
441              
442             =head2 C<_pick_latest_mtime>
443              
444             "Latest" C<mtime> candidate selector
445              
446             my $directory = $self->_pick_latest_mtime(@directory_objects)
447              
448             =head2 C<_get_candidate_version>
449              
450             Attempt to resolve a version from a directory name
451              
452             my $version = $self->_get_candidate_version($directory_object)
453              
454             B<NOTE:> At this time, the presence of C<-TRIAL> is simply stripped and ignored
455              
456             =head2 C<_pick_latest_parseversion>
457              
458             "Latest" C<version> candidate selector
459              
460             my $directory = $self->_pick_latest_parseversion(@directory_objects)
461              
462             =head2 C<_pick_candidate>
463              
464             Pick a directory from a list of candidates using the method described by C<try_built_method>
465              
466             my $directory = $self->_pick_candidate( @directory_objects );
467              
468             =head2 C<_add_inc>
469              
470             Internal: Used to perform the final step of injecting library paths into C<@INC>
471              
472             $self->_add_inc("$libraryPath");
473              
474             =begin MetaPOD::JSON v1.1.0
475              
476             {
477             "namespace":"Dist::Zilla::Role::Bootstrap",
478             "interface":"role",
479             "does":"Dist::Zilla::Role::Plugin"
480             }
481              
482              
483             =end MetaPOD::JSON
484              
485             =head1 AUTHOR
486              
487             Kent Fredric <kentnl@cpan.org>
488              
489             =head1 COPYRIGHT AND LICENSE
490              
491             This software is copyright (c) 2015 by Kent Fredric <kentfredric@gmail.com>.
492              
493             This is free software; you can redistribute it and/or modify it under
494             the same terms as the Perl 5 programming language system itself.
495              
496             =cut