TIMTOWTGTDV!

I previously wrote a post called "Get the damned version". This post is tentatively entitled "There Is More Than One Way To Get The Damned Version" and speaks about the plethora of applications and modules to get versions. Brace yourself!

So, apparently I'm not the only one who hates how you cannot distinctively get the version of a specific module. We have various trickeries such as loading the module and printing it, trying to load an unknown - yet hopefully - non-existent advanced version and failing thus showing the version and a few other such odd possibilities... and then there are the modules and applications.

While use.perl.org proves yet again to suck (and this time by not being able to continuously supply the simple CPAN module uploads RSS stream), I've gone to the original stream (which shows every single module upload as an RSS item), I noticed that a new module and application surfaced called App::Mver.

I reckon this is time to recap:

  • Module::Extract::VERSION (by brian d. foy): as with any project that has brian's name on it, it's clean, simple and strict. It can return the version, the filename and even the line number of where the $VERSION variable was found, amongst other things. Under the hood it is basically the code taken from mldistwatch, but refactored (and most likely cleaned up). It uses ExtUtils::MakeMaker and runs an eval.


    Hidden benefits: it can find versions that were declared on two lines (our\n$VERSION) and it doesn't get confused by POD syntax. It being almost the exact same code that runs on PAUSE (and being authored by brian) means it's stable as F&$K. You can pretty much count on this.

    Disadvantage:
    it is something to be used only in a module, it does not have an application. That means that any added functionality is needed to be written by you.


  • Module::Which (by Adriano Ferreira): another module from a deservingly respected author. It exports a single function (using @EXPORT instead of @EXPORT_OK). It uses ExtUtils::MakeMaker as well, specifically the parse_version method. Clean, slick. It has some wrappers for returning the information back in a hash as well (giving named keys for the data) which is nice. Seems like a few other features were tried and so far hasn't materialized (or were thrown back into design) by the commented subroutines in the source.


    Benefits: the actual command (named which) supports multiple modules and perl itself. Adriano also took into account the amount you have to normally write without using this module and showed it in the POD.

    Disadvantages: while it is true this module allows you to write less in the command line, it is only valid if you write your own application wrapper around it. If not, you'll need to write practically the same amount of letters as you would in any other case.


  • Module::InstalledVersion (Kirrily "Skud" Robert): this module takes pretty much the same approach as others. It went a step beyond (?) and just copied the ExtUtils::MakeMaker version regex and matches against it.


    Benefits: returns both the version and the directory. It's a simple module and that is probably why the last release was in 2001.

    Disadvantages: it assumes the EU::MM regex will never change (specifically since the version of EU::MM it took the regex from), doesn't allow multiple, doesn't return additional information and has no CLI application.


  • V (by Abe Timmerman): except winning the title of shortest (and best) module name for this type of thing, Abe's V is the shortest way to get a module's name strictly via a module - thanks to the aforementioned title.


    Benefits: supports multiple modules, short, clean, nice. Doesn't suck to run as a command line utility.

    Disadvantages: copied some stuff from Module::Info and EU::MM_Unix and that is bad in case of updates to those modules. Also, as all mentioned above, it is missing a CLI app but thanks to clever design, it's not that bad!


  • App::whichpm (by Jozef Kutej): finally a proper application. It is even properly separated into a module and frontend CLI application. It was released slightly (a month) before Module::Version (which is covered last) and provides a beginner-friendly way of getting a module version. It implements its own find subroutine to find the module. It uses it (in eval) and then eval's the VERSION method of it. Although interesting, it is a behavior Module::Version has already deprecated.


    Benefits: few dependencies, which is nice. A CLI application which is exactly what all the above were missing.

    Disadvantages: this doesn't seem like it was written to really get a module's version, but rather the path to the module. The version retrieval seems more like a side-effect and that explains a lack of a variety of options for the application.


  • App::Mver (by KSURI): this module was originally released only yesterday. It is not just a module, but also an application. So far it sports the shortest name for an application to retrieve module versions. It does exactly the same thing as App::whichpm, except it doesn't try to find the path. The rest of the code is eval upon eval.


    Benefits: CLI application which is nice.

    Disadvantages: kind of pointless, now that other applications exist, especially when some actually run the exact same code as you are, literally. This module/app also doesn't provide any options, the CLI isn't documented and there is no --help. Also, mver seems like the name for an application wrapper or a better version of the Unix mv command, which is why I'll probably never install such an application - to avoid messing with names. They should try to be unique, IMHO.


  • Module::Version (by Sawyer X): obviously my favorite choice, because I wrote it to suit what I wanted and felt was needed. Unlike the aforementioned projects, Module::Version attempts to be as clear and yet as feature-full as possible for users. Not hardcore Perl programmers but users. Seasonal Perl programmers, beginners, sysadmins, and random users. Module::Version has many features, supports multiple usages and in light of it, I personally cannot fathom why App::mver was written (except, perhaps, for short-sight of the author). I'm not a narcissist person (at least I hope I'm not), but I reckon the benefits speak for themselves.


    Benefits: CLI application, relatively short app name (mversion), clearly separated to Module::Version which provides get_version (using @EXPORT_OK only) and Module::Version::App which is the logic behind mversion command line. It supports multiple module names, verbose syntax (name and version, not just the version), reading from an input file, warnings on missing (or modules it was unable to parse), quiet mode to suppress those warnings, non-evaling on dev versions option (returning 0.01_1 instead of 0.01, which is the default), and even including additional multiple directories like perl has with the -Ilib option. It also features full documentation and clean, concise code. The main function is literally 4 lines (not including sub declaration and closing brace). It honestly doesn't eval the result but uses ExtUtils::MakeMaker function to find the module file and parse the version.

    Disadvantages: it doesn't support local::lib yet, but it will! :)


