1package Module::Starter::Simple;
2
3use 5.006;
4use strict;
5use warnings;
6
7use Cwd 'cwd';
8use File::Path qw( make_path remove_tree );
9use File::Spec ();
10use Carp qw( carp confess croak );
11use Module::Runtime qw( require_module );
12
13use Module::Starter::BuilderSet;
14
15=head1 NAME
16
17Module::Starter::Simple - a simple, comprehensive Module::Starter plugin
18
19=head1 VERSION
20
21version 1.77
22
23=cut
24
25our $VERSION = '1.77';
26
27=head1 SYNOPSIS
28
29    use Module::Starter qw(Module::Starter::Simple);
30
31    Module::Starter->create_distro(%args);
32
33=head1 DESCRIPTION
34
35Module::Starter::Simple is a plugin for Module::Starter that will perform all
36the work needed to create a distribution.  Given the parameters detailed in
37L<Module::Starter>, it will create content, create directories, and populate
38the directories with the required files.
39
40=head1 CLASS METHODS
41
42=head2 C<< new(%args) >>
43
44This method is called to construct and initialize a new Module::Starter object.
45It is never called by the end user, only internally by C<create_distro>, which
46creates ephemeral Module::Starter objects.  It's documented only to call it to
47the attention of subclass authors.
48
49=cut
50
51sub new {
52    my $class = shift;
53    return bless { @_ } => $class;
54}
55
56=head1 OBJECT METHODS
57
58All the methods documented below are object methods, meant to be called
59internally by the ephemeral objects created during the execution of the class
60method C<create_distro> above.
61
62=head2 postprocess_config
63
64A hook to do any work after the configuration is initially processed.
65
66=cut
67
68sub postprocess_config { 1 };
69
70=head2 pre_create_distro
71
72A hook to do any work right before the distro is created.
73
74=cut
75
76sub pre_create_distro { 1 };
77
78=head2 C<< create_distro(%args) >>
79
80This method works as advertised in L<Module::Starter>.
81
82=cut
83
84sub create_distro {
85    my $either = shift;
86
87    ( ref $either ) or $either = $either->new( @_ );
88
89    my $self    = $either;
90    my $modules = $self->{modules} || [];
91    my @modules = map { split /,/ } @{$modules};
92    croak "No modules specified.\n" unless @modules;
93    for (@modules) {
94        croak "Invalid module name: $_" unless /\A[a-z_]\w*(?:::[\w]+)*\Z/i;
95    }
96
97    if ( ( not $self->{author} ) && ( $^O ne 'MSWin32' ) ) {
98        ( $self->{author} ) = split /,/, ( getpwuid $> )[6];
99    }
100
101    if ( not $self->{email} and exists $ENV{EMAIL} ) {
102        $self->{email} = $ENV{EMAIL};
103    }
104
105    croak "Must specify an author\n" unless $self->{author};
106    croak "Must specify an email address\n" unless $self->{email};
107    ($self->{email_obfuscated} = $self->{email}) =~ s/@/ at /;
108
109    $self->{license}      ||= 'artistic2';
110    $self->{minperl}      ||= '5.006';
111    $self->{ignores_type} ||= ['generic'];
112    $self->{manifest_skip} = !! grep { /manifest/ } @{ $self->{ignores_type} };
113
114    $self->{license_record} = $self->_license_record();
115
116    $self->{main_module} = $modules[0];
117    if ( not defined $self->{distro} or not length $self->{distro} ) {
118        $self->{distro} = $self->{main_module};
119        $self->{distro} =~ s/::/-/g;
120    }
121
122    $self->{basedir} = $self->{dir} || $self->{distro};
123    $self->create_basedir;
124
125    my @files;
126    push @files, $self->create_modules( @modules );
127
128    push @files, $self->create_t( @modules );
129    push @files, $self->create_ignores;
130    my %build_results = $self->create_build();
131    push(@files, @{ $build_results{files} } );
132
133    push @files, $self->create_Changes;
134    push @files, $self->create_README( $build_results{instructions} );
135    push @files, $self->create_LICENSE if $self->{genlicense};
136
137    $self->create_MANIFEST( $build_results{'manifest_method'} ) unless ( $self->{manifest_skip} );
138    # TODO: put files to ignore in a more standard form?
139    # XXX: no need to return the files created
140
141    return;
142}
143
144=head2 post_create_distro
145
146A hook to do any work after creating the distribution.
147
148=cut
149
150sub post_create_distro { 1 };
151
152=head2 pre_exit
153
154A hook to do any work right before exit time.
155
156=cut
157
158sub pre_exit {
159     print "Created starter directories and files\n";
160}
161
162=head2 create_basedir
163
164Creates the base directory for the distribution.  If the directory already
165exists, and I<$force> is true, then the existing directory will get erased.
166
167If the directory can't be created, or re-created, it dies.
168
169=cut
170
171sub create_basedir {
172    my $self = shift;
173
174    # Make sure there's no directory
175    if ( -e $self->{basedir} ) {
176        die( "$self->{basedir} already exists.  ".
177             "Use --force if you want to stomp on it.\n"
178            ) unless $self->{force};
179
180        remove_tree $self->{basedir};
181
182        die "Couldn't delete existing $self->{basedir}: $!\n"
183          if -e $self->{basedir};
184    }
185
186    CREATE_IT: {
187        $self->progress( "Created $self->{basedir}" );
188
189        make_path $self->{basedir};
190
191        die "Couldn't create $self->{basedir}: $!\n" unless -d $self->{basedir};
192    }
193
194    return;
195}
196
197=head2 create_modules( @modules )
198
199This method will create a starter module file for each module named in
200I<@modules>.
201
202=cut
203
204sub create_modules {
205    my $self = shift;
206    my @modules = @_;
207
208    my @files;
209
210    for my $module ( @modules ) {
211        my $rtname = lc $module;
212        $rtname =~ s/::/-/g;
213        push @files, $self->_create_module( $module, $rtname );
214    }
215
216    return @files;
217}
218
219=head2 module_guts( $module, $rtname )
220
221This method returns the text which should serve as the contents for the named
222module.  I<$rtname> is the email suffix which rt.cpan.org will use for bug
223reports.  (This should, and will, be moved out of the parameters for this
224method eventually.)
225
226=cut
227
228our $LICENSES = {
229    perl      => 'Perl_5',
230    artistic  => 'Artistic_1_0',
231    artistic2 => 'Artistic_2_0',
232    mozilla   => 'Mozilla_1_1',
233    mozilla2  => 'Mozilla_2_0',
234    bsd       => 'BSD',
235    freebsd   => 'FreeBSD',
236    cc0       => 'CC0_1_0',
237    gpl       => 'GPL_2',
238    lgpl      => 'LGPL_2_1',
239    gpl3      => 'GPL_3',
240    lgpl3     => 'LGPL_3_0',
241    agpl3     => 'AGPL_3',
242    apache    => 'Apache_2_0',
243    qpl       => 'QPL_1_0',
244};
245
246sub _license_record {
247    my $self = shift;
248    my $key = $LICENSES->{ $self->{license} };
249    $key = $self->{license} unless defined $key;
250    my $class = $key =~ m/::/ ? $key : "Software::License::$key";
251    {
252        local $@;
253        undef $class unless eval { require_module $class; 1 } and $class->can('new');
254    }
255    unless (defined $class) {
256        require Software::LicenseUtils;
257        ($class) = Software::LicenseUtils->guess_license_from_meta_key($key);
258        return undef unless defined $class;
259    }
260    return $class->new( { holder => $self->{author} } );
261}
262
263sub _license_blurb {
264    my $self = shift;
265
266    my $record = $self->{license_record};
267    my $license_blurb = defined($record) ?
268        $record->notice :
269        <<"EOT";
270This software is Copyright (c) @{[ $self->_thisyear ]} by $self->{author}.
271
272This program is released under the following license:
273
274  $self->{license}
275EOT
276
277    chomp $license_blurb;
278    return $license_blurb;
279}
280
281# _create_module: used by create_modules to build each file and put data in it
282
283sub _create_module {
284    my $self = shift;
285    my $module = shift;
286    my $rtname = shift;
287
288    my @parts = split( /::/, $module );
289    my $filepart = (pop @parts) . '.pm';
290    my @dirparts = ( $self->{basedir}, 'lib', @parts );
291    my $SLASH = q{/};
292    my $manifest_file = join( $SLASH, 'lib', @parts, $filepart );
293    if ( @dirparts ) {
294        my $dir = File::Spec->catdir( @dirparts );
295        if ( not -d $dir ) {
296            make_path $dir;
297            $self->progress( "Created $dir" );
298        }
299    }
300
301    my $module_file = File::Spec->catfile( @dirparts,  $filepart );
302
303    $self->{module_file}{$module} = File::Spec->catfile('lib', @parts, $filepart);
304    $self->create_file( $module_file, $self->module_guts( $module, $rtname ) );
305    $self->progress( "Created $module_file" );
306
307    return $manifest_file;
308}
309
310sub _thisyear {
311    return (localtime())[5] + 1900;
312}
313
314sub _module_to_pm_file {
315    my $self = shift;
316    my $module = shift;
317
318    my @parts = split( /::/, $module );
319    my $pm = pop @parts;
320    my $pm_file = File::Spec->catfile( 'lib', @parts, "${pm}.pm" );
321    $pm_file =~ s{\\}{/}g; # even on Win32, use forward slash
322
323    return $pm_file;
324}
325
326sub _reference_links {
327  return (
328      { nickname => 'RT',
329        title    => 'CPAN\'s request tracker (report bugs here)',
330        link     => 'https://rt.cpan.org/NoAuth/Bugs.html?Dist=%s',
331      },
332      { title    => 'CPAN Ratings',
333        link     => 'https://cpanratings.perl.org/d/%s',
334      },
335      { title    => 'Search CPAN',
336        link     => 'https://metacpan.org/release/%s',
337      },
338    );
339}
340
341=head2 create_Makefile_PL( $main_module )
342
343This will create the Makefile.PL for the distribution, and will use the module
344named in I<$main_module> as the main module of the distribution.
345
346=cut
347
348sub create_Makefile_PL {
349    my $self         = shift;
350    my $main_module  = shift;
351    my $builder_name = 'ExtUtils::MakeMaker';
352    my $output_file  =
353    Module::Starter::BuilderSet->new()->file_for_builder($builder_name);
354    my $fname        = File::Spec->catfile( $self->{basedir}, $output_file );
355
356    $self->create_file(
357        $fname,
358        $self->Makefile_PL_guts(
359            $main_module,
360            $self->_module_to_pm_file($main_module),
361        ),
362    );
363
364    $self->progress( "Created $fname" );
365
366    return $output_file;
367}
368
369=head2 create_MI_Makefile_PL( $main_module )
370
371This will create a Module::Install Makefile.PL for the distribution, and will
372use the module named in I<$main_module> as the main module of the distribution.
373
374=cut
375
376sub create_MI_Makefile_PL {
377    my $self         = shift;
378    my $main_module  = shift;
379    my $builder_name = 'Module::Install';
380    my $output_file  =
381      Module::Starter::BuilderSet->new()->file_for_builder($builder_name);
382    my $fname        = File::Spec->catfile( $self->{basedir}, $output_file );
383
384    $self->create_file(
385        $fname,
386        $self->MI_Makefile_PL_guts(
387            $main_module,
388            $self->_module_to_pm_file($main_module),
389        ),
390    );
391
392    $self->progress( "Created $fname" );
393
394    return $output_file;
395}
396
397=head2 Makefile_PL_guts( $main_module, $main_pm_file )
398
399This method is called by create_Makefile_PL and returns text used to populate
400Makefile.PL; I<$main_pm_file> is the filename of the distribution's main
401module, I<$main_module>.
402
403=cut
404
405sub Makefile_PL_guts {
406    my $self = shift;
407    my $main_module = shift;
408    my $main_pm_file = shift;
409
410    (my $author = "$self->{author} <$self->{email}>") =~ s/'/\'/g;
411
412    my $slname = $self->{license_record} ? $self->{license_record}->meta2_name : $self->{license};
413
414    my $warnings = sprintf 'warnings%s;', ($self->{fatalize} ? " FATAL => 'all'" : '');
415
416    return <<"HERE";
417use $self->{minperl};
418use strict;
419use $warnings
420use ExtUtils::MakeMaker;
421
422my %WriteMakefileArgs = (
423    NAME             => '$main_module',
424    AUTHOR           => q{$author},
425    VERSION_FROM     => '$main_pm_file',
426    ABSTRACT_FROM    => '$main_pm_file',
427    LICENSE          => '$slname',
428    MIN_PERL_VERSION => '$self->{minperl}',
429    CONFIGURE_REQUIRES => {
430        'ExtUtils::MakeMaker' => '0',
431    },
432    TEST_REQUIRES => {
433        'Test::More' => '0',
434    },
435    PREREQ_PM => {
436        #'ABC'              => '1.6',
437        #'Foo::Bar::Module' => '5.0401',
438    },
439    dist  => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
440    clean => { FILES => '$self->{distro}-*' },
441);
442
443# Compatibility with old versions of ExtUtils::MakeMaker
444unless (eval { ExtUtils::MakeMaker->VERSION('6.64'); 1 }) {
445    my \$test_requires = delete \$WriteMakefileArgs{TEST_REQUIRES} || {};
446    \@{\$WriteMakefileArgs{PREREQ_PM}}{keys %\$test_requires} = values %\$test_requires;
447}
448
449unless (eval { ExtUtils::MakeMaker->VERSION('6.55_03'); 1 }) {
450    my \$build_requires = delete \$WriteMakefileArgs{BUILD_REQUIRES} || {};
451    \@{\$WriteMakefileArgs{PREREQ_PM}}{keys %\$build_requires} = values %\$build_requires;
452}
453
454delete \$WriteMakefileArgs{CONFIGURE_REQUIRES}
455    unless eval { ExtUtils::MakeMaker->VERSION('6.52'); 1 };
456delete \$WriteMakefileArgs{MIN_PERL_VERSION}
457    unless eval { ExtUtils::MakeMaker->VERSION('6.48'); 1 };
458delete \$WriteMakefileArgs{LICENSE}
459    unless eval { ExtUtils::MakeMaker->VERSION('6.31'); 1 };
460
461WriteMakefile(%WriteMakefileArgs);
462HERE
463
464}
465
466=head2 MI_Makefile_PL_guts( $main_module, $main_pm_file )
467
468This method is called by create_MI_Makefile_PL and returns text used to populate
469Makefile.PL; I<$main_pm_file> is the filename of the distribution's main
470module, I<$main_module>.
471
472=cut
473
474sub MI_Makefile_PL_guts {
475    my $self = shift;
476    my $main_module = shift;
477    my $main_pm_file = shift;
478
479    my $author = "$self->{author} <$self->{email}>";
480    $author =~ s/'/\'/g;
481
482    my $license_url = $self->{license_record} ? $self->{license_record}->url : '';
483
484    my $warnings = sprintf 'warnings%s;', ($self->{fatalize} ? " FATAL => 'all'" : '');
485
486    return <<"HERE";
487use $self->{minperl};
488use strict;
489use $warnings
490use inc::Module::Install;
491
492name     '$self->{distro}';
493all_from '$main_pm_file';
494author   q{$author};
495license  '$self->{license}';
496
497perl_version '$self->{minperl}';
498
499tests_recursive('t');
500
501resources (
502   #homepage   => 'http://yourwebsitehere.com',
503   #IRC        => 'irc://irc.perl.org/#$self->{distro}',
504   license    => '$license_url',
505   #repository => 'git://github.com/$self->{author}/$self->{distro}.git',
506   #repository => 'https://bitbucket.org/$self->{author}/$self->{distro}',
507   bugtracker => 'https://rt.cpan.org/NoAuth/Bugs.html?Dist=$self->{distro}',
508);
509
510configure_requires (
511   'Module::Install' => '0',
512);
513
514test_requires (
515   'Test::More' => '0',
516);
517
518requires (
519   #'ABC'              => '1.6',
520   #'Foo::Bar::Module' => '5.0401',
521);
522
523install_as_cpan;
524auto_install;
525WriteAll;
526HERE
527
528}
529
530=head2 create_Build_PL( $main_module )
531
532This will create the Build.PL for the distribution, and will use the module
533named in I<$main_module> as the main module of the distribution.
534
535=cut
536
537sub create_Build_PL {
538    my $self         = shift;
539    my $main_module  = shift;
540    my $builder_name = 'Module::Build';
541    my $output_file  =
542      Module::Starter::BuilderSet->new()->file_for_builder($builder_name);
543    my $fname        = File::Spec->catfile( $self->{basedir}, $output_file );
544
545    $self->create_file(
546        $fname,
547        $self->Build_PL_guts(
548            $main_module,
549            $self->_module_to_pm_file($main_module),
550        ),
551    );
552
553    $self->progress( "Created $fname" );
554
555    return $output_file;
556}
557
558=head2 Build_PL_guts( $main_module, $main_pm_file )
559
560This method is called by create_Build_PL and returns text used to populate
561Build.PL; I<$main_pm_file> is the filename of the distribution's main module,
562I<$main_module>.
563
564=cut
565
566sub Build_PL_guts {
567    my $self = shift;
568    my $main_module = shift;
569    my $main_pm_file = shift;
570
571    (my $author = "$self->{author} <$self->{email}>") =~ s/'/\'/g;
572
573    my $slname = $self->{license_record} ? $self->{license_record}->meta2_name : $self->{license};
574
575    my $warnings = sprintf 'warnings%s;', ($self->{fatalize} ? " FATAL => 'all'" : '');
576
577    return <<"HERE";
578use $self->{minperl};
579use strict;
580use $warnings
581use Module::Build;
582Module::Build->VERSION('0.4004');
583
584my \$builder = Module::Build->new(
585    module_name         => '$main_module',
586    license             => '$slname',
587    dist_author         => q{$author},
588    dist_version_from   => '$main_pm_file',
589    release_status      => 'stable',
590    configure_requires => {
591        'Module::Build' => '0.4004',
592    },
593    test_requires => {
594        'Test::More' => '0',
595    },
596    requires => {
597        #'ABC'              => '1.6',
598        #'Foo::Bar::Module' => '5.0401',
599    },
600    add_to_cleanup     => [ '$self->{distro}-*' ],
601);
602
603\$builder->create_build_script();
604HERE
605
606}
607
608=head2 create_Changes( )
609
610This method creates a skeletal Changes file.
611
612=cut
613
614sub create_Changes {
615    my $self = shift;
616
617    my $fname = File::Spec->catfile( $self->{basedir}, 'Changes' );
618    $self->create_file( $fname, $self->Changes_guts() );
619    $self->progress( "Created $fname" );
620
621    return 'Changes';
622}
623
624=head2 Changes_guts
625
626Called by create_Changes, this method returns content for the Changes file.
627
628=cut
629
630sub Changes_guts {
631    my $self = shift;
632
633    return <<"HERE";
634Revision history for $self->{distro}
635
6360.01    Date/time
637        First version, released on an unsuspecting world.
638
639HERE
640}
641
642=head2 create_LICENSE
643
644This method creates the distribution's LICENSE file.
645
646=cut
647
648sub create_LICENSE {
649    my $self = shift;
650
651    my $record = $self->{license_record} || return ();
652    my $fname = File::Spec->catfile( $self->{basedir}, 'LICENSE' );
653    $self->create_file( $fname, $record->license );
654    $self->progress( "Created $fname" );
655
656    return 'LICENSE';
657}
658
659=head2 create_README( $build_instructions )
660
661This method creates the distribution's README file.
662
663=cut
664
665sub create_README {
666    my $self = shift;
667    my $build_instructions = shift;
668
669    my $fname = File::Spec->catfile( $self->{basedir}, 'README' );
670    $self->create_file( $fname, $self->README_guts($build_instructions) );
671    $self->progress( "Created $fname" );
672
673    return 'README';
674}
675
676=head2 README_guts
677
678Called by create_README, this method returns content for the README file.
679
680=cut
681
682sub _README_intro {
683    my $self = shift;
684
685    return <<"HERE";
686The README is used to introduce the module and provide instructions on
687how to install the module, any machine dependencies it may have (for
688example C compilers and installed libraries) and any other information
689that should be provided before the module is installed.
690
691A README file is required for CPAN modules since CPAN extracts the README
692file from a module distribution so that people browsing the archive
693can use it to get an idea of the module's uses. It is usually a good idea
694to provide version information here so that people can decide whether
695fixes for the module are worth downloading.
696HERE
697}
698
699sub _README_information {
700    my $self = shift;
701
702    my @reference_links = _reference_links();
703
704    my $content = "You can also look for information at:\n";
705
706    foreach my $ref (@reference_links){
707        my $title;
708        $title = "$ref->{nickname}, " if exists $ref->{nickname};
709        $title .= $ref->{title};
710        my $link  = sprintf($ref->{link}, $self->{distro});
711
712        $content .= qq[
713    $title
714        $link
715];
716    }
717
718    return $content;
719}
720
721sub _README_license {
722    my $self = shift;
723
724    my $license_blurb = $self->_license_blurb();
725
726return <<"HERE";
727LICENSE AND COPYRIGHT
728
729$license_blurb
730HERE
731}
732
733sub README_guts {
734    my $self = shift;
735    my $build_instructions = shift;
736
737    my $intro         = $self->_README_intro();
738    my $information   = $self->_README_information();
739    my $license       = $self->_README_license();
740
741return <<"HERE";
742$self->{distro}
743
744$intro
745
746INSTALLATION
747
748$build_instructions
749
750SUPPORT AND DOCUMENTATION
751
752After installing, you can find documentation for this module with the
753perldoc command.
754
755    perldoc $self->{main_module}
756
757$information
758
759$license
760HERE
761}
762
763=head2 create_t( @modules )
764
765This method creates a bunch of *.t files.  I<@modules> is a list of all modules
766in the distribution.
767
768=cut
769
770sub create_t {
771    my $self = shift;
772    my @modules = @_;
773
774    my %t_files  = $self->t_guts(@modules);
775    my %xt_files = $self->xt_guts(@modules);
776
777    my @files;
778    push @files, map { $self->_create_t('t',  $_, $t_files{$_}) }  keys %t_files;
779    push @files, map { $self->_create_t('xt', $_, $xt_files{$_}) } keys %xt_files;
780
781    return @files;
782}
783
784=head2 t_guts( @modules )
785
786This method is called by create_t, and returns a description of the *.t files
787to be created.
788
789The return value is a hash of test files to create.  Each key is a filename and
790each value is the contents of that file.
791
792=cut
793
794sub t_guts {
795    my $self = shift;
796    my @modules = @_;
797
798    my %t_files;
799    my $minperl = $self->{minperl};
800    my $warnings = sprintf 'warnings%s;', ($self->{fatalize} ? " FATAL => 'all'" : '');
801
802    my $header = <<"EOH";
803#!perl
804use $minperl;
805use strict;
806use $warnings
807use Test::More;
808
809EOH
810
811    $t_files{'pod.t'} = $header.<<'HERE';
812unless ( $ENV{RELEASE_TESTING} ) {
813    plan( skip_all => "Author tests not required for installation" );
814}
815
816# Ensure a recent version of Test::Pod
817my $min_tp = 1.22;
818eval "use Test::Pod $min_tp";
819plan skip_all => "Test::Pod $min_tp required for testing POD" if $@;
820
821all_pod_files_ok();
822HERE
823
824    $t_files{'manifest.t'} = $header.<<'HERE';
825unless ( $ENV{RELEASE_TESTING} ) {
826    plan( skip_all => "Author tests not required for installation" );
827}
828
829my $min_tcm = 0.9;
830eval "use Test::CheckManifest $min_tcm";
831plan skip_all => "Test::CheckManifest $min_tcm required" if $@;
832
833ok_manifest();
834HERE
835
836    $t_files{'pod-coverage.t'} = $header.<<'HERE';
837unless ( $ENV{RELEASE_TESTING} ) {
838    plan( skip_all => "Author tests not required for installation" );
839}
840
841# Ensure a recent version of Test::Pod::Coverage
842my $min_tpc = 1.08;
843eval "use Test::Pod::Coverage $min_tpc";
844plan skip_all => "Test::Pod::Coverage $min_tpc required for testing POD coverage"
845    if $@;
846
847# Test::Pod::Coverage doesn't require a minimum Pod::Coverage version,
848# but older versions don't recognize some common documentation styles
849my $min_pc = 0.18;
850eval "use Pod::Coverage $min_pc";
851plan skip_all => "Pod::Coverage $min_pc required for testing POD coverage"
852    if $@;
853
854all_pod_coverage_ok();
855HERE
856
857    my $nmodules = @modules;
858    my $main_module = $modules[0];
859    my $use_lines = join(
860        "\n", map { qq{    use_ok( '$_' ) || print "Bail out!\\n";} } @modules
861    );
862
863    $t_files{'00-load.t'} = $header.<<"HERE";
864plan tests => $nmodules;
865
866BEGIN {
867$use_lines
868}
869
870diag( "Testing $main_module \$${main_module}::VERSION, Perl \$], \$^X" );
871HERE
872
873    return %t_files;
874}
875
876=head2 xt_guts( @modules )
877
878This method is called by create_t, and returns a description of the author
879only *.t files to be created in the xt directory.
880
881The return value is a hash of test files to create.  Each key is a filename and
882each value is the contents of that file.
883
884=cut
885
886sub xt_guts {
887    my $self = shift;
888    my @modules = @_;
889
890    my %xt_files;
891    my $minperl = $self->{minperl};
892    my $warnings = sprintf 'warnings%s;', ($self->{fatalize} ? " FATAL => 'all'" : '');
893
894    my $header = <<"EOH";
895#!perl
896use $minperl;
897use strict;
898use $warnings
899use Test::More;
900
901EOH
902
903    my $module_boilerplate_tests;
904    $module_boilerplate_tests .=
905      "  module_boilerplate_ok('".$self->_module_to_pm_file($_)."');\n" for @modules;
906
907    my $boilerplate_tests = @modules + 2;
908    $xt_files{'boilerplate.t'} = $header.<<"HERE";
909plan tests => $boilerplate_tests;
910
911sub not_in_file_ok {
912    my (\$filename, \%regex) = \@_;
913    open( my \$fh, '<', \$filename )
914        or die "couldn't open \$filename for reading: \$!";
915
916    my \%violated;
917
918    while (my \$line = <\$fh>) {
919        while (my (\$desc, \$regex) = each \%regex) {
920            if (\$line =~ \$regex) {
921                push \@{\$violated{\$desc}||=[]}, \$.;
922            }
923        }
924    }
925
926    if (\%violated) {
927        fail("\$filename contains boilerplate text");
928        diag "\$_ appears on lines \@{\$violated{\$_}}" for keys \%violated;
929    } else {
930        pass("\$filename contains no boilerplate text");
931    }
932}
933
934sub module_boilerplate_ok {
935    my (\$module) = \@_;
936    not_in_file_ok(\$module =>
937        'the great new \$MODULENAME'   => qr/ - The great new /,
938        'boilerplate description'     => qr/Quick summary of what the module/,
939        'stub function definition'    => qr/function[12]/,
940    );
941}
942
943TODO: {
944  local \$TODO = "Need to replace the boilerplate text";
945
946  not_in_file_ok(README =>
947    "The README is used..."       => qr/The README is used/,
948    "'version information here'"  => qr/to provide version information/,
949  );
950
951  not_in_file_ok(Changes =>
952    "placeholder date/time"       => qr(Date/time)
953  );
954
955$module_boilerplate_tests
956
957}
958
959HERE
960
961    return %xt_files;
962}
963
964sub _create_t {
965    my $self = shift;
966    my $directory = shift;  # 't' or 'xt'
967    my $filename = shift;
968    my $content = shift;
969
970    my @dirparts = ( $self->{basedir}, $directory );
971    my $tdir = File::Spec->catdir( @dirparts );
972    if ( not -d $tdir ) {
973        make_path $tdir;
974        $self->progress( "Created $tdir" );
975    }
976
977    my $fname = File::Spec->catfile( @dirparts, $filename );
978    $self->create_file( $fname, $content );
979    $self->progress( "Created $fname" );
980
981    return join('/', $directory, $filename );
982}
983
984=head2 create_MB_MANIFEST
985
986This methods creates a MANIFEST file using Module::Build's methods.
987
988=cut
989
990sub create_MB_MANIFEST {
991    my $self = shift;
992    $self->create_EUMM_MANIFEST;
993}
994
995=head2 create_MI_MANIFEST
996
997This method creates a MANIFEST file using Module::Install's methods.
998
999Currently runs ExtUtils::MakeMaker's methods.
1000
1001=cut
1002
1003sub create_MI_MANIFEST {
1004    my $self = shift;
1005    $self->create_EUMM_MANIFEST;
1006}
1007
1008=head2 create_EUMM_MANIFEST
1009
1010This method creates a MANIFEST file using ExtUtils::MakeMaker's methods.
1011
1012=cut
1013
1014sub create_EUMM_MANIFEST {
1015    my $self     = shift;
1016    my $orig_dir = cwd();
1017
1018    # create the MANIFEST in the correct path
1019    chdir $self->{'basedir'} || die "Can't reach basedir: $!\n";
1020
1021    require ExtUtils::Manifest;
1022    $ExtUtils::Manifest::Quiet = 0;
1023    ExtUtils::Manifest::mkmanifest();
1024
1025    # return to our original path, wherever it was
1026    chdir $orig_dir || die "Can't return to original dir: $!\n";
1027}
1028
1029=head2 create_MANIFEST( $method )
1030
1031This method creates the distribution's MANIFEST file.  It must be run last,
1032because all the other create_* functions have been returning the functions they
1033create.
1034
1035It receives a method to run in order to create the MANIFEST file. That way it
1036can create a MANIFEST file according to the builder used.
1037
1038=cut
1039
1040sub create_MANIFEST {
1041    my ( $self, $manifest_method ) = @_;
1042    my $fname = File::Spec->catfile( $self->{basedir}, 'MANIFEST' );
1043
1044    $self->$manifest_method();
1045    $self->filter_lines_in_file(
1046        $fname,
1047        qr/^xt\/boilerplate\.t$/,
1048        qr/^ignore\.txt$/,
1049    );
1050
1051    $self->progress( "Created $fname" );
1052
1053    return 'MANIFEST';
1054}
1055
1056=head2 get_builders( )
1057
1058This methods gets the correct builder(s).
1059
1060It is called by C<create_build>, and returns an arrayref with the builders.
1061
1062=cut
1063
1064sub get_builders {
1065    my $self = shift;
1066
1067    # pass one: pull the builders out of $self->{builder}
1068    my @tmp =
1069        ref $self->{'builder'} eq 'ARRAY' ? @{ $self->{'builder'} }
1070                                          : $self->{'builder'};
1071
1072    my @builders;
1073    my $COMMA = q{,};
1074    # pass two: expand comma-delimited builder lists
1075    foreach my $builder (@tmp) {
1076        push( @builders, split( $COMMA, $builder ) );
1077    }
1078
1079    return \@builders;
1080}
1081
1082=head2 create_build( )
1083
1084This method creates the build file(s) and puts together some build
1085instructions.  The builders currently supported are:
1086
1087ExtUtils::MakeMaker
1088Module::Build
1089Module::Install
1090
1091=cut
1092
1093sub create_build {
1094    my $self = shift;
1095
1096    # get the builders
1097    my @builders    = @{ $self->get_builders };
1098    my $builder_set = Module::Starter::BuilderSet->new();
1099
1100    # Remove mutually exclusive and unsupported builders
1101    @builders = $builder_set->check_compatibility( @builders );
1102
1103    # compile some build instructions, create a list of files generated
1104    # by the builders' create_* methods, and call said methods
1105
1106    my @build_instructions;
1107    my @files;
1108    my $manifest_method;
1109
1110    foreach my $builder ( @builders ) {
1111        if ( !@build_instructions ) {
1112            push( @build_instructions,
1113                'To install this module, run the following commands:'
1114            );
1115        }
1116        else {
1117            push( @build_instructions,
1118                "Alternatively, to install with $builder, you can ".
1119                "use the following commands:"
1120            );
1121        }
1122        push( @files, $builder_set->file_for_builder($builder) );
1123        my @commands = $builder_set->instructions_for_builder($builder);
1124        push( @build_instructions, join("\n", map { "\t$_" } @commands) );
1125
1126        my $build_method = $builder_set->method_for_builder($builder);
1127        $self->$build_method($self->{main_module});
1128
1129        $manifest_method = $builder_set->manifest_method($builder);
1130    }
1131
1132    return(
1133        files           => [ @files ],
1134        instructions    => join( "\n\n", @build_instructions ),
1135        manifest_method => $manifest_method,
1136    );
1137}
1138
1139
1140=head2 create_ignores()
1141
1142This creates a text file for use as MANIFEST.SKIP, .cvsignore,
1143.gitignore, or whatever you use.
1144
1145=cut
1146
1147sub create_ignores {
1148    my $self  = shift;
1149    my $type  = $self->{ignores_type};
1150    my %names = (
1151        generic  => 'ignore.txt',
1152        cvs      => '.cvsignore',
1153        git      => '.gitignore',
1154        hg       => '.hgignore',
1155        manifest => 'MANIFEST.SKIP',
1156    );
1157
1158    my $create_file = sub {
1159        my $type  = shift;
1160        my $name  = $names{$type};
1161        my $fname = File::Spec->catfile( $self->{basedir}, $names{$type} );
1162        $self->create_file( $fname, $self->ignores_guts($type) );
1163        $self->progress( "Created $fname" );
1164    };
1165
1166    if ( ref $type eq 'ARRAY' ) {
1167        foreach my $single_type ( @{$type} ) {
1168            $create_file->($single_type);
1169        }
1170    } elsif ( ! ref $type ) {
1171        $create_file->($type);
1172    }
1173
1174    return; # Not a file that goes in the MANIFEST
1175}
1176
1177=head2 ignores_guts()
1178
1179Called by C<create_ignores>, this method returns the contents of the
1180ignore file.
1181
1182=cut
1183
1184sub ignores_guts {
1185    my ($self, $type) = @_;
1186
1187    my $ms = $self->{manifest_skip} ? "MANIFEST\nMANIFEST.bak\n" : '';
1188    my $guts = {
1189        generic => $ms.<<"EOF",
1190Makefile
1191Makefile.old
1192Build
1193Build.bat
1194META.*
1195MYMETA.*
1196.build/
1197_build/
1198cover_db/
1199blib/
1200inc/
1201.lwpcookies
1202.last_cover_stats
1203nytprof.out
1204pod2htm*.tmp
1205pm_to_blib
1206$self->{distro}-*
1207$self->{distro}-*.tar.gz
1208EOF
1209        # make this more restrictive, since MANIFEST tends to be less noticeable
1210        # (also, manifest supports REs.)
1211        manifest => <<'EOF',
1212# Top-level filter (only include the following...)
1213^(?!(?:script|examples|lib|inc|t|xt|maint)/|(?:(?:Makefile|Build)\.PL|README|LICENSE|MANIFEST|Changes|META\.(?:yml|json))$)
1214
1215# Avoid version control files.
1216\bRCS\b
1217\bCVS\b
1218,v$
1219\B\.svn\b
1220\b_darcs\b
1221# (.git or .hg only in top-level, hence it's blocked above)
1222
1223# Avoid temp and backup files.
1224~$
1225\.tmp$
1226\.old$
1227\.bak$
1228\..*?\.sw[po]$
1229\#$
1230\b\.#
1231
1232# avoid OS X finder files
1233\.DS_Store$
1234
1235# ditto for Windows
1236\bdesktop\.ini$
1237\b[Tt]humbs\.db$
1238
1239# Avoid patch remnants
1240\.orig$
1241\.rej$
1242EOF
1243    };
1244    $guts->{hg} = $guts->{cvs} = $guts->{git} = $guts->{generic};
1245
1246    return $guts->{$type};
1247}
1248
1249=head1 HELPER METHODS
1250
1251=head2 verbose
1252
1253C<verbose> tells us whether we're in verbose mode.
1254
1255=cut
1256
1257sub verbose { return shift->{verbose} }
1258
1259=head2 create_file( $fname, @content_lines )
1260
1261Creates I<$fname>, dumps I<@content_lines> in it, and closes it.
1262Dies on any error.
1263
1264=cut
1265
1266sub create_file {
1267    my $self = shift;
1268    my $fname = shift;
1269
1270    my @content = @_;
1271    open( my $fh, '>', $fname ) or confess "Can't create $fname: $!\n";
1272    print {$fh} @content;
1273    close $fh or die "Can't close $fname: $!\n";
1274
1275    return;
1276}
1277
1278=head2 progress( @list )
1279
1280C<progress> prints the given progress message if we're in verbose mode.
1281
1282=cut
1283
1284sub progress {
1285    my $self = shift;
1286    print @_, "\n" if $self->verbose;
1287
1288    return;
1289}
1290
1291=head2 filter_lines_in_file( $filename, @compiled_regexes )
1292
1293C<filter_lines_in_file> goes over a file and removes lines with the received
1294regexes.
1295
1296For example, removing t/boilerplate.t in the MANIFEST.
1297
1298=cut
1299
1300sub filter_lines_in_file {
1301    my ( $self, $file, @regexes ) = @_;
1302    my @read_lines;
1303    open my $fh, '<', $file or die "Can't open file $file: $!\n";
1304    @read_lines = <$fh>;
1305    close $fh or die "Can't close file $file: $!\n";
1306
1307    chomp @read_lines;
1308
1309    open $fh, '>', $file or die "Can't open file $file: $!\n";
1310    foreach my $line (@read_lines) {
1311        my $found;
1312
1313        foreach my $regex (@regexes) {
1314            if ( $line =~ $regex ) {
1315                $found++;
1316            }
1317        }
1318
1319        $found or print {$fh} "$line\n";
1320    }
1321    close $fh or die "Can't close file $file: $!\n";
1322}
1323
1324=head1 BUGS
1325
1326Please report any bugs or feature requests to the bugtracker for this project
1327on GitHub at: L<https://github.com/xsawyerx/module-starter/issues>. I will be
1328notified, and then you'll automatically be notified of progress on your bug
1329as I make changes.
1330
1331=head1 AUTHOR
1332
1333Dan Book, L<< <dbook@cpan.org> >>
1334
1335Sawyer X, C<< <xsawyerx@cpan.org> >>
1336
1337Andy Lester, C<< <andy@petdance.com> >>
1338
1339C.J. Adams-Collier, C<< <cjac@colliertech.org> >>
1340
1341=head1 Copyright & License
1342
1343Copyright 2005-2009 Andy Lester and C.J. Adams-Collier, All Rights Reserved.
1344
1345Copyright 2010 Sawyer X, All Rights Reserved.
1346
1347This program is free software; you can redistribute it and/or modify it
1348under the same terms as Perl itself.
1349
1350Please note that these modules are not products of or supported by the
1351employers of the various contributors to the code.
1352
1353=cut
1354
1355sub _module_header {
1356    my $self = shift;
1357    my $module = shift;
1358    my $rtname = shift;
1359    my $warnings = sprintf 'warnings%s;', ($self->{fatalize} ? " FATAL => 'all'" : '');
1360
1361    my $content = <<"HERE";
1362package $module;
1363
1364use $self->{minperl};
1365use strict;
1366use $warnings
1367
1368\=head1 NAME
1369
1370$module - The great new $module!
1371
1372\=head1 VERSION
1373
1374Version 0.01
1375
1376\=cut
1377
1378our \$VERSION = '0.01';
1379HERE
1380    return $content;
1381}
1382
1383sub _module_bugs {
1384    my $self   = shift;
1385    my $module = shift;
1386    my $rtname = shift;
1387
1388    my $bug_email = "bug-\L$self->{distro}\E at rt.cpan.org";
1389    my $bug_link  =
1390      "https://rt.cpan.org/NoAuth/ReportBug.html?Queue=$self->{distro}";
1391
1392    my $content = <<"HERE";
1393\=head1 BUGS
1394
1395Please report any bugs or feature requests to C<$bug_email>, or through
1396the web interface at L<$bug_link>.  I will be notified, and then you'll
1397automatically be notified of progress on your bug as I make changes.
1398
1399HERE
1400
1401    return $content;
1402}
1403
1404sub _module_support {
1405    my $self   = shift;
1406    my $module = shift;
1407    my $rtname = shift;
1408
1409    my $content = qq[
1410\=head1 SUPPORT
1411
1412You can find documentation for this module with the perldoc command.
1413
1414    perldoc $module
1415];
1416    my @reference_links = _reference_links();
1417
1418    return undef unless @reference_links;
1419    $content .= qq[
1420
1421You can also look for information at:
1422
1423\=over 4
1424];
1425
1426    foreach my $ref (@reference_links) {
1427        my $title;
1428        my $link = sprintf($ref->{link}, $self->{distro});
1429
1430        $title = "$ref->{nickname}: " if exists $ref->{nickname};
1431        $title .= $ref->{title};
1432        $content .= qq[
1433\=item * $title
1434
1435L<$link>
1436];
1437    }
1438    $content .= qq[
1439\=back
1440];
1441    return $content;
1442}
1443
1444sub _module_license {
1445    my $self = shift;
1446
1447    my $module = shift;
1448    my $rtname = shift;
1449
1450    my $license_blurb = $self->_license_blurb();
1451
1452    my $content = qq[
1453\=head1 LICENSE AND COPYRIGHT
1454
1455$license_blurb
1456];
1457
1458    return $content;
1459}
1460
1461sub module_guts {
1462    my $self = shift;
1463    my $module = shift;
1464    my $rtname = shift;
1465
1466    # Sub-templates
1467    my $header  = $self->_module_header($module, $rtname);
1468    my $bugs    = $self->_module_bugs($module, $rtname);
1469    my $support = $self->_module_support($module, $rtname);
1470    my $license = $self->_module_license($module, $rtname);
1471
1472    my $content = <<"HERE";
1473$header
1474
1475\=head1 SYNOPSIS
1476
1477Quick summary of what the module does.
1478
1479Perhaps a little code snippet.
1480
1481    use $module;
1482
1483    my \$foo = $module->new();
1484    ...
1485
1486\=head1 EXPORT
1487
1488A list of functions that can be exported.  You can delete this section
1489if you don't export anything, such as for a purely object-oriented module.
1490
1491\=head1 SUBROUTINES/METHODS
1492
1493\=head2 function1
1494
1495\=cut
1496
1497sub function1 {
1498}
1499
1500\=head2 function2
1501
1502\=cut
1503
1504sub function2 {
1505}
1506
1507\=head1 AUTHOR
1508
1509$self->{author}, C<< <$self->{email_obfuscated}> >>
1510
1511$bugs
1512
1513$support
1514
1515\=head1 ACKNOWLEDGEMENTS
1516
1517$license
1518
1519\=cut
1520
15211; # End of $module
1522HERE
1523    return $content;
1524}
1525
15261;
1527
1528# vi:et:sw=4 ts=4
1529