Perl Advent Calendar 2006-12-06

Santa's Little Helper

by Jeff Lavallee

Everyone can use a helping hand

I'm sure even Santa's elves appreciate any head start they can get when they start building a new toy, and Module::Starter is a great way to get a leg up when crafting a new Perl module.

What Is It?

Module::Starter is a little different from most Perl modules. Typically, you wouldn't use it directly. When installed, it provides the module-starter script. The script will create the basic framework of a module suitable for distribution. If you're new to creating modules for CPAN this can be a great help.

What about good old h2xs?

h2xs predates Module::Starter, and is mainly designed to get a head start when creating a module from C header files. It can be used to create a pure-Perl module via the -X option, however the module skeleton that it creates is not as complete as what is provided by Module::Starter. h2xs also assumes that the module you're creating will be based on Exporter, and its generated documentation also requires significantly more editing.

A Few Options

module-starter accepts a few different options to control its behavior. Here are a few of the most important:

--module=module Module name (required)
The name of the module you'd like to create.
--author=name Author's name (required) & --email=address Author's email (required)
Give credit where credit is due.

--builder=module Build with 'ExtUtils::MakeMaker' or 'Module::Build'
These options control what style of module building system will be used. Installation of an ExtUtils::MakeMaker based module uses the traditional
perl Makefile.PL; make; make install
incantation whereas Module::Build uses the new-fangled, make-free
perl Build.PL; ./Build; ./Build install
If you're new to creating modules, the latter can be a friendlier way to go.

An Example:

Let's look at what it does, using a hypothetical Toy::Factory as an example:

 santa@northpole:~ $ module-starter --verbose --module=Toy::Factory \
 --author="Kris Kringle" --builder=Module::Build
 Created Toy-Factory
 Created Toy-Factory/lib/Toy
 Created Toy-Factory/lib/Toy/
 Created Toy-Factory/t
 Created Toy-Factory/t/pod-coverage.t
 Created Toy-Factory/t/pod.t
 Created Toy-Factory/t/boilerplate.t
 Created Toy-Factory/t/00-load.t
 Created Toy-Factory/.cvsignore
 Created Toy-Factory/Build.PL
 Created Toy-Factory/MANIFEST
 Created starter directories and files

And so module-starter created a skeleton for us to start working with, complete with sample tests. Can we build it?

 santa@northpole:~ $ cd Toy-Factory
 santa@northpole:~/Toy-Factory $ perl Build.PL
 Checking whether your kit is complete...
 WARNING: the following files are missing in your kit:
 Please inform the author.

 Checking prerequisites...
 Looks good

 Creating new 'Build' script for 'Toy-Factory' version '0.01'
 santa@northpole:~/Toy-Factory $ ./Build
 lib/Toy/ -> blib/lib/Toy/
 Manifying blib/lib/Toy/ -> blib/libdoc/Toy::Factory.3pm

Yes we can! But I wonder if it passes its tests?

 santa@northpole:~/Toy-Factory $ ./Build test
 t/00-load.........ok 1/1# Testing Toy::Factory 0.01, Perl 5.008006, /usr/bin/perl
 t/boilerplate.....NOK 1/3                                                   
 #   Failed test 'README contains boilerplate text'
 #   in t/boilerplate.t at line 23.
 # The README is used... appears on lines 3
 # 'version information here' appears on lines 11
 t/boilerplate.....NOK 2/3                                                   
 #   Failed test 'Changes contains boilerplate text'
 #   in t/boilerplate.t at line 23.
 # placeholder date/time appears on lines 3
 t/boilerplate.....NOK 3/3                                                   
 #   Failed test 'lib/Toy/ contains boilerplate text'
 #   in t/boilerplate.t at line 23.
 # stub function definition appears on lines 36 40 43 47
 # boilerplate description appears on lines 20
 # the great new $MODULENAME appears on lines 8
 # Looks like you failed 3 tests of 3.
         Test returned status 3 (wstat 768, 0x300)
 DIED. FAILED tests 1-3
         Failed 3/3 tests, 0.00% okay
 Failed Test     Stat Wstat Total Fail  List of Failed
 t/boilerplate.t    3   768     3    3  1-3
 Failed 1/4 test scripts. 3/6 subtests failed.
 Files=4, Tests=6,  2 wallclock secs ( 0.84 cusr +  0.21 csys =  1.05 CPU)
 Failed 1/4 test programs. 3/6 subtests failed.

Doh! As a part of the framework it provided module-starter created sample README and Changes files, as well as tests to check these files and ensure that we've replaced the default text with something more meaningful. If you've ever noticed a module on CPAN that claims to be a "Perl extension for blah blah blah" you can appreciate how these tests would have come in handy. In addition, these POD tests will raise your module's Kwalitee score on CPANTS.

After editing lib/Toy/, README and Changes appropriately let's try this again:

 santa@northpole:~/Toy-Factory $ ./Build test
 Deleting blib/lib/Toy/
 lib/Toy/ -> blib/lib/Toy/
 t/00-load.........ok 1/1# Testing Toy::Factory 0.01, Perl 5.008006, /usr/bin/perl
 All tests successful.
 Files=4, Tests=6,  2 wallclock secs ( 0.83 cusr +  0.22 csys =  1.05 CPU)

Great! We're all set to teach Toy::Factory how to make toys. But what about that warning we got when we first built it, about META.yml missing? META.yml is created auotmagically when you issue the ./Build dist command.

 santa@northpole:~/Toy-Factory $ ./Build dist
 Creating META.yml
 Creating Toy-Factory-0.01
 Creating Toy-Factory-0.01.tar.gz
 tar cf Toy-Factory-0.01.tar Toy-Factory-0.01
 gzip Toy-Factory-0.01.tar
 Deleting Toy-Factory-0.01

Now we won't get that pesky error the next time we build! This also created the tarball we'd use to distribute our module, Toy-Factory-0.01.tar.gz.

 santa@northpole:~/Toy-Factory $ perl Build.PL
 Checking whether your kit is complete...
 Looks good

 Checking prerequisites...
 Looks good

 Deleting Build
 Removed previous script 'Build'

 Creating new 'Build' script for 'Toy-Factory' version '0.01'

If only building a real toy factory was so easy!

Editor's note: Module::Starter is pluggable, and you could therefore create a subclass which generates templates suited to your organization's needs.