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

Adriano Ferreira aferreira at shopzilla.com
Fri May 30 14:20:59 BST 2008


On Fri, May 30, 2008 at 10:05 AM, Adriano Ferreira
<aferreira at shopzilla.com> wrote:
> 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 ;-) )

For the record, as far as I understand it, we don't inject INIT (or
any other BEGIN, CHECK, END) blocks to a module, because it looks like
Perl does not have such per-module control. Instead you need to time
the injection to happen based on some of the compile statements Perl
has: eval, do, require, etc. That's one of the things that is meant to
be better documented as soon as these kind of rules become more clear
to me. (For example, an INIT block injected at runtime may not run at
all or maybe it runs at the next compile statement in some surprising
way.)

> 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