Creating (and Maintaining) Perl Modules

Goals

The goal of this web page is to help you write easily maintainable and re-usable code. In Perl, re-usability is implemented through modules, which are similar to libraries in other languages.

This page will guide you through creating your module and documenting it, as well as giving you some tips on how to make your code as maintainable and re-usable as possible.

Creating Perl Modules

Perl modules are those files that end in .pm. If you do things right, you can make the process of writing, testing, and installing your module really slick. You'll also be able to easily bundle up your module for testing and installation on other machines, or uploading to CPAN.

Here are the steps in creating a module:

Create a place to develop your module

The simplest way to do this is to create one directory per module. Give this directory any name that clearly identifies the module that it contains. See the Math::BaseCalc module for a simple example, or the Apache::Filter modules for a more involved one.

Create skeleton files for your module

Perl is distributed with a program called h2xs. This program, while initially intended to help programmers implement C extensions to Perl, can also be used to generate skeleton files for a new module.

Let's create a module called NewModule.pm that doesn't do very much. I'll run the h2xs program:

[~/modules],2:05pm% h2xs -AXc -n NewModule
Writing NewModule/NewModule.pm
Writing NewModule/Makefile.PL
Writing NewModule/test.pl
Writing NewModule/Changes
Writing NewModule/MANIFEST
[~/modules],2:05pm% cd NewModule/
[~/modules/NewModule],2:05pm% ls
Changes        MANIFEST       Makefile.PL    NewModule.pm   test.pl

The Changes file is where you might keep track keep track of changes you make to your module as you write new versions. If you're using RCS or CVS version control, you shouldn't use the Changes file, since all your history & logs will be in revision control and is much more reliable there (you are adding detailed revision notes in version control, aren't you?). I've found that the best scheme is to automatically build the Changes file from the revision control history, but your preferences might vary.

MANIFEST contains a list of files in this directory. If you add new files to the directory, you should also add them to the MANIFEST. The MANIFEST is used to create a tarball of your module for distribution, and it's also checked when people unpack the tarball and install the module.

Makefile.PL is a Perl program used to create a Unix Makefile. You'll use this Makefile to test and install your module.

NewModule.pm is your module. You'll write the code here in the next step.

test.pl is a Perl program that tests your module. You don't run it directly, you type "make test" at a Unix prompt and it runs it for you. We'll develop this test suite a little later.

Document your module

One of the great things about Perl modules is that they can have their documentation right in the same file. Once this module is installed, its documentation can be read by typing "perldoc NewModule" at a Unix prompt. Keeping the code and documentation together is a great thing, since it means you'll always have the most recent documentation if you've got the most recent code.

Here is some sample documentation.

=head1 NAME

NewModule - Perl module for hooting

=head1 SYNOPSIS

  use NewModule;
  my $hootie = new NewModule;
  $hootie->verbose(1);
  $hootie->hoot;  # Hoots
  $hootie->verbose(0);
  $hootie->hoot;  # Doesn't hoot
  

=head1 DESCRIPTION

This module hoots when it's verbose, and doesn't do anything 
when it's not verbose.  

=head2 Methods

=over 4

=item * $object->verbose(true or false)

=item * $object->verbose()

Returns the value of the 'verbose' property.  When called with an
argument, it also sets the value of the property.  Use a true or false
Perl value, such as 1 or 0.

=item * $object->hoot()

Returns a hoot if we're supposed to be verbose.  Otherwise it returns
nothing.

=back

=head1 AUTHOR

Ken Williams (ken@mathforum.org)

=head1 COPYRIGHT

Copyright 1998 Swarthmore College.  All rights reserved.

This library is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

=head1 SEE ALSO

perl(1).

=cut

When you create the module using h2xs it will create several sections for you automatically. They are:

One other critical section that you should create is FUNCTIONS or METHODS (depending on whether your code is function-based or object-oriented). This section should list every single function or method intended for public use. At the very minimum, these descriptions should list the parameters each function/method takes and the return values it can give back.

Feel free to expand your documentation beyond these sections. Make sure to note any areas where your module does something that might go against someone else's assumption. Also make sure to mention limitations of the module that might not be obvious without looking at the code.

Your documentation is complete when someone can use your module without ever having to look at its code. This is very important. This makes it possible for you to separate your module's documented interface from its internal implementation (guts). This is good because it means that you are free to change the module's internals as long as the interface remains the same.

POD (Plain Old Documentation)

The perldoc program expects your documentation to be in POD format. The pod format has a few (very few) tags that you use to markup plain text. As an aside, the Perl compiler ignores POD commands so they can be used for extended comments inside your code.

Here is a list of some of the tags, with some HTML tags that are similar in spirit:
POD tag HTML equivalent Description
=head1 <H1> Primary heading.
=head2 <H2> Secondary heading.
=over N <UL> or <OL> Indent N spaces until it finds a =back tag. The convention is generally to indent in multiples of 4, so you see =over 4 a lot.
=back </UL> or </OL> Indicates that you are done with indenting.
=item <LI> Indicates a list item. The convention is to use =over to begin a list, and =back to end it. Generally you do =item *, which puts bullets in front of each list item.
=cut </HTML> Indicates the end of a POD section.

For more information on POD, type perldoc perlpod at a UNIX prompt. There's not much to POD, and it will behoove you to know it inside & out.

Write some Perl code

What you've got now is a documented, fully functional Perl module that doesn't do anything. We've got to write some code in NewModule.pm to make it do something. This code should implement the interface defined in the documentation we just wrote.

The NewModule.pm file will have this in it already:

package NewModule;

use strict;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);

