Wednesday, August 6, 2008

Modules

An introduction
Subroutines are oft-used pieces of code. They exist so you can re-use the code and not have to constantly rewrite it.

A module is, in principle, similar to a subroutine. It is also an oft-used piece of code. The difference is that modules don't live in your program, they are their own separate script outside your code. For example, you might write a routine to send email. You could then use this code in ten, a hundred, a thousand different programs just by referencing the original program.

As you would expect, the basic Perl package includes a large number of modules. These have been written by people who had a need for the code, made it a module and released it into the big wide world. Many of these modules have been debugged, improved and documented by yet more people. To quote the OpenSource mantra, all bugs are shallow under the scrutiny of every programmer.

Aside from the many modules included with Perl there are hundreds more available on CPAN, the Comprehensive Perl Archive Network. Refer to your documentation for details.




File::Find -- using a module
An example of a module included with Perl is File::Find. There are several modules under the File::Find section, such as File::Basetree, File::Compare and File::Stat.

This is an example of how File::Find can be used:

use File::Find;

$dir1='/some/dir/with/lots/of/files';
$dir2='/another/directory/';

find(\&wanted, $dir1,$dir2);

sub wanted {
print "Found it $File::Find::dir/$_\n" if /^[a-d]/i;

}

The first line is the most important. The use function loads the File::Find module. Now, all the power and functionality of File::Find is available for use. Such as the find function. This accepts two basic parameters:
The name of a subroutine, usually wanted which defines what you want to do with the list of files being returned. The filename will be in $_.
A list of directories to be searched. Subdirectories will also be searched.
The subroutine wanted simply prints the directory the file was found in if the filename begins with a,b,c or d. Make your own regex to suit. The line $File::Find::dir means the $dir variable in the module $File::Find. This is explained further in the next section.

Note -- the \&wanted parameter is a reference to a subroutine. Essentially, this means that the code in File::Find knows where to find the &wanted subroutine. It is basically like shortcuts under Windows 9x and NT4, instead of actual files (but the UNIX Perl people would slaughter me for that, so be quiet).




ChangeNotify
Another example is Win32::ChangeNotify. As you might expect there are a number of Win32-specific modules, and ChangeNotify is one of them. It waits until a something changes in a directory, then acts. What it waits for and what it does are up to you, for example:
use Win32::ChangeNotify;

$Path='/downloads';
$WatchSubTree=0;
$Events='FILE_NAME';
$browser='E:/progs/netscape/Communicator/program/netscape.exe';
$changes=0;

$notify = Win32::ChangeNotify->new($Path,$WatchSubTree,$Events);

while (1) {
print "- ",scalar(localtime)," $changes so far to $Path.\n";
$notify->wait;
++$changes;
print "- ",scalar(localtime), " Launching $browser...\n";
system("$browser $Path");
$notify->reset;
}


Again, the module is incorporated into the program with use . An object referred to by the variable $notify is created. The parameters passed are the path to be watched, whether we want to watch subtrees, and what sort of events we want to be notified about, in this case only filename changes.

Then, we enter a loop which continues while 1 is true -- which will be forever.

The program pauses when the wait method of the $notify notify object is called. Only when there is a change to the directory, then the rest of the subroutine completes, launching the browser. We have to reset the $notify object.

There is some pretty frightening stuff about objects in the explanation. But you don't actually need to understand anything about objects. Just read the documentation, and experiment.

You can use as many modules as you like in one program. As they are all written with carefully scoped variables you need not worry about programmers using the same variable names in different modules. Now you *really* appreciate scoping!



Your Very Own Module
You too can write your own modules. It is easy. First, we will create the fantastic bit of code that we want to re-use everywhere. First, we'll write a normal Perl program:
$name=shift;

print &logname($name);

sub logname {
my $name=shift;
my @namebits;
my ($logon,$inital);
@namebits=split /\s+/,$name;
($inital)=$name=~/(\w)/;
$logon=$inital.$namebits[$#namebits];
$logon=lc $logon;
return $logon;
}

Execute like so; perl script.pl "Nick Bladon"

The script itself is nothing amazing. The lc function stands for LowerCase, or probably lOWERcASE -- you can see what it does.

In order to turn it into a module carry out the following steps:

Find out where your copy of Perl is installed, for example c:\progs\perl.
Within that directory there should be a lib directory.
Make a directory within lib, for example c:\progs\perl\lib\RMP\
Now we'll make the module. Remember, a module is just code you are going to reuse. So we don't need all of the above example. Just this bit:
sub logname {
my $name=shift;
my @namebits;
my ($logon,$inital);
@namebits=split /\s+/,$name;
($inital)=$name=~/(\w)/;
$logon=$inital.$namebits[$#namebits];
$logon=lc $logon;
return $logon;
}

1;

The bit that has been added is the 1 at the bottom. Why? Perl requires that all modules return true. We know that a subroutine always returns the value of the last expression evaluated. As 1 evaluates to true, that'll do.

You need to save this as logon.pm in your newly created directory under lib. The pm stands for Perl Module.

That's it. A module created. To use, just make a normal Perl script such as:

use RMP::logon;

$name=shift;

print logname($name);

and hey presto! Module power is yours!

You don't have to create your own subdirectory within lib but I would advise it for the sake of neatness. And as you might expect, there is a lot more to learn about modules but this is supposed to be a basic tutorial, so that's enough for the time being.

No comments: