Hidden Treasures of the Perl Core
The Perl Core comes with a lot of little modules to help you get thejob done. Many of these modules are not well-known. Even some of the well-known modules have some nice features that are often overlooked. In this article, we'll dive into many of these hidden treasures of the Perl Core.
blib
This module allows you to use MakeMaker
s to-be-installed version of a package. Most
of the distributions on the CPAN conform to MakeMaker
s building techniques. If
you are writing a Perl module that has a build system, then there would be a good chance
MakeMaker
is involved. Testing on the command line is common; I know
I find myself doing it often. This is one of the places that blib
comes in handy.
When running my test suite (you all have test suites, right?) on the command
line, I'm able to execute individual tests easily.
perl -Mblib t/deepmagic.t
If you are building someone elses module and find yourself debugging a testing
failure, then blib
could be used the same way.
diagnostics
PC Load Letter, what the frell does that mean?! -- Micheal Bolton
When pushed hard enough, the Perl interpreter can spew out hundreds of
error messages. Some of them can be quite cryptic. Running the following code
snippet under the warnings
pragma yields the warning Unterminated <>
operator at program.perl line 11.
$i <<< $j;
Thankfully, diagnostics
is an easy way to get a better explanation from Perl.
Since we're all running our important programs under the strict
and warnings
pragmas, it's easy to add diagnostics
to the mix.
use strict;
use warnings;
use diagnostics;
The previous code snippet now yields the following warning:
Unterminated <> operator at -e line 1 (#1)
(F) The lexer saw a left-angle bracket in a place where it was expecting
a term, so it's looking for the corresponding right-angle bracket, and
not finding it. Chances are you left some needed parentheses out
earlier in the line, and you really meant a "less than".
Uncaught exception from user code:
Unterminated <> operator at program.perl line 11.
Use of the diagnostics
pragma should be kept to development only (where it's
truly useful).
Benchmark
It can be difficult to benchmark code. When trying to optimise a program or routine,
you want to try several approaches and see which comes out faster. That's what the
Benchmark
module is for. This way, you don't have to calculate start and stop
times yourself, and in general you can do high-level profiling quickly. Here is an
example that tries to determine which is faster, literal hash slices or retrieving
hash values one at a time.
use Benchmark;
sub literal_slice {
my %family = (
Daughter => 'Evilina',
Father => 'Casey',
Mother => 'Chastity',
);
my ($mom, $dad) = @family{qw[Mother Father]};
}
sub one_at_a_time {
my %family = (
Daughter => 'Evelina',
Father => 'Casey',
Mother => 'Chastity',
);
my $mom = $family{Mother};
my $dad = $family{Father};
}
timethese(
5_000_000 => {
slice => \&literal_slice,
one_at_time => \&one_at_a_time,
},
);
On the hardware I have at work, a dual G4 PowerMac, the answer seems obvious. Being cute and clever doesn't hurt us too badly. Here is the output.
Benchmark: timing 5000000 iterations of one_at_time, slice...
one_at_time: 53 wallclock secs (53.63 usr + 0.00 sys = 53.63 CPU)
@ 93231.40/s (n=5000000)
slice: 56 wallclock secs (56.72 usr + 0.00 sys = 56.72 CPU)
@ 88152.33/s (n=5000000)
CGI::Pretty
Many of you know you can use Perl to write your HTML, in fact, this trick is often
used in CGI programs. If you have used the CGI
module to create HTML, then it would be obvious
that the output is not intended for humans to parse. The ``browser only'' nature of
the output makes debugging nearly impossible.
use CGI qw[:standard];
print header,
start_html( 'HTML from Perl' ),
h2('Writiing HTML using Perl' ),
hr,
p( 'Writing HTML with Perl is simple with the CGI module.' ),
end_html;
The previous program produces the following incomprehensible output.
Content-Type: text/html; charset=ISO-8859-1
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
<head><title>HTML from Perl</title></head><body><h2>Writing
HTML using Perl</h2><hr /><p>Writing HTML with Perl is simple with the
CGI module.</p></body></html>
By changing the first line to use CGI::Pretty qw[:standard];
, our output is now manageable.
Content-Type: text/html; charset=ISO-8859-1
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
<head><title>HTML from Perl</title>
</head><body>
<h2>
Writing HTML using Perl
</h2>
<hr><p>
Writing HTML with Perl is simple with the CGI module.
</p>
</body></html>
While not as attractive as I'd like, there are lots of customizations to be made, all
outlined in the CGI::Pretty
documentation.
Class::ISA
The world of class inheritance is a complex and twisting maze. This module provides
some functions to help us navigate the maze. The most common need is for the
function super_path()
. When dealing with complex OO hierarchies, super_path()
can help us know which classes we're inheriting from (it isn't always obvious), and
find method declarations.
I have a little project that requires Class::DBI
, so I ran super_path()
on one
of the classes to determine how Perl would search the inheritance tree for a method.
perl -MJobSearch -MClass::ISA -le'print for
Class::ISA::super_path( "JobSearch::Job" )'
The following list of classes is in the order Perl would search to find a method.
JobSearch::Object
Class::DBI::mysql
Class::DBI
Class::DBI::__::Base
Class::Data::Inheritable
Class::Accessor
Ima::DBI
Class::WhiteHole
DBI
Exporter
DynaLoader
Now if I have a question about a method implementation, or where methods are coming from,
I have a nice list to look through. Class::ISA
intentionally leaves out the current
class (in this case JobSearch::Job
), and UNIVERSAL
.
Here is a little trick that allows me to find out which classes may implement the
mk_accessors
method.
perl -MJobSearch -MClass::ISA -le \
'for (Class::ISA::super_path( "JobSearch::Job" )) {
print if $_->can("mk_accessors") }'
Because of inheritance, all of the classes listed can invoke mk_accessors
, but not all
of them actually define mk_accessors
. It still manages to narrow the list.
Class::ISA
was introduced to the Perl Core in release 5.8.0. If you're using an
older Perl, you can download it from the CPAN.
Cwd
This module makes it simple to find the current working directory. There is
no need to go to the shell, as so many of us do. Instead, use Cwd
.
use Cwd;
my $path = cwd;
Env
Perl provides access to environment variables via the global %ENV
hash. For
many applications, this is fine. Other times it can get in the way. Enter the
Env
module. By default, this module will create global scalars for all
the variables in your environment.
use Env;
print "$USER uses $SHELL";
Some variables are of better use as a list. You can alter the behavior of
Env
by specifying an import list.
use Env qw[@PATH $USER];
print "$USER's path is @PATH";
Yet another module to save time and energy when writing programs.
File::Path
This module has a useful function called mkpath
. With mkpath
you
can create more than one level of directory at a time. In some cases, this
could reduce a recursive function or a loop construct to a simple function
call.
use File::Path;
mkpath "/usr/local/apache/htdocs/articles/2003";
Since mkpath
will create any directory it needs to in order to finally
create the 2003
directory, a tremendous amount of code is no longer needed.
File::Spec::Functions
This module implements a sane and useful interface over the File::Spec
module. File::Spec
must be used by calling class methods, while
File::Spec::Functions
turns those methods into functions. There are
many functions that are all useful (and fully documented in
File::Spec::Unix
). Here are a few examples.
use File::Spec::Functions qw[splitpath canonpath splitdir abs2rel];
# split a path into logical pieces
my ($volume, $dir_path, $file) = splitpath( $path );
# clean up directory path
$dir_path = canonpath $dir_path;
# split the directories into a list
my @dirs = splitdir $dir_path;
# turn the full path into a relative path
my $rel_path = abs2rel $path;
As you can see, there are plenty of ways to save yourself coding time by
using File::Spec::Functions
. Don't forget, these functions are portable
because they use different symantecs behind the senses for the operating
system Perl is running on.
File::Temp
If you need a temporary file, then use File::Temp
. This module will find a
temporary directory that is suitable for the operating system Perl is
running on and open a temporary file in that location. This is yet another
example of the Perl Core saving you time.
use File::Temp;
my $fh = tempfile;
print $fh "temp data";
This will open a temporary file for you and return the filehandle for you to write to. When your program exits, the temporary file will be deleted.
FindBin
FindBin
has a small but useful purpose: to find the original directory
of the Perl script being run. When a program is invoked, it can be hard to
determine this directory. If a program is calling chdir
, then it can be even
more difficult. FindBin
makes it easy.
use FindBin;
my $program_dir = $FindBin::Bin;
Shell
Shell
takes the ugliness of dealing with the command line and wraps it
up in pretty functions. The effect here is prettier programs. Here is
a simple demonstration.
use Shell qw[ls du];
use File::Spec::Functions qw[rel2abs];
chomp( my @files = ls );
foreach ( @files ) {
print du "-sk", rel2abs $_;
}
Time::localtime
This module allows localtime
to return an object. The object gives you
by-name access to the individual elements returned by localtime
in list
context. This doesn't save us much coding time, but is can save us a trip
to the documentation.
use Time::localtime;
my $time = localtime;
print $time->year += 1900;
There is a similar module called Time::gmtime
, which provides the same
functionality for the gmtime
function.
UNIVERSAL
The UNIVERSAL
module is handy. Two of its most common functions,
isa
and can
are almost always used in OO programming as methods.
isa
is used to determine what class an object belongs to, and can
will tell us whether an object supports a method. This is useful for testing.
For example.
use Time::localtime;
my $time = localtime;
if ( $time->isa( 'Time::localtime' ) ) {
print "We have a Time::localtime object";
}
if ( $time->can( "year" ) ) {
print "We can get the year from our object";
}
Another less-known function in UNIVERSAL
is VERSION
. I often need to know
the version of an installed module and I find myself writing a one-liner like:
perl -MTest::More -le'print $Test::More::VERSION'
That's just not as pretty as this.
perl -MTest::More -le'print Test::More->VERSION'
The Perl Core has many hidden wonders, and I've just laid out a few
here. Trolling the Core for interesting functions and modules has saved me a
lot of work over the years. If you would like to look further, then browse the
perlmodlib
manpage for a list of the core modules. Whether your interest is
CGI, I18N, Locale, or Math, you can find something there that saves a few hours
of work.
Return to Related Articles from the O'Reilly Network .
Perl.com Compilation Copyright © 1998-2003 O'Reilly & Associates, Inc.