views:

130

answers:

4

I'm finding it very convenient to pass configuration and other data that is read or calculated once but then used many times throughout a program by using Perl's use mechanism. I'm doing this by exporting a hash into the caller's namespace. For example:

package Myconfiguration;

my %config;

sub import {
    my $callpkg = caller(0);
    my $expsym = $_[1];

    configure() unless %config;

    *{"$callpkg\::$expsym"} = \%config;
}

and then in other modules:

use MyConfiguration (loc_config_sym);

if ( $loc_config_sym{paramater} ) {
    # ... do stuff ...
}

However, I'm not sure about this as a best practice. Is it better to add a method that returns a hash ref with the data? Something else?

+2  A: 

I think it's better to work with a copy of the config hash. This way, if you modify some elements, this won't affect the rest of your code.

I usually use simple object (optionally Singleton) for this with a single method like get_property().

eugene y
This particular example was one of configuration, but it need not be. I.e., I might *want* to change elements.
gvkv
You can change the values in a singleton.
brian d foy
A: 

In general, it's best to let the user decide whether or not to import symbols. Exporter makes this easy. Writing a custom import method to let the user decide what to name imported symbols can be useful on rare occasions but I don't think this is one of them.

package MyConfiguration;
require Exporter;

our @ISA       = qw(Exporter);
our @EXPORT_OK = qw(Config);

our %Config;

And then, in your script:

use MyConfiguration;
print $MyConfiguration::Config{key};

or

use MyConfiguration qw(Config);
print $Config{key};
Michael Carman
Except the Exporter docs specifically say: "Exporting variables is not a good idea. They can change under the hood, provoking horrible effects at-a-distance, that are too hard to track and to fix. Trust me: they are not worth it." Really, in this case, just because you can, doesn't mean it's a good idea.
Robert P
+2  A: 

If you only want to read the values of %config, then why not have a routine to do it for you?

 my %config;
 sub config_value
 {
      my ($value) = @_;
      return $config{$value};
 }

You could export this by default if you wanted to:

package Mypackage;
require Exporter;
@EXPORT = qw/config_value/;

The reason that I would not allow access to the hash all over the place in lots of different modules is that I would have a hard time mentally keeping track of all the places it was being used. I would rather make the above kind of access routine so that, if some bug happened, I could add a print statement to the routine, or something, to find out when the value was being accessed. I don't know if that is related to "best practices" or it is just because I'm stupid, but the kind of confusion created by global variables scares me.

There's no reason you can't have a set routine too:

 sub set_value
 {
      my ($key, $value) = @_;
      $config{$key} = $value;
 }
Kinopiko
This is what the monkey inside my head was trying to tell me but I couldn't get out (although I prefer 'use Exporter qw(import);' :)). I didn't really think of this as a global variable when in practice that's exactly what I'm doing here. The only downside here is the method call overhead but that's not likely to be important, especially considering maintenance and debugging. Plus, it's easy enough alias the sub if it's convenient to have a locally relevant name (this isn't just about configuration).
gvkv
+2  A: 

I suggest never exporting variables. Create a class that can return a reference to a private variable instead. People can then store it in a variable with whichever name they like, and only when they decide they want to use it.

brian d foy