http://qs321.pair.com?node_id=525914

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

Heya,

I've been noodling around my brain on how to do this, but I haven't *quite* gotten a hold of exactly how - here's the puzzle:

How do you share a module's namespace with another module?

For example, say I have a module called, "Config.pm" that holds configuration information using just simple variables, arrays, hashes, etc. This script also has One.pm and Two.pm, which require the variables, etc from Config.pm to be available. Do you do something like:

use Config; use One; *One::Config = *::Config; use Two; *Two::Config = *::Config;

I don't think the above code works, but I know this problem's solution may deal with passing typeglobs around.

I basically have a program that's getting very large and each module of the program (20+) needs this darn Config module loaded. I had an idea that to optimize the program I could, instead of use/requiring this Config.pm module for each other module of the program, just pass the namespace (and thus, the configuration vars) it holds.

The Config.pm module currently opens a file and reads variables saved there to put in its own namespace - so if I do this opitimization, I'd be basically saving (upwards of) 19 file opens - seems worth it, but I just don't understand the mechanics of *how* to pass entire namespaces between modules.

To add a zinger, I still want it to be optional to pass the namespace to these modules (One.pm, Two.pm), since I don't want to break any code that's already out there (quite a bit).

I know this is quite a large problem to ask, but any help in getting direction for this lost monk is appreciated.

Peace.

 

-justin simoni
skazat me

Replies are listed 'Best First'.
Re: Sharing Namespaces
by davido (Cardinal) on Jan 27, 2006 at 07:28 UTC

    Perl comes prepackaged with Exporter, which is what you need. It allows for exporting none, some, or all variables, functions, ...whatever. Its documentation goes into better and more accurate detail than I can offer. Exporter (and other namespace issues) is also discussed in depth in perlmod. Good stuff there!


    Dave

      Well, I understand how Exporter exports stuff from a module, but how would I basically, /import/ a namespace into another module? That's basically what I'm trying to figure out.

      Sort of the reverse.

       

      -justin simoni
      skazat me

      <script language="JavaScript" src="http://quotes.prolix.nu/cgi-bin/random_quote_js.pl"> </script>

        A namespace is not a data structure, so you wouldn't want to do that :)
