Building Your Own Perl Modules
The Perl Journal January 2003
By Arthur Ramos Jr.
Arthur is a Systems Administrator and Adjunct Instructor at Orange County Community College in Middletown, New York. He is the owner of Winning Web Design (http://www.winningweb.com/) and can be contacted at [email protected].
The English language has been a vibrant, robust language for over a millennia. It has grown and expanded with the march of progress from the dark ages to the information age. It adapts itself well to our exponential increase in knowledge and to influences from other languages and cultures. This robustness is mirrored in the Perl programming language.
Perl was written to be extensible, to allow new functions and features to be added in without having to submit a request and wait for the next version. The Comprehensive Perl Archive Network (CPAN, http://www.cpan.org/) is the repository for all the modules that have been written by programmers from around the world. These modules have enhanced Perl with powerful features allowing CGI programming, Database access, LDAP management, X windows programming, and much, much more. These modules can be downloaded and installed, enhancing the local version of Perl. This is analogous to a dialect of the English language.
Not only can modules be downloaded and installed from CPAN, but programmers can write modules for use on the local machine. Rather than cutting and pasting useful subroutines from one script to another, and dealing with the maintenance morass that creates, why not create a local Perl module containing the nifty subroutine? This module can then be included in each script that needs the subroutine. The scripts can use the subroutine as though it was hardcoded into the script file. When changes are made to this subroutine, the new version will automatically be picked up by the scripts.
I had just such a subroutine, one that I had duplicated in many different languages over the years. This subroutine started out as a Fortran subroutine! The subroutine's name is oneof. It is passed a search value and a list of valid values separated by a specified character. It returns a 0 if the value is not found in the list, or an index 1..n specifying which value the search value matched. This allows me to do quick testing of values without having to define extraneous variables in my script. The following fragment shows how the oneof subroutine is used, in this case without testing the return variable:
$mine = "of"; if (oneof($mine,"this,is,only,a,test,of,oneof")) { print "Found it in the string\n"; } else { print "NOT FOUND\n"; }
This fragment shows the use of oneof, this time testing the return variable:
$mine = "of"; if ($item = oneof($mine,"this,is,only,a,test,of,one of")) { print "Found it in the string at location $item\n"; } else { print "NOT FOUND\n"; }
If the list of valid values is separated by a character other than a comma, a third value can be passed containing the separator character:
$mine = "of"; if ($item = oneof($mine,"this;is;only;a;test;of;one of",";")) { print "Found it in the string at location $item\n"; } else { print "NOT FOUND\n"; }
The full subroutine with comments is shown in Example 1.
More experienced Perl programmers would probably write the same subroutine without the comments and with as few extra variables as possible. This is where obfuscated Perl code beginstrying to do a task with as few characters as possible. Presented here is a slightly obfuscated version of the oneof subroutine to make later examples more succinct:
sub oneof { if ($_[2]=~/^$/ || $_[2]!~/.{1}/) {$_[2]=",";} @l = split(/$_[2]/,$_[1]); for ($x=0,$f=0;$x<$#l;$x++) { if ($_[0] eq $l[$x]) { $f=$x+1; last; } } return $f; }
In order to modularize this subroutine, create a Perl module file. In vi (or whatever editor you prefer) open a file called ONEOF.pm. I capitalize the module name so that it will stand out in my Perl scripts. In this file, there is no bang command to specify that perl is to be run (no #!/usr/bin/perl). The first line defines the module's namespace. A namespace is a separate area set aside for the package so that variables and subroutines within will not clobber items with the same name in the main script. In this example, we will use a namespace called ONEOF, which I also capitalize to set it apart from the subroutine name oneof:
package ONEOF;
We then have to tell the package that the subroutine name oneof can be exported to the main script's namespace. Another package, called "Exporter," is required to perform this task. Include the Exporter package in the new module (from here on, bold code signifies the newly added material):
Package ONEOF; Use Exporter; @ISA = ('Exporter');
Or, alternatively, you could write that last line as:
@ISA = qw(Exporter);
The programmer must specify to the Exporter module that the subroutine oneof can indeed be Exported to other namespaces:
Package ONEOF; Use Exporter;@ ISA = qw(Exporter); @EXPORT = ('oneof');
Again, you could use @EXPORT = qw(oneof); for that last line. Next include the text of your subroutine, and then terminate the module with a 1;. This terminator is required.
Package ONEOF; Use Exporter;@ ISA = qw(Exporter)@ EXPORT = qw(oneof); sub oneof { if ($_[2]=~/^$/ || $_[2]!~/.{1}/) {$_[2]=",";} @lst = split(/$_[2]/,$_[1]); for ($x=0,$f=0;$x<$#lst;$x++) { if ($_[0] eq $lst[$x]) { $f=$x+1; last; } } return $f; } 1;
This is now a complete Perl module. Make sure that ONEOF.pm is either in the same directory as the script that will be run, or in a directory specified in the PATH environment variable. In the script that contains the test fragment shown at the beginning of this article, specify to Perl that you want to use the ONEOF.pm module:
use ONEOF;
$mine = "of"; if ($item = oneof($mine,"this,is,only,a,test,of,one of")) { print "Found it in the string at location $item\n"; } else { print "NOT FOUND\n"; }
If we had not exported the oneof subroutine from the module, we would have to explicitly reference the namespace and subroutine name:
ONEOF::oneof
The if statement in our test fragment would be changed to:
if ($item = ONEOF::oneof ($mine,"this,is,only,a,test,of,oneof")) {
This facility was written in this way to give the programmer control over what can be accessed between namespaces. To eliminate having to specify the namespace when not doing the exporting in the module itself, modify the use statement to explicitly export a specific subroutine within the module:
use ONEOF ('oneof');
or
use ONEOF qw(oneof);
Multiple subroutines can be placed in a single Perl module. Try to collect subroutines that perform similar functions in individual modules (for example, "MYPRINTERS.pm"). If you are programming in an organization, a good suggestion would be to use the organization's initials at the beginning of the module name to show other programmers that this module is local. For example, I am employed by Orange County Community College in Middletown, NY, so I would use a module name such as OCCCPRINTERS.pm. When you have several subroutines in a module that need to be exported, separate the subroutines with a space in the @EXPORT statement:
@EXPORT = ('subone' 'subtwo' 'subthree');
Likewise, if you are explicitly referencing multiple subroutines in your script, use this same convention:
Use OCCCPRINTERS ('subone' 'subtwo' 'subthree');
And that's it. This will help you begin to modularize your Perl code. We'll get into more advanced modularization topics and namespace usage in future articles.
TPJ