Sam's Blog

Custom minting with Module::Starter and Test::XT

Date: Thursday, 26 August 2010, 18:49.

Categories: perl, ironman, module-authoring, tutorial, intermediate, module-starter, test-xt.

In the comments to a previous article, I mentioned that I use Module::Starter when starting a new distribution.

I actually use a custom profile along with the very handy Test::XT to produce robust release tests that won't break when people are just trying to install with the minimum fuss.

I thought it might be useful for other people if I wrote a quick tutorial on how to achieve the same results themselves.

Basic Installation

First install the modules needed from CPAN, get to your preferred CPAN prompt ("sudo cpan" or whatever your usual method is) and type the following:

cpan> install Module::Starter Module::Starter::Plugin::DirStore Module::Starter::Plugin::TT2 Test::XT cpan> exit

Configuring Module::Starter

Now we need to create the config directory for Module::Starter, at your shell prompt:

$ mkdir -p ~/.module-starter/skel

Next edit ~/.module-starter/config and insert the following, with appropriate author, email and template_dir settings:

author: Sam Graham email: YOUREMAIL@YOURDOMAIN builder: Module::Build plugins: Module::Starter::Simple Module::Starter::Plugin::Template Module::Starter::Plugin::DirStore Module::Starter::Plugin::TT2 template_dir: /home/illusori/.module-starter/skel template_parms: ''

What we've done here is alter the default behaviour of module-starter so that it will run Template::Toolkit on the files in your skeleton dir, and use those files when minting your new distribution.

Of course, we don't actually have any files in there, so let's change that, first copy the files from template/dir in the Module::Starter::Plugin::TT2 distribution.

If you're not sure where they ended up you can do the following to get them:

$ wget $ tar -xzf Module-Starter-Plugin-TT2-0.125.tar.gz $ cp Module-Starter-Plugin-TT2-0.125/templates/dir/* ~/.module-starter/skel/ $ rm -rf Module-Starter-Plugin-TT2-0.125 $ rm Module-Starter-Plugin-TT2-0.125.tar.gz

Now two of these files you're going to create improved versions of, so it's best to clear them out of the way now:

$ rm ~/module-starter/skel/pod*.t

You now have a basic set of templates that will be installed into any newly minted distribution, you'll probably want to edit them slightly for taste, I find that the default Build.PL doesn't fit my standard changes, so I edit it to this:

use strict;
use warnings;
use Module::Build;

my $builder = Module::Build->new(
    module_name         => '[%main_module%]',
    license             => '[%self.license%]',
    dist_author         => '[] <[]>',
    dist_version_from   => '[%main_pm_file%]',
    build_requires => {
        'Test::More' => 0,
    requires => {
    sign => 1,
    dynamic_config => 0,


This makes use of build_requires rather than requires and turns on signing and sets the dynamic_config flag to off.

And for which gets copied as the stub of the main module for the distribution:

package [%module%];

# ABSTRACT: FIXME: the fantastic new [%module%]!

use warnings;
use strict;

our $VERSION = '0.99_01';





FIXME: Quick summary of what the module does.

Perhaps a little code snippet.

    use [%module%];

    my $foo = [%module%]->new;


FIXME: description


Please report any bugs or feature requests to
C<bug-[%rtname%]>, or through the web interface at
L<>.  I will be notified, and then you'll automatically be
notified of progress on your bug as I make changes.


This puts all the Pod after the __END__ block, and trims out all the default Module::Starter stuff that I usually end up deleting myself, oh and sets my preferred initial-release version number.

A bunch of the boilerplate that I insert with Pod::Weaver is also removed, since I'll be generating that myself.

You may wish to tweak things differently.

Configuring Test::XT

That's things done as far as Module::Starter is concerned, now we need to set up our standard release tests:

$ perl -MTest::XT -e "Test::XT::WriteXT( 'Test::CheckChanges' => '$HOME/.module-starter/skel/90-check-changes.t', 'Test::Fixme' => '$HOME/.module-starter/skel/91-fixme.t', 'Test::HasVersion' => '$HOME/.module-starter/skel/92-hasversion.t', 'Test::CPAN::Meta' => '$HOME/.module-starter/skel/93-meta.t', 'Test::MinimumVersion' => '$HOME/.module-starter/skel/94-minimumversion.t', 'Test::DistManifest' => '$HOME/.module-starter/skel/95-manifest.t', 'Test::Pod' => '$HOME/.module-starter/skel/98-pod.t', 'Test::Pod::Coverage' => '$HOME/.module-starter/skel/99-pod-coverage.t', );"

This creates some "best practice" release tests that will get copied into each newly minted distribution's t/ directory, if you want some details on this then take a look at the documentation for Test::XT, there's other tests available and these are just the ones that I personally use.

Now if we take a look in ~/.module-starter/skel you should have something like:

$ ls ~/.module-starter/skel 00-load.t 93-meta.t Build.PL README 90-check-changes.t 94-minimumversion.t Changes cvsignore 91-fixme.t 95-pod.t MANIFEST 92-hasversion.t 99-pod-coverage.t

You will probably have a Makefile.PL in there, which I've manually deleted from my dir because I always use Module::Build.

You're now ready to mint a new distribution.

Minting a new distribution

Some of this you'll want to change to suit your own practices, this is just an example of how I work.

$ mkdir ~/projects/ShinyNewProject $ cd ~/projects/ShinyNewProject $ module-starter --module Shiny::NewProject --dir=src Created starter directories and files $ cd src $ git init Initialized empty Git repository in /home/illusori/projects/ShinyNewtProject/src/.git/ $ ls Build.PL Changes MANIFEST README ignore.txt lib t

And that's all it now takes me to start a new distribution and start hacking away at it.

Browse Sam's Blog Subscribe to Sam's Blog

By day of August: 16, 23, 26.

By month of 2010: March, April, May, June, July, August, September, November.

By year: 2010, 2011, 2012, 2013.

Or by: category or series.


blog comments powered by Disqus
© 2009-2013 Sam Graham, unless otherwise noted. All rights reserved.