ServersThe Perl You Need to Know, Part II: Working with Nested Subroutines...

The Perl You Need to Know, Part II: Working with Nested Subroutines Page 4




First you might wonder, why in the world will someone need to define an
inner subroutine? Well, for example to reduce some of Perl’s script startup
overhead you might decide to write a daemon that will compile the scripts
and modules only once, and cache the pre-compiled code in memory. When some
script is to be executed, you just tell the daemon the name of the script
to run and it will do the rest and do it much faster since compilation has
already taken place.

Seems like an easy task, and it is. The only problem is once the script is
compiled, how do you execute it? Or let’s put it the other way: after it
was executed for the first time and it stays compiled in the daemon’s
memory, how do you call it again? If you could get all developers to code
their scripts so each has a subroutine called run() that will
actually execute the code in the script then we’ve solved half the problem.

But how does the daemon know to refer to some specific script if they all
run in the main:: name space? One solution might be to ask the developers to declare a
package in each and every script, and for the package name to be derived
from the script name. However, since there is a chance that there will be
more than one script with the same name but residing in different
directories, then in order to prevent namespace collisions the directory
has to be a part of the package name too. And don’t forget that the script
may be moved from one directory to another, so you will have to make sure
that the package name is corrected every time the script gets moved.

But why enforce these strange rules on developers, when we can arrange for
our daemon to do this work? For every script that the daemon is about to
execute for the first time, the script should be wrapped inside the package
whose name is constructed from the mangled path to the script and a
subroutine called run(). For example if the daemon is about to
execute the script /tmp/hello.pl:

  hello.pl
  --------
  #!/usr/bin/perl
  print "Hellon";

Prior to running it, the daemon will change the code to be:

  wrapped_hello.pl
  ----------------
  package cache::tmp::hello_2epl;
  
  sub run{
    #!/usr/bin/perl 
    print "Hellon";
  }

The package name is constructed from the prefix cache::, each directory separation slash is replaced with ::, and non alphanumeric characters are encoded so that for example . (a dot) becomes _2e (an underscore followed by the ASCII code for a dot in hex representation).

 % perl -e 'printf "%x",ord(".")'

prints: 2e. The underscore is the same you see in URL encoding except the % character is used instead (%2E), but since % has a special meaning in Perl (prefix of hash variable) it couldn't be
used.

Now when the daemon is requested to execute the script
/tmp/hello.pl, all it has to do is to build the package name as before based on the
location of the script and call its run() subroutine:

  use cache::tmp::hello_2epl;
  cache::tmp::hello_2epl::run();

We have just written a partial prototype of the daemon we wanted. The only
outstanding problem is how to pass the path to the script to the daemon.
This detail is left as an exercise for the reader.

If you are familiar with the Apache::Registry module, you know that it works in almost the same way. It uses a different
package prefix and the generic function is called handler()
and not run(). The scripts to run are passed through the HTTP
protocol's headers.

Latest Posts

Related Stories