Any way to "inject" an INIT block into a different module?
Matt Lawrence
matt.lawrence at virgin.net
Fri May 30 14:06:02 BST 2008
untested stab-in-the-dark:
BEGIN { our $in_begin = 1 }
INIT {
our $in_begin = 0;
...
}
sub import {
if (our $in_begin) {
push @HOOKS, ... ;
}
else {
export ( ... )
}
}
Matt
Andy Wardley wrote:
>
> I'm messing with INIT blocks, trying to get some methods
> auto-generated at
> the right time.
>
> 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