ajwood ajwood - 1 year ago 69
Perl Question

Accessing subs from a require'd perl script

I'm going to import some perl code with the

statement. The code I'd like to import is in

package FOO::BAR;

sub routine {
print "A message!\n";


package FOO::BAZ;

sub routine {
print "Another message!\n";

Then I'm going to use it like this:

foreach my $lib (qw/ mylibA.pl mylibB.pl /){
require $lib;
print "Make a call to ${lib}'s &routine!\n";

Is there a way for my script to figure out the namespace that was pulled in with the

Answer Source

There are two problems here:

  1. How do I change the behaviour of a script when executed as a standalone and when used as a module?
  2. How do I discover the package name of a piece of code I just compiled?

The general answer to question 2 is: You don't, as any compilation unit may contain an arbitrary number of packages.

Anyway, here are three possible solutions:

  1. Name your modules so that you already know the name when you load it.
  2. Have each module register itself at a central rendezvous point.
  3. Like #1, but adds autodiscovery of your plugins.

The simplest solution is to put all of the API in an ordinary module, and put the standalone logic in a seperate script:


Where each standalone basically looks like

use Module::A;

If another script wants to reuse that code, it does

use lib "/the/location";
use Module::A;

If the loading happens on runtime, then Module::Runtime helps here:

use Module::Runtime 'use_module';
use lib "/the/location";
my $mod_a = use_module('Module::A');

It isn't strictly necessary to place the contents of a-standalone.pl and Module/A.pm into separate files, although that is clearer. If you want to conditionally run code in a module only if it is used as a script, you can utilize the unless(caller) trick.

Of course all of this is tricksing: Here we determine the file name from the module name, not the other way round – which as I already mentioned we cannot do.

What we can do is have each module register itself at a certain predefined location, e.g. by

Rendezvous::Point->register(__FILE__ => __PACKAGE__);

Of course the standalone version has to shield against the possibility that there is no Rendezvous::Point, therefore:

if (my $register = Rendezvous::Point->can("register")) {
  $register->(__FILE__ => __PACKAGE__);

Eh, this is silly and violates DRY. So let's create a Rendezvous::Point module that takes care of this:

In /the/location/Rendezvous/Point.pm:

package Rendezvous::Point;
use strict; use warnings;

my %modules_by_filename;

sub get {
  my ($class, $name) = @_;

sub register {
  my ($file, $package) = @_;
  $modules_by_filename{$file} = $package;

sub import {
  my ($class) = @_;

Now, use Rendezvous::Point; registers the calling package, and the module name can be retrived by the absolute path.

The script that wants to use the various modules now does:

use "/the/location";
use Rendezvous::Point ();  # avoid registering ourself

my $prefix = "/the/location";
for my $filename (map "$prefix/$_", qw(Module/A.pm Module/B.pm)) {
  require $filename;
  my $module  = Rendezvous::Point->get($filename)
             // die "$filename didn't register itself at the Rendezvous::Point";

Then there are fully featured plugin systems like Module::Pluggable. This system works by looking at all paths were Perl modules may reside, and loads them if they have a certain prefix. A solution with that would look like:


Everything is just like with the first solution: Standalone scripts look like

use lib "/the/location/";
use MyClass::Plugin::A;

But MyClass.pm looks like:

package MyClass;
use Module::Pluggable require => 1;  # we can now query plugins like MyClass->plugins

sub run {
  # Woo, magic! Works with inner packages as well!
  for my $plugin (MyClass->plugins) {

Of course, this still requires a specific naming scheme, but it auto-discovers possible plugins.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download