views:

123

answers:

3

I have a file revs.pm:

my %vers = ( foo => "bar" );

And another file like importer.pl:

use revs;

How can I access %vers from importer.pl ?

+11  A: 

Create a proper module and change the my keyword to our:

# revs.pm
package revs;

our %vers = ( foo => "bar" );

1; # Perl modules need to return a boolean "true" value.

# importer.pl
use revs;

print $revs::vers{foo} . "\n";
Mikael S
Surely, you meant to say `our %vers` instead of `my %vers`. :-P
Chris Jester-Young
Haha, oops. Fixed. :)
Mikael S
Keep in mind that global variables are evil, so using an accessor method might be better. :)
Ether
+7  A: 

Another conventional way is to use the Exporter module in your package, and export the variable:

package revs;
use strict;
use warnings;
require Exporter;
our @ISA    = qw(Exporter);
our @EXPORT = qw(%vers);
our %vers   = (foo=>'bar');
1;

This avoids having to use the package name when referring to the variable from importer.pl:

use strict;
use warnings;
use Data::Dumper;
use revs;
print Dumper(\%vers);

One disadvantage is that you must make sure your variable name is unique in order to avoid name collisions.

toolic
+6  A: 

Alternatively, you could just not couple parts of your program with a global variable. Consider what happens when you use this hash in one module:

package Foo;
use MyApp::Versions qw(%versions); # i'm going to pretend that you didn't call the module "revs".

some_function {
    while(my ($k, $v) = each %versions){
       return if $some_condition;
    }
}

And then in some other module:

package Bar;
use MyApp::Versions qw(%versions);

some_other_function {
    while(my ($k, $v) = each %versions){
       print "$k => $v\n";
    }
}

And then use both modules:

use Foo;
use Bar;

some_other_function;
some_function;
some_other_function;

Depending on $some_condition, some_other_function produces different results each time you call it. Have fun debugging that. (This is more of an each problem than a global state problem; but by exposing the internal implementation, you allow your callers to do things that you didn't intend, and that can easily break your program.)

It's also a pain to rewrite Foo and Bar when you change the hard-coded hash to an on-demand database lookup, for example.

So the real solution is to design a proper API, and export that instead of the entire variable:

package MyApp::Versions;
use strict;
use Carp qw(confess);
use Sub::Exporter -setup => {
    exports => ['get_component_version'],
};

my %component_versions = ( foo => 42 ); # yes, "my", not "our".

sub get_component_version {
    my ($component) = @_;
    return $component_versions{$component} ||
        confess "No component $component!"
}

1;

Now your module is easier to use:

package Foo;
use MyApp::Versions qw(get_component_version);

sub some_function {
    die 'your foo is too old'
        unless get_component_version('foo') >= 69;

    return FooComponent->oh_hai;
}

Now some_function can't mess up some_other_function, and when you change the implementation of get_component_version, the rest of your program won't care.

jrockway
+1. It seems people are eager to vote for the hard and fast solution over best practices.
rjh