require Exporter;

@ISA = qw(Exporter AutoLoader);
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
@EXPORT = qw(

);
$VERSION = '0.01';


# Preloaded methods go here.

# Autoload methods go after =cut, and are processed by the autosplit program.

1;
__END__

Here is a line by line explanation of what this means:

Let's create some sample code. Don't worry about what this code does or how it works. We're mostly concerned with having a few methods so we can demonstrate how to document a module. For reference, this code is using the Object Oriented Perl syntax and features that became available with Perl 5.

package NewModule;

use strict;
use vars qw($VERSION);
$VERSION = '0.01';

sub new {
  my $package = shift;
  return bless({}, $package);
}

sub verbose {
  my $self = shift;
  if (@_) {
    $self->{'verbose'} = shift;
  }
  return $self->{'verbose'};
}

sub hoot {
  my $self = shift;
  return "Don't pollute!" if $self->{'verbose'};
  return;
}

1;
__END__

Write some tests for your code

One of the benefits of developing modules this way is that you can maintain a list of tests for your code that make sure it's working properly. This is what the test.pl file is for. Let's put a couple of tests at the end of the file - here is the complete file now:

# Before `make install' is performed this script should be runnable with
# `make test'. After `make install' it should work as `perl test.pl'

######################### We start with some black magic to print on failure.

# Change 1..1 below to 1..last_test_to_print .
# (It may become useful if the test is moved to ./t subdirectory.)

BEGIN { $| = 1; print "1..1\n"; }
END {print "not ok 1\n" unless $loaded;}
use NewModule;
$loaded = 1;
print "ok 1\n";

######################### End of black magic.

# Insert your test code below (better if it prints "ok 13"
# (correspondingly "not ok 13") depending on the success of chunk 13
# of the test code):

# Test 2:
my $obj = new NewModule;
$obj->verbose(1);
my $result = $obj->hoot;
print ($result eq "Don't pollute!" ? "ok 2\n" : "not ok 2\n");

# Test 3:
$obj->verbose(0);
my $result = $obj->hoot;
print ($result eq "" ? "ok 3\n" : "not ok 3\n");

The first test has already been created by h2xs in step one. It just makes sure we can load NewModule.pm in the first place. The second and third tests check that the hoot method returns the right things. These tests were written by the programmer.

These tests should completely exercise every function/method of the entire module, as exhaustively as possible. This script should be the regression test for your module. Every time you make a change to the module's implementation, you can test it against this script to make sure that nothing is broken. It also lets you determine whether your code will work on different platforms.

While this is a signficant time commitment for a large module, it also has a big payoff. Whenever a change is made to this module, you can find out very quickly whether or not the existing functionality has been changed. And when a bug gets reported, the first thing you can do is add a test to test.pl that exhibits the bug - when we fix the bug, we'll never have to worry about it escaping our attention again.

Install the module

Now we've got everything written, we can try installing the module. The general procedure for installing any Perl module is:

  perl Makefile.PL
  make
  make test
  make install

Let's try it now.

[~/modules/NewModule],6:07pm% ls
Changes        MANIFEST       Makefile.PL    NewModule.pm   README         test.pl
[~/modules/NewModule],6:07pm% perl Makefile.PL
Checking if your kit is complete...
Looks good
Writing Makefile for NewModule
[~/modules/NewModule],6:09pm% make
mkdir ./blib
mkdir ./blib/lib
mkdir ./blib/arch
mkdir ./blib/arch/auto
mkdir ./blib/arch/auto/NewModule
mkdir ./blib/lib/auto
mkdir ./blib/lib/auto/NewModule
mkdir ./blib/man3
cp NewModule.pm ./blib/lib/NewModule.pm
Manifying ./blib/man3/NewModule.3
[~/modules/NewModule],6:09pm% make test
PERL_DL_NONLAZY=1 /usr/local/bin/perl -I./blib/arch -I./blib/lib -I/usr/local/li
b/perl5/alpha-dec_osf/5.00404 -I/usr/local/lib/perl5 test.pl
1..1
ok 1
ok 2
ok 3
[~/modules/NewModule],6:10pm% su
s/key 1111 aa11111
Password:
[forum]:/home/ken/modules/NewModule# make install
Installing /usr/local/lib/perl5/site_perl/./NewModule.pm
Installing /usr/local/lib/perl5/man/man3/./NewModule.3
Writing /usr/local/lib/perl5/site_perl/alpha-dec_osf/auto/NewModule/.packlist
Appending installation info to /usr/local/lib/perl5/alpha-dec_osf/5.00404/perllocal.pod

Notice that I had to become root to install the module globally. Installation involves copying files into the Perl library directory, which most people don't have permission to copy into. Since this isn't a very useful module, I installed it and then immediately uninstalled it by deleting the first three files mentioned in the "make install" step.

If you want to install the module in some non-standard location (like /foo/bar/lib), you give a LIB directive in the Makefile.PL step, i.e. perl Makefile.PL LIB=/put/module/here. This can also be put inside Makefile.PL if you're writing a module that will only be used locally and has a specific installation location that you want to enforce.

Tips

Created in 1997 by Ken Williams. Expanded and revised in 1999 for Digital River by Dave Rolsky and Ken Williams. Revised in 2001 by Ken Williams for general use.