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

So today i decided that i am not going to write any more code that avoids certain design / style issues that i have consciously avoided in the past. So i've spent most of the day reading various docs about good module design, etc. Its been an eye-opening day in any case.

Im currently focusing on using modules (to be use'd) for support function libraries, instead of require'd by the scripts that want them.

I have read through most of the Jose's Guide for creating Perl modules, but thats more for writing pretty massive modules, and doesnt address some of the nitpicky things i want to deal with.
I've also reread and considered various wisdom contained in my thread constants in multiple libraries

Im trying to figure out how to best deal with other support libraries that i want included with them. For instance i have a couple general function libraries that are wrappers for DBD::MySQL, which will want to be utilized by some specific libraries i'm working on writing.

So lets say im writing a module called UtilLib.pm, and it wants to require mysql_wrapper_lib.pl, but i cant decide the best way to do this. My concern is over the idea of including the base library, and how its functions will be referenced.

Basically the file UtilLin.pm will look like this:

# require outside the package so that its functions are # NOT imported into the UtilLib namespace, so that other # libraries can use them. require mysql_wrapper_lib.pl; package UtilLib; use strict; use warnings; use Exporter; our @ISA = qw(Exporter); our %EXPORT_TAGS = ( 'public_constants' => [ qw(...) ], ... ); sub do_something { ... } # etc
So then, in any code in UtilLib, if i want to use functions in mysql_wrapper_lib.pl, i'll need to preface their names with main:: i.e. main::safe_query_execute() - otherwise perl will try to find safe_query_execute() in UtilLib.
But, i've seen it said here & there that that is bad practice. That idea is the core of this post. Complex tools are built upon more basic tools, so i am having trouble resolving how i should code in situations like this.

Is the way i am planning on structuring this UtilLib a good way to do it, or should i be doing something fundamentally different?

Replies are listed 'Best First'.
Re: Good Module Design
by dragonchild (Archbishop) on May 26, 2005 at 19:11 UTC
    Rewrite your mysql_wrapper_lib.pl to be a module that uses Exporter - that's the problem. Then, you don't have to worry about your prefixing shenanigans in UtilLib (which, btw, looks just fine).

    • In general, if you think something isn't in Perl, try it out, because it usually is. :-)
    • "What is the sound of Perl? Is it not the sound of a wall that people have stopped banging their heads against?"
      prefixing shenanigans in UtilLib (which, btw, looks just fine).

      Except it's ill-named and ill-named modules tend to denigrate faster than others (subjectively speaking of course).

      -derby
        Well, UtilLib is not the real name of the module, i was just using a generic name in the post.
Re: Good Module Design
by bart (Canon) on May 27, 2005 at 07:05 UTC
    Basically the file UtilLib.pm will look like this:
    # require outside the package so that its functions are # NOT imported into the UtilLib namespace, so that other # libraries can use them. require mysql_wrapper_lib.pl; package UtilLib;
    (BTW your filename for require must be quoted.)
    This is the worst possible idea. Why? Because mysql_wrapper_lib.pl will be required only once, and it'll be imported into the package that was the first to use your module. It is a moving target, in general you have no idea where that will be. A very simple example:
    # File mysql_wrapper_lib.pl $foo = 123; 1;
    Now with the code:
    package One; use UtilLib; package Two; use UtilLib;
    Where will your $foo end up? In package One, and not in package Two. Without the use statement in package One, or with package One loaded later, it will end up in package Two.

    Now imagine that package One and package Two are in two different modules, and loaded from various places. What package will $foo eventually end up in? Well, it'll probably be either in package One or in package Two, but which one?

    And that's why use got support for import, so the same variables/functions can be aliased in any package you like. Problem solved.

    # File Mysql_wrapper.pm package Mysql_wrapper; use base 'Exporter'; @EXPORT = qw($foo); $foo = 123; 1;
    package One; use Mysql_wrapper; package Two; use Mysql_wrapper;
    So now where will $foo end up in? Guess what: both in package One and in package Two. $One::foo and $Two::foo will actually point to the same variable, that's why they are called "aliases".

    p.s. It's not absolutely necessary to use Exporter, but it saves you from having to do the manual work of actually importing the variables yourself. That would end up to be a copy/paste job anyway, as it's not code that is easy to remember. Just to show it can be done (after all, we're here to learn right? :) ) here is some code that would do just that:

    package Mysql_wrapper; $foo = 123; sub import { my $pkg = caller; no strict 'refs'; *{"$pkg\::foo"} = \$foo; # and the same for other variables... } 1;
      Wow, i hadn't looked at it quite like that before. The multiple packages useing UtilLib does screw everything up horribly. This is exactly the sort of thing i was looking for.