File Coverage

blib/lib/Dist/Inkt.pm
Criterion Covered Total %
statement 29 151 19.2
branch 0 28 0.0
condition 0 39 0.0
subroutine 10 30 33.3
pod 0 11 0.0
total 39 259 15.0


line stmt bran cond sub pod time code
1             package Dist::Inkt;
2              
3 1     1   804 use 5.010001;
  1         3  
4              
5             our $AUTHORITY = 'cpan:TOBYINK';
6             our $VERSION = '0.026';
7              
8 1     1   550 use Moose;
  1         441851  
  1         7  
9 1     1   7395 use Module::Metadata;
  1         7221  
  1         36  
10 1     1   513 use List::MoreUtils qw(uniq);
  1         11395  
  1         6  
11 1     1   1437 use Types::Standard -types;
  1         62206  
  1         11  
12 1     1   4384 use Types::Path::Tiny -types;
  1         30612  
  1         8  
13 1     1   915 use Path::Tiny 'path';
  1         2  
  1         53  
14 1     1   563 use Path::Iterator::Rule;
  1         10322  
  1         36  
15 1     1   8 use Module::Runtime qw(use_package_optimistically);
  1         2  
  1         8  
16 1     1   541 use namespace::autoclean;
  1         7046  
  1         5  
17              
18             sub builder_id {
19 0     0 0   my $real_class = shift->_real_class;
20 0           sprintf('%s version %s', $real_class, $real_class->VERSION);
21             }
22              
23             sub _real_class {
24 0     0     my $self = shift;
25 0           my ($real_class) = grep !/__ANON__/, Moose::Util::find_meta($self)->class_precedence_list;
26 0           $real_class;
27             }
28              
29             has name => (
30             is => 'ro',
31             isa => Str,
32             required => 1,
33             );
34              
35             has lead_module => (
36             is => 'ro',
37             isa => Str,
38             lazy => 1,
39             builder => '_build_lead_module',
40             );
41              
42             sub _build_lead_module
43             {
44 0     0     my $self = shift;
45 0           (my $name = $self->name) =~ s/-/::/g;
46 0           return $name;
47             }
48              
49             has version => (
50             is => 'ro',
51             isa => Str,
52             lazy => 1,
53             builder => '_build_version',
54             );
55              
56             sub _build_version
57             {
58 0     0     my $self = shift;
59 0           my $mm = 'Module::Metadata'->new_from_module(
60             $self->lead_module,
61             inc => [$self->sourcefile('lib')],
62             );
63 0           return $mm->{version}{original};
64             }
65              
66             has rootdir => (
67             is => 'ro',
68             isa => AbsDir,
69             required => 1,
70             coerce => 1,
71             handles => {
72             sourcefile => 'child',
73             },
74             );
75              
76             has targetdir_pattern => (
77             is => 'ro',
78             isa => Str,
79             lazy => 1,
80             builder => '_build_targetdir_pattern',
81             );
82              
83             sub _build_targetdir_pattern {
84 0     0     '%(name)s-%(version)s'
85             }
86              
87             has targetdir => (
88             is => 'ro',
89             isa => Path,
90             lazy => 1,
91             coerce => 1,
92             builder => '_build_targetdir',
93             handles => {
94             targetfile => 'child',
95             cleartarget => 'remove_tree',
96             },
97             );
98              
99             sub _build_targetdir
100             {
101 0     0     my $self = shift;
102            
103 0           require Text::sprintfn;
104 0           my $name = Text::sprintfn::sprintfn(
105             $self->targetdir_pattern,
106             {
107             name => $self->name,
108             version => $self->version,
109             },
110             );
111            
112 0           $self->rootdir->child($name);
113             }
114              
115             has metadata => (
116             is => 'ro',
117             isa => InstanceOf['CPAN::Meta'],
118             lazy => 1,
119             builder => '_build_metadata',
120             );
121              
122             sub _build_metadata
123             {
124 0     0     require CPAN::Meta;
125 0           my $self = shift;
126 0           my $meta = 'CPAN::Meta'->new({
127             name => $self->name,
128             version => $self->version,
129             no_index => { directory => [qw/ eg examples inc t xt /] },
130             generated_by => $self->builder_id,
131             dynamic_config => 0,
132             });
133 0           for (qw/ license author /) {
134 0 0 0       $meta->{$_} = [] if @{$meta->{$_}}==1 && $meta->{$_}[0] eq 'unknown';
  0            
135             }
136 0 0         if ($self->sourcefile('meta/META.PL')->exists)
137             {
138 0           local $_ = $meta;
139 0           my $filename = $self->sourcefile('meta/META.PL')->absolute->stringify;
140 0           do($filename);
141             }
142 0           return $meta;
143             }
144              
145             has project_uri => (
146             is => 'ro',
147             isa => Str,
148             lazy => 1,
149             builder => '_build_project_uri',
150             );
151              
152             sub _build_project_uri
153             {
154 0     0     my $self = shift;
155 0           sprintf('http://purl.org/NET/cpan-uri/dist/%s/project', $self->name);
156             }
157              
158             has targets => (
159             is => 'ro',
160             isa => ArrayRef[Str],
161             builder => '_build_targets',
162             );
163              
164 0     0     sub _build_targets { [] }
165              
166             has rights_for_generated_files => (
167             is => 'ro',
168             isa => HashRef[ArrayRef],
169             default => sub {
170             +{
171             COPYRIGHT => [ 'None' => 'public-domain' ],
172             };
173             },
174             );
175              
176       0     sub _inherited_rights {}
177              
178             sub new_from_ini
179             {
180 0     0 0   my $self = shift;
181 0           my $ini = shift;
182 0           my (%args) = @_;
183            
184 0 0         if (defined $ini)
185             {
186 0           $ini = File->assert_coerce($ini);
187             }
188             else
189             {
190 0           require Cwd;
191 0           $ini = path(Cwd::cwd)->child('dist.ini');
192 0 0         $ini->exists or confess("Could not find dist.ini; bailing out");
193             }
194            
195 0           my @lines = grep /^;;/, $ini->lines_utf8;
196 0           chomp @lines;
197            
198             my %config = map {
199 0           s/(?:^;;\s*)|(?:\s*$)//g;
  0            
200 0           my ($key, $value) = split /\s*=\s*/, $_, 2;
201 0           $key => scalar(eval($value));
202             } @lines;
203            
204 0   0       my $class = delete($config{class}) || 'Dist::Inkt::Profile::Simple';
205            
206 0   0       $config{rootdir} ||= $ini->dirname;
207            
208 0           use_package_optimistically($class)->new(%config, %args);
209             }
210              
211             sub BUILD
212             {
213 0     0 0   my $self = shift;
214 0 0         return if $self->{_already_built}++;
215            
216 0           $self->PopulateModel;
217 0           $self->PopulateMetadata;
218            
219 0           my $die = 0;
220            
221 0           my $l = $self->metadata->{license};
222 0 0 0       unless ($l and ref($l) eq 'ARRAY' and @$l and $l->[0] ne 'unknown')
      0        
      0        
223             {
224 0           $self->log("ERROR: licence unknown!");
225 0           $die++;
226             }
227            
228 0           my $a = $self->metadata->{author};
229 0 0 0       unless ($a and ref($a) eq 'ARRAY' and @$a and $a->[0] ne 'unknown')
      0        
      0        
230             {
231 0           $self->log("ERROR: author unknown!");
232 0           $die++;
233             }
234            
235 0           my $b = $self->metadata->{abstract};
236 0 0 0       unless (defined($b) and $b ne 'unknown')
237             {
238 0           $self->log("ERROR: abstract unknown!");
239 0           $die++;
240             }
241            
242 0 0         die "Incomplete metadata; stopped" if $die;
243             }
244              
245       0 0   sub PopulateModel {}
246       0 0   sub PopulateMetadata {}
247              
248             sub BuildTargets
249             {
250 0     0 0   my $self = shift;
251            
252 0           $self->cleartarget;
253 0           $self->targetdir->mkpath;
254            
255 0 0         $self->Build_Files if $self->DOES('Dist::Inkt::Role::CopyFiles');
256            
257 0           for my $target (uniq @{ $self->targets })
  0            
258             {
259 0 0 0       next if $self->DOES('Dist::Inkt::Role::CopyFiles') && $target eq 'Files';
260            
261 0           my $method = "Build_$target";
262 0           $self->$method;
263             }
264             }
265              
266             sub BuildManifest
267             {
268 0     0 0   my $self = shift;
269            
270 0           my $file = $self->targetfile('MANIFEST');
271 0           $self->log("Writing $file");
272 0   0       $self->rights_for_generated_files->{'MANIFEST'} ||= [
273             'None', 'public-domain'
274             ];
275            
276 0           my $rule = 'Path::Iterator::Rule'->new->file;
277 0           my $root = $self->targetdir;
278 0           my @files = map { path($_)->relative($root) } $rule->all($root);
  0            
279            
280 0           $file->spew(map "$_\n", sort 'MANIFEST', @files);
281             }
282              
283             sub BuildTarball
284             {
285 0     0 0   my $self = shift;
286 0   0       my $file = path($_[0] || sprintf('%s.tar.gz', $self->targetdir));
287 0           $self->log("Writing $file");
288            
289 0           require Archive::Tar;
290 0           my $tar = 'Archive::Tar'->new;
291            
292 0           my $rule = 'Path::Iterator::Rule'->new->file;
293 0           my $root = $self->targetdir;
294 0           my $pfx = $root->basename;
295 0           for ($rule->all($root))
296             {
297 0           my $abs = path($_);
298 0           $tar->add_data( "$pfx/" . $abs->relative($root), $abs->slurp );
299             }
300            
301 0           $tar->write($file, Archive::Tar::COMPRESS_GZIP());
302             }
303              
304             has should_compress => (
305             is => 'ro',
306             isa => Bool,
307             default => sub { !$ENV{PERL_DIST_INKT_NOTARBALL} },
308             );
309              
310             sub BuildAll
311             {
312 0     0 0   my $self = shift;
313 0           $self->BuildTargets;
314 0           $self->BuildManifest;
315 0 0         if ($self->should_compress) {
316 0           $self->BuildTarball;
317 0           $self->cleartarget;
318             }
319             }
320              
321             sub BuildTravisYml
322             {
323 0     0 0   my $self = shift;
324            
325 0           $self->log("Generating .travis.yml");
326 0           my $yml = $self->sourcefile(".travis.yml")->openw;
327            
328 0   0       my $perl_ver = $self->metadata->{prereqs}{runtime}{requires}{perl} || '5.014';
329            
330 0           print {$yml} "language: perl\n";
  0            
331 0           print {$yml} "perl:\n";
  0            
332 0           for my $v (8, 10, 12, 14, 16, 18, 20)
333             {
334 0           my $formatted = sprintf("5.%03d000", $v);
335 0 0         $formatted = '5.008001' if $formatted eq '5.008000';
336            
337 0 0         if ($formatted ge $perl_ver)
338             {
339 0           print {$yml} " - \"5.$v\"\n";
  0            
340             }
341             }
342            
343 0           my $class = $self->_real_class;
344            
345             ## no Test::Tabs
346            
347 0           print {$yml} <<"TAIL";
  0            
348             matrix:
349             include:
350             - perl: 5.18.2
351             env: COVERAGE=1
352             before_install:
353             - export DIST_INKT_PROFILE="$class"
354             - git clone git://github.com/tobyink/perl-travis-helper
355             - source perl-travis-helper/init
356             - build-perl
357             - perl -V
358             - build-dist
359             - cd \$BUILD_DIR
360             install:
361             - cpan-install --toolchain
362             - cpan-install --deps
363             - cpan-install --coverage
364             before_script:
365             - coverage-setup
366             script:
367             - prove -l \$(test-dirs)
368             after_success:
369             - coverage-report
370              
371             TAIL
372              
373             ## use Test::Tabs
374            
375 0           return;
376             }
377              
378             sub log
379             {
380 0     0 0   my $self = shift;
381 0           my ($fmt, @args) = @_;
382 0           printf STDERR "$fmt\n", @args;
383             }
384              
385             1;
386              
387             __END__
388              
389             =pod
390              
391             =encoding utf-8
392              
393             =for stopwords gzipped tarball
394              
395             =head1 NAME
396              
397             Dist::Inkt - yet another distribution builder
398              
399             =head1 STATUS
400              
401             Experimental.
402              
403             =head1 DESCRIPTION
404              
405             L<Dist::Zilla> didn't have the prerequisite amount of crazy for me, so
406             I wrote this instead.
407              
408             Dist::Inkt itself does virtually nothing; it creates an empty directory,
409             generates a MANIFEST file, and then wraps it all up into a gzipped
410             tarball. But it provides various hooks along the way for subclasses
411             to grab hold of. So the general idea is that you write a subclass of
412             Dist::Inkt, which consumes various Moose::Roles to do the actual work
413             of populating the distribution with files.
414              
415             As such, Dist::Inkt is not so much a distribution builder, as it is a
416             framework for writing your own distribution builder.
417              
418             Several roles of varying utility are bundled with Dist::Inkt, as is
419             L<Dist::Inkt::Profile::Simple>, a subclass of Dist::Inkt which consumes
420             most of these roles.
421              
422             =head1 COMPANIONS
423              
424             Dist::Inkt does just one thing - building the tarball from some
425             checkout of the repo.
426              
427             Although roles could theoretically be written for other tasks, out of
428             the box, Dist::Inkt doesn't do any of the following:
429              
430             =over
431              
432             =item B<< Minting new distributions >>
433              
434             I'm writing a separate tool, L<Dist::Inktly::Minty> for that.
435              
436             =item B<< Test suite running >>
437              
438             Use L<App::Prove> or L<App::ForkProve>.
439              
440             =item B<< CPAN Uploading >>
441              
442             Use L<CPAN::Uploader>.
443              
444             =item B<< Changing the version number across many files >>
445              
446             Use L<Perl::Version>.
447              
448             =item B<< Integration with version control tools >>
449              
450             Just use C<hg> or C<svn> or C<git> of whatever as you normally would.
451             None of the files generated by Dist::Inkt should probably be checked
452             into your repo.
453              
454             =back
455              
456             =head1 BUGS
457              
458             Please report any bugs to
459             L<http://rt.cpan.org/Dist/Display.html?Queue=Dist-Inkt>.
460              
461             =head1 SEE ALSO
462              
463             If you are not me, then you may well want one of these instead:
464              
465             =over
466              
467             =item *
468              
469             L<Dist::Zilla>
470              
471             =item *
472              
473             L<Dist::Milla>
474              
475             =item *
476              
477             L<Minilla>
478              
479             =back
480              
481             Various extensions for Dist::Inkt:
482              
483             =over
484              
485             =item *
486              
487             L<Dist::Inkt::DOAP>
488              
489             =item *
490              
491             L<Dist::Inkt::Profile::TOBYINK>
492              
493             =back
494              
495             =head1 AUTHOR
496              
497             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
498              
499             =head1 COPYRIGHT AND LICENCE
500              
501             This software is copyright (c) 2013-2014 by Toby Inkster.
502              
503             This is free software; you can redistribute it and/or modify it under
504             the same terms as the Perl 5 programming language system itself.
505              
506             =head1 DISCLAIMER OF WARRANTIES
507              
508             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
509             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
510             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
511