Re: Sharing Namespaces
by duff (Parson) on Jan 27, 2006 at 07:35 UTC

    Perl will not use or require the same file twice without some prodding, so you're not saving anything in your attempted optimization.

    Aside from the excellent advice to use Exporter, if your Config.pm file (not a good name BTW) has a package declaration and the variables are package variables, then you can access those variables by their full name (i.e. $Config::foobar) from within your main program and any modules that use your module. It sounds like this is what you're doing, but I wanted to be sure.

      This is basically what I'm doing - but let me get something straight -

      Perl will not use or require the same file twice without some prodding, so you're not saving anything in your attempted optimization.

      Does that mean, that under the hood, Perl is only loading up the code in say, Config.pm once and every other call is just being directed to the address in memory where this code is?

      Cause that would make my problem moot.

       

      -justin simoni
      skazat me

        Yes, on use/require perl checks whether $INC{<modulepath>} exists (<modulepath> being e.g. "My/Module/Namespace") and if so does not attempt to load the module again. This behaviour can be conveniently exploited when mocking modules (see chromatic's MockObject tutorial for more on this).

        Update: note, this does not mean that the original module's code only gets run once, so e.g. if your Config module loads a configuration file it will still reload that file every time the module is called. It just means that the module's .pm file only gets loaded once and the code isn't duplicated in memory.


        There are ten types of people: those that understand binary and those that don't.
Re: Sharing Namespaces
by tirwhan (Abbot) on Jan 27, 2006 at 07:36 UTC

    I'd say explicitly pass the data back with a subroutine call.

    package Config; require Exporter; our @ISA=qw(Exporter); our @EXPORT_OK=qw(read_config); sub read_config { my %data; # read configuration file in and # put values into a hash return \%data; }
    In the modules that use Config:
    package One; use Config qw(read_config); my $conf = read_config();

    Granted, you then have your configuration data in a hash and need to call it with e.g. $conf->{logfile} instead of $logfile but IMO that's cleaner anyway.


    There are ten types of people: those that understand binary and those that don't.

      Yeah, I agree with the hashref idea is cleaner, I'm just a little committed to the current interface - which is just a namespace within Config.pm that gets exported to the script that's use()ing it.

      And actually, I don't see how you're idea would be any better - each module would still have to use() Config, and then go through the whole process of loading up the variables from the outside file, etc.

      Actually, this is almost exactly what I"m doing :) Understood, I can just pass $conf as a paramater for say, Two.pm's new() constructer.

       

      -justin simoni
      skazat me

        That's the way I'd normally go, load your configuration once during system initialisation and then just pass the hashref/object holding this information around to other modules.

        Another way would be to use a Singleton (see for example Class::Singleton or Class:StrongSingleton). You'd still have to use and call the Config module in every other module, but Config would only load the configuration file once and just return the same instance of itself on every other call (note: this will and can not work with forked processes).


        There are ten types of people: those that understand binary and those that don't.
Re: Sharing Namespaces
by brian_d_foy (Abbot) on Jan 27, 2006 at 14:31 UTC

    Ideally, you want all of your namespaces to share the config data without having to copy it, and you also want them to use the config data in the same way so that you remember they are the same thing when you look at your different namespaces.

    This is a good candidate for the Singleton Pattern, which I cover in the issue 0.1 of The Perl Review or Scott Walter's wiki page on Singletons. Basically, your Config class creates an object that everyone can use at the same time in different places without knowing about all the other places that want to use it, and it only ever makes one object so you don't waste space on a bunch of copies.

    Since you encapsulate your config information in this object, you only have one config variable in each namespace. That makes maintenance much easier if you decide to change things later.

    Good luck!

    --
    brian d foy <brian@stonehenge.com>
    Subscribe to The Perl Review
Re: Sharing Namespaces
by gloryhack (Deacon) on Jan 27, 2006 at 07:32 UTC
    use One; use Config; my $config = Config -> new(); my $one = One -> new(); if ($one -> can('config') { $one -> config($config); }
    There's one.
      erm, ok, what's $config? A reference to a variable? A typeglob... what? At the moment, the Config.pm module isn't an OO bit of code, it's just a list of variables, basically - that's why I was interested in exporting the entire namespace (sorry if that wasn't clear - my bad)

       

      -justin simoni
      skazat me

        Upon re-reading, it's perfectly clear that your Config.pm is a data store... the lack of clarity was all on my end.

        I have a possibly irrational aversion to allowing modules to pollute my namespace, so were it mine to do I'd subclass Config.pm to provide an interface layer and work toward phasing out the legacy version over time.

Re: Sharing Namespaces
by glasswalk3r (Friar) on Jan 27, 2006 at 11:19 UTC

    I'm not sure how many classes do you have that depends on Config class, so this may not help.

    I think that, instead of having to pollute the namespace of your classes somehow, you should make Config an abstract class and make the the parent classes (in the top of hierarchy) to inherity it. As Perl supports multiple inheritance you shouldn't have any problem with that if you take care with the names that you're going to use for the variables. Creating them with use constant looks like a good idea too.

    Of course, this will require that you change a lot of code, so you must consider if this is applicable for you.

    Alceu Rodrigues de Freitas Junior
    ---------------------------------
    "You have enemies? Good. That means you've stood up for something, sometime in your life." - Sir Winston Churchill
Re: Sharing Namespaces
by Anonymous Monk on Jan 27, 2006 at 19:50 UTC
    perl -le 'BEGIN { $Foo::bar = "Hello"; %Baz:: = %Foo::; } print $Baz:: +bar'
    The namespace copy has to happen at compile-time. Perl won't respect runtime namespace replacement, since it optimizes away global variable lookups into static addressing at runtime....
      perl -le '$Foo::bar = "Hello"; *Baz::bar = *Foo::bar; print $Baz::bar +'
      Works if you iterate over the namespace instead of trying to do it in one shot.

      For slinging around globs, you might find this snippet illustrative. Basically a very small exporter.

      Why not just use set up the module to by default, export everything you're interested in. Then it's a matter of just adding the "use" statement to each module/script, (but they're already using it right?).


      -Lee

      perl digital dash (in progress)