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

This is just a thought about how to get around some of the problems listed in Getting Fed Up with ActiveState. However, it might have a more general purpose use, even though the thought gives me the willies.

Let's say that you need to use a particular function out of a module but the module won't compile on your system or, in the case of ActiveState, it's a core module you can't upgrade to the latest version. In the former case, maybe you only need to reimplement one function. In the case of the latter, maybe you can't find a PPM for it or (as in some cases), trying to explain to myriad users how to use a different repository is difficult. It might be nice to have a standard mechanism to work around this. Here's on thought, though I confess I'm not terribly happy with this.

use Module::Substitute 'Scalar::Util' => [qw/refaddr openhandle/]; print refaddr $some_object;

Of course, the devil is in the details, but here's a quick implementation/hack of this:

package Module::Substitute; use warnings; use strict; use Data::Dumper; our $VERSION = '0.01'; sub import { my $class = shift; my $calling_package = caller(0); my %module = @_; while ( my ($module, $import_list) = each %module ) { local $Data::Dumper::Terse = 1; $import_list = [$import_list] unless 'ARRAY' eq ref $import_list; my $imports = Dumper($import_list); $imports =~ s/^\[|\]$//g; my $substitute = __PACKAGE__ . "::$module"; eval <<" END_IMPORT"; package $calling_package; use $substitute $imports; END_IMPORT if ( my $error = $@ ) { _croak("Could not substitute ($module): $error"); } } } sub _croak { my $message = shift; require Carp; $Carp::CarpLevel = 1; Carp::croak($message); } 1;

With that, you'd set up a Module::Substitute::Scalar::Util which allows the functions to be exported. So far the above code seems to work fine in my initial tests.

Downsides: this has the obvious problem that we'd be likely to duplicate code and for every author willing to provide a Module::Substitute::Some::Other::Module package, they'd have to keep it in synch with the original package. This could be a serious problem.

Advantages: currently, authors who use features of upgraded core modules have a good chance of those failing the ActiveState build process. For example, consider the Scalar::Util::refaddr mentioned listed above. Since I use CPAN::Mini, I was able to use schwern's grep_cpan to search for modules which used refaddr (this was a naive check which didn't take into account that someone might be using a different function with the same name). After a bit of massaging the output and using a hack with HTML::TableExtract and LWP, I was able to list which of those modules were able to be built on Windows by ActiveState. My results?

Failed 95
Passed 9

Here's a quick sample of some of those modules:

I'm sure many of you have used a few of those. Do you really want them to not be available via PPM? If they're only failing because of refaddr, my proposal, though it requires code duplication, could potentially allow them to be available.

A cursory check of the nine which passed suggested that some were not directly importing refaddr from Scalar::Util but were instead relying on the fully-qualified function name and their test suites were not sufficiently robust enough to detect that whether it was available.

Frankly, I don't like my suggestion, but I'm trying to figure out a way around this problem and other suggestions would be welcome.

Cheers,
Ovid

New address of my CGI Course.

Replies are listed 'Best First'.
Re: Working Around Missing Modules (KISS2)
by tye (Sage) on Dec 03, 2006 at 02:15 UTC

    My previous reply was a near-trivial fix for getting your module to survive to become an ActiveState PPM, which I thought was the main problem. It is dawning on me that PPM can't support making a new release of Scalar::Util so here is the KISS way to solve that problem.

    I see no reason to require two modules, one to facilitate picking between two implementations (which is trivial to code) and another to provide the needed replacement. Just write one very simple module:

    package Scalar::Util::RefAddr; use vars ( @EXPORT_OK ); @EXPORT_OK= qw( refaddr ); require Exporter; *import= \&Exporter::import; eval { require Scalar::Util }; if( defined &Scalar::Util::refaddr ) { *refaddr= \&Scalar::Util::refaddr; } else { require DynaLoader; DynaLoader::bootstrap( __PACKAGE__ ); } 1;

    And cut'n'paste the XS code for refaddr() from Scalar::Util into RefAddr.xs.

    - tye        

Re: Working Around Missing Modules (KISS)
by tye (Sage) on Dec 02, 2006 at 15:10 UTC
    package aliased; BEGIN { eval { require Scalar::Util }; if( defined &Scalar::Util::refaddr ) { *refaddr= \&Scalar::Util::refaddr; } else { *refaddr= sub { 0+$_[0] }; } } ...

    - tye        

      That can fail with overloaded objects, thus forcing one back to reimplementing refaddr

      #!/usr/bin/perl use strict; use warnings; { package Foo; use overload '+' => sub { 'oops' }; sub new { bless {}, shift } } my $foo = Foo->new; print 0+$foo;

      Cheers,
      Ovid

      New address of my CGI Course.

        Duh. I don't see that as a big problem, frankly.

        My solution gets your module available from ActiveState as a PPM download (likely). Then for those who haven't learned the lesson of ETOOMUCHMAGIC and not only insist on using aliased.pm and overload.pm but using them together need only make sure they have a new enough Scalar::Util and everything works.

        Yes, it'd be nice if you could encode into the automation tools "Require Scalar::Util, but if you use overload.pm on your aliased module, then require version X of it".

        An improved version of my fix is:

        package aliased; BEGIN { eval { require Scalar::Util }; if( defined &Scalar::Util::refaddr ) { *refaddr= \&Scalar::Util::refaddr; } else { eval { require overload }; if( defined &overload::Overloaded ) { *refaddr= sub { if( overload::Overloaded( $_[0] ) ) { require Carp; Carp::confess( "Upgrade Scalar::Util ..." ); } return 0+$_[0]; }; } else { *refaddr= sub { 0+$_[0] }; } } } ...

        - tye        

Re: Working Around Missing Modules
by rinceWind (Monsignor) on Dec 03, 2006 at 01:05 UTC

    You might be interested in Module::Optional as shimware. The original motivation for my writing this module shares common ground with your scenario.

    --

    Oh Lord, won’t you burn me a Knoppix CD ?
    My friends all rate Windows, I must disagree.
    Your powers of persuasion will set them all free,
    So oh Lord, won’t you burn me a Knoppix CD ?
    (Missquoting Janis Joplin)

Re: Working Around Missing Modules
by syphilis (Archbishop) on Dec 02, 2006 at 14:57 UTC
    or, in the case of ActiveState, it's a core module you can't upgrade to the latest version

    This is where I start to get lost.

    Precisely which core module is it that one is unable to update on ActivePerl using the freely available M$ compiler and/or the freely available MinGW compiler ?

    Cheers,
    Rob

      You've asked this question before, so I'll just refer you back to the answer I've already given you. If it was just myself, I'd happily accept your solution and it's the sort of thing I've done before. However, as mentioned in my previous reply, if someone is in the position of pushing this solution on thousands of end users, it doesn't work. Tool chains should just work and I'm not comfortable of the idea of just shrugging and telling others "ain't my problem".

      Cheers,
      Ovid

      New address of my CGI Course.

        You've asked this question before, so I'll just refer you back to the answer I've already given you

        Would be better if you could just answer the question ... rather than refer me back to some non-existent answer :-)

        Cheers,
        Rob