C'est tout.

10 Comments

I once wrote my own tool for this called 'pmq', but it is a command-line tool only, not a library. It includes an --all option to report all installed modules.

Thanks for the review.
I saw your Module::Version when I did some search on CPAN, but somehow I missed that it comes with a CLI application too :(
App::whichpm did not came to light at all.

What I needed is just a simple and short replacement for damned perl -MModule -le'print Module->VERSION'. So I did.

My own tool, pmver.cmd, on Windows:

@perl -m%1 -E "say %1->VERSION"

Of course, it may have side effects as the module is loaded and executed contrary to ExtUtils::MakeMaker.

See also Module::Load::Conditional

Lucky for you that you didn't try to review Getopt modules or this would be a *really* long post :D

I used to think that the pmvers script from the pmtools venerable distribution is kind of the standard tool for your problem

I didn't provide an application in Module::Extract::VERSION because that's a lot of work that no one has made a good interface for. Even perldoc sucks at this. The module really existing for MyCPAN indexing.

The problem is that you have to find the right file. The perldoc code that does this sometimes does the wrong thing (like finding the *.pod file before the *.pm file). So, not being able to re-use the perldoc code, I'd have to write my own code to crawl through @INC. It's not terribly hard, but it's annoying.

So, crawling @INC is not that hard, but which @INC do I want to use? I have about 30 perls installed. How do I tell my application which one to use. That's not terribly difficult either. But what if I want federated results across all Perls.

Also, when I thought about writing the application, I wanted to find all instances of a module as well as reporting the one that perl would load.

I could write that application, but I haven't needed it so I haven't. Someone else could write that application so I could add it to Module::Extract::VERSION.

I myself have looked at just about every bit of code on the CPAN that can be used to extract the version from a module. The thing that makes it interesting is that if you run a single module through all of these different "extractors" you will likely end up with several different representations of the version!


Even more crazy is that older versions of these extractor modules may report additional different representations than the current versions.


Also, don't forget the code buried in things like Module::Build and ExtUtils::MakeMaker - those extract the versions to put them in the META.* files and also to determine the "version component" of the distribution tarball's file name!


Part of the problem also, is the dizzying number of ways that $VERSION is defined and specified throughout the CPAN. Dave Golden has an interesting post about this.


So far, the best I have been able to come up with is to normalize whatever value you've extracted from a module, and if you need to compare, compare with another normalized value:

$mod_ver_extracted = some_extraction_method( 'Foo::Bar' );
$mod_ver_normal = version->parse( $extracted_ver )->normal;
$desired_ver_normal = version->parse( '1.2.3' )->normal;
# do stuff with $mod_ver_normal and $desired_ver_normal

Leave a comment

About Sawyer X

user-pic Gots to do the bloggingz