![]() |
|
||||||||||||||||||||||||||||
|
Creating (and Maintaining) Perl ModulesThis guide is reproduced with kind permission from Ken Williams - see bottom for contact details. I found it very useful, and Ken kindly gave me permission to reproduce it here. It's quite long, but I didn't want to break it down into pages - it's convenient to just print the whole lot. GoalsThe 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 ModulesPerl 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 moduleThe 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. Create skeleton files for your modulePerl 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 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 moduleOne 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 only 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:
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 codeWhat 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 codeOne 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 moduleNow 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. Revised and reformatted and placed here in 2003 by David Precious with kind permission from Ken Williams. You can find out more about Ken on his website at: http://mathforum.org/~ken/
Random Quote:
"Unix is simple, but it takes a genius to understand the simplicity." - Dennis Ritchie
(see more) |
|
|||||||||||||||||||||||||||