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