A much improved and refined sub VERSION() for the implicit approach.
sub VERSION {
my ($module, $version) = @_;
return $VERSION unless $version; # satisfy UNIVERSAL::VERSION()
do { require File::Spec;
require File::Basename;
my $file = File::Spec->catfile( split /::/, $module ) . ".pm";
my $dir;
$dir = File::Basename::dirname( $dir || $INC{$file} )
for 0 .. $module =~ /::/g;
for my $dir ( local @INC = grep { !/^$dir$/ && -e File::Spec->
+catfile( $_, $file ) } @INC ) {
open MOD, "<", File::Spec->catfile( $dir, $file ) or warn
+"$@";
grep {
/(.*VERSION.*)/
and eval "eval '$1' == $version"
and do {
delete $INC{$file};
eval " require $module; $module->VERSION " == $ver
+sion and do return $version ;
shift @INC;
}
} <MOD>;
}
require Carp;
Carp::croak(" Loading $module ($version) failed. ");
} unless ($version == $VERSION);
}
A loader hook for @INC for explicit version control, basically just the above modified slightly.
BEGIN {
sub my_use {
my ($arg_ref, $module) = @_;
my $version;
local @INC;
(undef, $version, @INC) = @$arg_ref;
require File::Spec;
for my $dir ( grep { -e File::Spec->catfile( $_, $module ) } @
+INC ) {
my $file = File::Spec->catfile( $dir, $module );
open MOD, "<", $file or warn "failed opening $file : $@";
grep {
/(?:VERSION\s*=(.*))/
and eval "eval '$1' || 0" == $version
and do {
open my $fh, "<", $file or warn "open $file failed
+ : $@";
close MOD;
return $fh;
}
} <MOD>;
}
}
{
local @INC = [ \&my_use, "1.03", qw|a b c|, @INC ];
eval "use strict";
}
{
local @INC = [ \&my_use, "5.01", @INC, qw|a b c|];
eval "use Foo::Bar";
}
}
There's bound to be room for correction and improvement, especially to creating a simple and elegant data structure to house information about module and version numbers.
|