Anupama G
Anupama G

Reputation: 311

Can a module be reloaded from a different location, from within the module?

I have an API A::B that's "use"d by others in their scripts like so:

use A::B;
my $out = A::B->new(dir => $dir);
...

Now, I want a different version of A::B to be used depending on some parameter of the attribute $dir passed to the constructor. The user of this module has no need to know this - all he has to do is

use A::B

in his code, and expect that the module is picked up from the right location. Is it possible to do this?

Upvotes: 0

Views: 60

Answers (3)

ikegami
ikegami

Reputation: 386461

Messy:

BEGIN {
   my $version = '2.0';
   require "/home/$version/lib/A/B.pm";
}

my $obj = A::B->new();

If it's an external entity controlling the choice, then simply adjust Perl's library search path.

version=2.0
PERL5LIB="/home/$version/lib" script

use A::B qw( );

my $obj = A::B->new();

If it's an internal choice, use submodules instead.

package A::B;

sub new {
   my ($class, $version) = @_;
   $version =~ s/\./_/g;
   $class .= "::V$version";
   require $class;
   return $class->new(@_);
}

1;

use A::B qw( );

my $version = '2.0';
my $obj = A::B->new($version);

Upvotes: 3

Håkon Hægland
Håkon Hægland

Reputation: 40778

If you allow a different package name of the other version internally (while the user still uses A::B and does not know about the different names of the same module), you could achieve this. For example, let's say the different version of the module is internally called A::B::C. Then package A::B could be like this:

package A::B;

sub new {
    my ( $class, %opts ) = @_;

    if ( $opts{dir} eq "dir1" ) {
        require A::B::C;
        return A::B::C->new();
    }
    else {
        my $self = {};
        return bless $self, $class;
    }
}

1;

Upvotes: 0

larsen
larsen

Reputation: 1431

You can't have different versions of a module with the same name.

What I would do instead is having different subclasses of A::B, and a Factory class that instantiates objects of the appropriate class, choosing and loading it based on the factory invocation parameters.

For example:

my $out = A::B::Factory->create( dir => $dir );
# $out is a A::B::Foo or A::B::Bar depending on $dir

In the factory class:

package A::B::Factory;
use UNIVERSAL::require;

sub create {
  my (undef, %params) = @_;

  my $dir = $params{ dir };

  # some strategy to decide which class has to be used.
  # $class will be a string like "A::B::Foo" or "A::B::Bar"
  # or in general the same of an existing class in your system.
  my $class = ...; 

  $class->use;
  return $class->new();
}

Upvotes: 1

Related Questions