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

Back in Automated module install, I started to look into installing modules from local tarballs directly. It's more-or-less working now, but I really would like some extra eyes to code-review part of this because it's confusing as heck.

This snippet is intended to simply take a tarball name, extract the package and version numbers from it, and return true if the module is already installed and its version is at least the version given. I am absolutely unconcerned about speed or memory usage, only about correctness. Assume @INC is set up properly. That is, I'm installing to a local directory, and that directory is already in @INC. CODE refs that should be in @INC are there, etc.. (Actually, I'm not using any of those, but leaving the problem somewhat generic...).

Assume, further, that strict and warnings are enabled higher up in the script, since they are.

It seems to work with trivial tests so far, but as more tarballs are added to the mix, I just want to make sure it stays working without having to come back in here and modify anything, if I can help it.

=item is_module_installed Tries to guess whether a module is installed (yet) or not. =cut sub is_module_installed { my $module_name = shift; my $mod = $module_name; $mod =~ s/-([\d\.]+\d).*$//; my $min_ver = $1; (my $mod_name = $mod) =~ s/-/::/g; $mod =~ s/(?:-|::)/\//g; $mod .= '.pm'; eval { require $mod; 1} or return 0; if ($min_ver or scalar @_) { $min_ver ||= shift; $min_ver = normalise_version($min_ver); my $mod_ver = do { no strict 'refs'; normalise_version(${"${mod_name}::VERSION"}); }; if ($mod_ver and $mod_ver lt $min_ver) { return 0; } } 1 } sub normalise_version { my $version = shift; my @subs; if ($version =~ /\./) { @subs = split /\./, $version; } else { @subs = map { ord } split //, $version; } join '.', sprintf(join('', ("%03d")x@subs), @subs); }

The usage is something like this:

my @tarballs = grep { not is_module_installed($_); } glob '*.tar*';
(That's not the exact code I'm using, but without the rest of the context, this is close enough.)

I'm open to any suggestions. Well, other than brace style. :-)

Thanks,

Replies are listed 'Best First'.
Re: Checking module (of a level) being installed
by cog (Parson) on Apr 11, 2005 at 16:14 UTC

      Thanks. I would like to point out that we have a slightly different take on things, which Perl is making more and more difficult ;-)

      On one hand, as you point out, that I completely forgot about, is that modules can load and do nasty things. Ones that do nasty things on require should be taken out behind the barn and shot. On use (aka, import) is another matter. And even then, nasty things such as those Acme modules aren't likely to make it into my corporate build tree :-)

      On the other hand, putting CODE refs into @INC is now perfectly plausible - I doubt any solution other than physically loading the module (somehow) will work. Placing your whole VM at risk of infection by whatever is being loaded... having a "use Blather;" line results in Acme::emcA would be problematic in many ways, not the least of which would be manifested at runtime. So, we'll assume, for the minute, that CODE refs in @INC are simply ways to load things in a funny way, and not ways to barrel over the existing code tree.

      At the bottom of it all is the fact that I'm not actually using CODE refs in @INC, nor using anything from Acme::*, so I'm going to take a closer look at ExtUtils::MM->version(). I'm not looking for the versions of every single module in the system - just a few. So the idea is to find those modules in @INC and see if ExtUtils::MM can figure out just those specific versions. Thanks!

Re: Checking module (of a level) being installed
by ysth (Canon) on Apr 11, 2005 at 16:55 UTC

      This is a very good point. I do have one feature that makes this really simple. It's called "mv". ;-)

      $ mv TermReadKey-2.30.tar.gz Term-ReadKey-2.30.tar.gz $ ci Term-ReadKey-2.30.tar.gz

      So now I'm going to have to write documentation to show what this script expects, and how to do it. But I definitely do thank you for this insight/reminder. I can almost imagine needing Term::ReadKey for stuff (unlike those Acme::* modules above ;->) and especially Date::Manip - that could be extremely useful at some point in the near future, so this could save me some headache.

Re: Checking module (of a level) being installed
by jacques (Priest) on Apr 11, 2005 at 16:31 UTC
    This might also be of help:
    use HTML::Perlinfo; perlinfo(INFO_MODULES);
    Okay, I will stop trumpeting my own modules now. ;)