joe_kool has asked for the wisdom of the Perl Monks concerning the following question:

Hi, dear monks, I have a question about Perl packages. In part to solve namespace collision problems in mod_perl, I have to assign a package name to all of my objects and librairies. I've looked a lot on the web for complete answers on the correct way to do that, but some things are still confusing... I have the current dir configuration:
/myApp/cgi/login.cgi /myApp/config/webData.pm ---------------------------------------------------------- login.cgi: ---------------------------------------------------------- #!/usr/bin/perl use CGI qw(:standard); use CGI::Carp qw(fatalsToBrowser); use lib "../config"; use webData qw(GetLinks); use strict; ... my $links_ref = MyPackages::Config::webData::GetLinks(); ---------------------------------------------------------- webData.pm: ---------------------------------------------------------- package MyPackages::Config::webData; use Exporter (); @ISA = qw(Exporter); @EXPORT_OK = qw(GetLinks GetEmails); use strict; ...
But what if I want to simply tell login.cgi:

my $links_ref = GetLinks();

instead of

my $links_ref = MyPackages::Config::webData::GetLinks();

Isn't there a way, in login.cgi header, to load the GetLinks function by it's complete package name, so that we can later in the file only use the short function name? And also, with the way I first described, I can access the GetEmails function by calling MyPackages::Config::webData::GetEmails(), even thought it wasn't listed in the use webData qw(GetLinks); list of keyword. What is the good, normal way to procede for applications like in this case?

Replies are listed 'Best First'.
Re: Good package practices
by bmann (Priest) on Jan 26, 2006 at 22:46 UTC
    There is a problem with the package declaration in the module. The package name should match the path to the module relative to @INC.

    In the script, you have:

    use lib "../config"; use webData qw(GetLinks);

    In webData.pm, you have:

    package MyPackages::Config::webData;

    See the difference? One has MyPackages::Config, one doesn't. You have two choices here - change the package declaration or change the 'use' statement:

    1. Change the package statement in the pm to "package webData;". Perl will be able to import the funtion.
    2. Move the module to a directory that matches the package statement and
      use MyPackages::Config::webData qw(GetLinks);
      (ie '../config/MyPackages/Config/webData.pm").
Re: Good package practices
by kwaping (Priest) on Jan 26, 2006 at 21:58 UTC
    Use @EXPORT instead of @EXPORT_OK in your module file.

    With that said, however, for my own stuff I prefer the OO method. Here's a very simplified example.
    package My::Example; sub new { return bless {}; } sub GetLinks { # ... }

    And then...
    #!/usr/bin/perl use warnings; use strict; use lib '/path/to/lib'; use My::Example; my $example = My::Example->new(); my $links = $example->GetLinks();
      Thank you for the suggestion. I have a couple of objects in this application too, but I was wondering what was the good way to go for plain librairies...

      I'm not exactly sure what changing EXPORT_OK to EXPORT was supposed to do in this situation, though... I can still access any function from webData.pm in login.cgi if I put the full package name before the function name, even if it is not in the webData's EXPORT list or even if it is not in the keyword list when I do use webData qw();...

      I would like to prevent, for example, GetEmails from being accessed in login.cgi if it is not in the EXPORT list, or if it is not in the keyword list of the use webData qw();

      Moreover, I still can't call in login.cgi main code the function GetLinks without specifying the full package name (as in my $links_ref = MyPackages::Config::webData::GetLinks();).
        @EXPORT should let you use GetLinks() without the namespace prepended to it. Read the Exporter docs for more info.

        In regards to keeping a module's method private, read this little snippet from perltoot:

        Perl doesn't impose restrictions on who gets to use which methods. The public-versus-private distinction is by convention, not syntax.

    Re: Good package practices
    by Errto (Vicar) on Jan 26, 2006 at 22:47 UTC

      The way your code is written you should be able to call GetLinks directly from your CGI script, except for one thing: your package names don't match. Specifically you're calling use webData but the actual package is MyPackages::Config::webData. There's no rule against this except that Exporter isn't actually doing you any good since its import method never gets called. So if you want this to work you're going to have to change the package name to just webData or else move the .pm file so that it's in a directory called MyPackages/Config relative to your "use lib" root.

      Once you've got that sorted out, then kwaping's first comment applies. You need to switch from @EXPORT_OK to @EXPORT if you want to use exported methods from your CGI script without explicitly importing them there.

    Re: Good package practices
    by ysth (Canon) on Jan 27, 2006 at 05:04 UTC
      As others have said, the problem is the mismatch between just "use webData" and "package MyPackages::Config::webData". The name you give to use has two purposes; one is to be translated to a filename for which to look, the other is to be the class for which the import() method is called. When you say use webData qw(GetLinks);, this is like:
      BEGIN { require "webData.pm"; "webData"->import( qw(GetLinks) ); }
      and since there is no webData package to call import for, no importing happens.

      Since you seem to say the long package name is a necessity, the easiest solution is to change your filenames to match, so you have:

      /myApp/cgi/login.cgi /myApp/config/MyPackages/Config/webData.pm
      and do use MyPackages::Config::webData qw(GetLinks);.
    Re: Good package practices
    by Anonymous Monk on Jan 27, 2006 at 07:32 UTC
    Re: Good package practices
    by joe_kool (Novice) on Jan 27, 2006 at 20:15 UTC
      These posts helped me a lot! Thank you all for your precious help!