Any way to "inject" an INIT block into a different module?

Adriano Ferreira aferreira at shopzilla.com
Fri May 30 14:05:55 BST 2008


On Fri, May 30, 2008 at 9:26 AM, Andy Wardley <abw at wardley.org> wrote:
> I'm messing with INIT blocks, trying to get some methods auto-generated at
> the right time.

That seems like the kind of things Devel::Hook was invented to do.
However, it is very experimental and imature by now.

http://search.cpan.org/dist/Devel-Hook/

(And there is the promise to change the module name "soon" -- for
definition of that ;-) )

Cheers,
Adriano.

> Here's an example.  Let's say I have an amplifier.  Naturally, it goes up
> to eleven.
>
>    package Amp;
>    use Control qw( volume sustain );
>
>    sub volume {
>        print "This amp goes up to eleven\n"
>    }
>
> My Control module looks something like this:
>
>    package Control;
>
>    our @HOOKS;
>    INIT { export(@_) for @HOOKS; }
>
>    sub import {
>        my $class = shift;
>        push(@HOOKS, [(caller(0))[0], @_]);
>    }
>
>    sub export {
>        my $target = shift;
>        no strict 'refs';
>
>        foreach my $sub (@_) {
>            if (defined &{"${target}::$sub"}) {
>                print "* skipping ${target}::$sub (already defined)\n";
>            }
>            else {
>                print "* creating ${target}::$sub\n";
>                *{"${target}::$sub"} = sub {
>                    print "Control $sub\n";
>                };
>            }
>        }
>    }
>
> It creates simple accessor methods in the Amp class, or indeed any class
> that you C<use> it in.  In this case, they're just dummy subs, but you
> get the idea.
>
> Because I don't want these auto-generated methods to overwrite any existing
> methods that I've written (like volume()), we skip over any methods that
> are already defined.  But to do this, we have to wait until the INIT phase
> to give Perl a chance to parse all the source code and figure out what
> methods have or haven't been defined.  So the import() method pushes the
> stuff onto @HOOKS for the INIT block to subsequently pipe back via export().
>
> So far so good (or evil, depending on your proximity to the crack pipe).
> This small test program:
>
>    use Amp;
>    Amp->volume;
>    Amp->sustain;
>
> Generates this output:
>
>    * skipping Amp::volume (already defined)
>    * creating Amp::sustain
>    This amp goes up to eleven
>    Control sustain
>
> At first glance this is most satisfactory.  If I'm on already on ten, all
> the way up, on ten on my guitar, and I need that extra push over the cliff,
> I can go up to eleven.  And I could go away and have a cup of tea and still
> be hearing that sustain.
>
> However, behind the glitzy facade of the topsy-turvy world of rock'n'roll,
> there limps a small and fragile pony over-burdened with a heavy load of
> FAIL.
> If you try and load the Amp module dynamically (let's say we're starting a
> band and we haven't yet decided if we're going to be electric or acoustic),
> like so:
>
>    use Control qw( sex drugs );
>
>    # later...
>    eval "use Amp";
>    Amp->volume;
>    Amp->sustain;
>
> Then it blows chunks thusly:
>
>    This amp goes up to eleven
>    Can't locate object method "sustain" via package "Amp" at ...blah
>
> Because, of course, the INIT time for my Control class has come and gone.
>
> Now I realise that I can work around this by forcing the caller to do some
> extra work, e.g. define their own INIT block or call a specific method
> to generate the methods at the right time.  But I'm trying to figure out
> if there's a way around it without requiring any extra work in the caller's
> package.  If only for chuckles.
>
> What I think I want to do, in conceptual terms at least, is to have my
> Control module "export" an INIT block/action into the Amp module.  I know
> I can't do that (at least I don't think I can, can I?) but that's the effect
> that I'm after.
>
> Any ideas?
>
> A
>


More information about the london.pm mailing list