http://qs1969.pair.com?node_id=411225

This is currently just pod for an idea I've had. Do you think it would be a useful module?

NAME

Module::Stubber - Provide hooks and handles to modules not in @INC

SYNOPSIS

use Module::Stubber; stub 'Foo::Bar' => { new => sub { bless {}, $_[0]; }, stringify => sub { "$_[0]"; } }; autorequire( qr/^Acme::/ => undef ); # Block access to Acme:: modul +es autorequire( 1 => sub { my $mod = shift; local @INC; unshift @INC,$special_lib; eval "require $mod"; !$@};

DESCRIPTION

There are several scenarios when this module could be useful. I have a case of using a module which pulls in a whole host of dependencies, some of which are missing on some platforms I am targetting. By providing surrogate code, at the expense of the full module functionality, it is possible to eliminate the chain dependency and allow deployment onto a wider range of platforms.

Also, this module can be used to hook to a CPAN or CPANPLUS load if the module has not already been installed (in the manner of Acme::Everything, but with more control).

Similarly, the modules might be installed elsewhere, such as a private LIB or PREFIX area. This could provide a way of hooking to these modules in such a way as to track whether and when any of these modules are being used, without stopping the functionality.

stub

The purpose of stub is to replace subs and method calls in a module that has not been required, with user supplied code. If the module has been successfully required, the genuine subs are left intact. Similarly if a successful require happens after the call to stub, the stub reference is overwritten.

autorequire

This is similar in concept to AUTOLOAD, but works for module names. When a require or use happens for a module outside @INC, autorequire is called with a parameter of the module name and any import list specified.

The first parameter to autorequire can be a regex, a code reference or a value. The regexp is matched against the module name, the code ref called passing in the module name, or the value tested; in each case for a true value. If this was true, the second parameter was called or tested, and execution continues if this is true. If the second parameter is false, the code carps with "Unable to require <module>".

Update: part of the concept in this RFC was released to CPAN as Module::Optional

--
I'm Not Just Another Perl Hacker

Replies are listed 'Best First'.
Re: RFC: Module::Stubber
by stvn (Monsignor) on Nov 30, 2004 at 18:07 UTC

    This is interesting. It reminds me of Test::MockObject and the more recently uploaded Test::MockModule in functionality, but for more general purpose use. I do have a few comments though.

    To start with the name is horrible. "Stubber" is just a weird word and you are probably better off just using Module::Stub which is not yet taken.

    Second, since use statements run at compile time, I would suggest offering an alternate interface for compile time stub-ing. Something like this.

    use Module::Stubber ( 'Foo::Bar' => {         new => sub { bless {}, $_[0]; },         stringify => sub { "$_[0]"; }   });
    The code to do so would be simple enough.

    Also I would be sure the stub module is properly added to the %INC hash, otherwise require and use will try to load the real module. (you may have already done this, but you didn't post code, so I don't know).

    Your description describes a number wonderous possibilites which your module opens up to it's user (hooking into CPAN, alternate @LIB directories, etc). I would create some of those possibilities, either as subclasses of your module or as plug-ins of some kind. Otherwise they are just empty promises. Doing so will also serve to illustrate the use of your module far better than any textual description can. It will also increase the usefulness of your module to a larger audience for whom writing such code is out of their range of skills.

    -stvn
      Thanks for the feedback. Perhaps Module::Stub is a better name. I also like your idea for using importer semantics instead of calling stub.

      I was aware of Test::MockObject, but not MockModule. However, these are tools for writing tests; I intend Module::Stub to be more general purpose, and useable in production code.

      Also I would be sure the stub module is properly added to the %INC hash, otherwise require and use will try to load the real module. (you may have already done this, but you didn't post code, so I don't know).
      This depends on the circumstances. I am intending to stub when the module is not installed on the target system, so the 'real' module won't be there anyway. But a lot depends on how Module::Stub is going to be used.

      Regarding the status of the module, I have not written any code yet, just the POD sections I posted in the root node. I do feel that there is sufficient interest here, and I will spend time on it and release the module. I will address the API issues of subclassing or plugins as I develop it. I also agree with your last sentence and revdiablo's comment - I want this module to be accessible to a large audience, which does not have a grasp of Perl's arcane features, such as symbol tables, coderefs in @INC, the %INC hash, etc.

      --
      I'm Not Just Another Perl Hacker

        I was aware of Test::MockObject, but not MockModule. However, these are tools for writing tests; I intend Module::Stub to be more general purpose, and useable in production code.

        At the moment Test::MockModule is pretty generic despite the namespace. It doesn't include any test functions right now. And if it did, they'd be optional so you could still use it in production code.

        See also Sub::Override, which is very similar to T::MM. The main differences are that T::MM allows you override subs that didn't previously exist, and inherited methods. And Sub::Override works on multiple packages, whereas T::MM only operates on one package per object

        -- simonflk

Re: RFC: Module::Stubber
by revdiablo (Prior) on Nov 30, 2004 at 18:12 UTC

    This sounds vaguely interesting, but I don't really understand what this is supposed to do. Your DESCRIPTION seems to do a good job of laying out scenarios when the module is useful, but it doesn't actually describe what the module does. Perhaps it should be blindingly obvious from the SYNOPSIS, but I just don't get it.

    Update: Ah, I should have read the individual function descriptions. Now I see what is going on. Maybe the documentation should be rearranged for dim-wits like myself. :-)