Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Re: Notification on module load, and redefinition of module methods.

by lodin (Hermit)
on Feb 01, 2008 at 17:10 UTC ( [id://665609]=note: print w/replies, xml ) Need Help??


in reply to Notification on module load, and redefinition of module methods.

I don't quite understand what you mean when you say that the module is reloaded. Are you using Module::Reload or somesuch? A little more information about that would probably be helpful.

As for wrapping a subroutine you usually can use this format:

my $old = \&IWL::Script::setScript; *IWL::Script::setScript = sub { ...; goto &$old; };
You assign to *IWL::Script::setScript at runtime, after you've saved away the old code in $old. Using goto here is good as your wrapper subroutine will be transparent to the original subroutine and not confuse it (or e.g. Carp). If you want to change a parameter you replace the element in @_ using push/pop, unshift/shift or splice or assign to @_ = .... You generally don't want to do $_[...] = ... because @_ is aliased. In your case you might want to do
# Untested. use Scalar::Util 'reftype'; use 5.010; # For ~~. my $old = \&IWL::Script::setScript; *setScript = sub { my ($self, $param) = @_; $param = $p2js->covert($param) if reftype($param) ~~ 'CODE'; @_ = ($self, $param); # or: #splice @_, 1, 1, $p2js->convert($param) # if reftype($param) ~~ 'CODE'; goto &$old; };

Hope this helps,
lodin

Replies are listed 'Best First'.
Re^2: Notification on module load, and redefinition of module methods.
by /dev/urandom (Beadle) on Feb 01, 2008 at 20:17 UTC
    No, I'm not using any module to 'reload'. Here's a more detailed outline of this problem.

    Whenever a user requires some module, like this: 'require IWL::Script', my current approach was to have a subref in @INC. This subref would get called, so that it can be used to find the wanted module. Of course, I used this approach in a totally different way. So inside this subref, while the user's require is still going, I require the module myself, so that it's compiled. Unfortunately, Perl will still load the wanted module, after it exits from my subref. And since I've already loaded the same module myself, it will reload it on top of what I've loaded, thus, erasing whatever changes I've made to the symbol table.

    Thanks for the second suggestion though. I'll try it and see if it works. My biggest concern is that my $old = \&IWL::Script::setScript; might create an alias, instead of a copy of the subref. So if i'd invoke the goto &$old, it might create a circular reference. Also, from what I've understood of your post, the $old copy will receive the same @_ that is visible in the scope of the wrapper, correct?

      The wrapping works as I wrote it--there will be no circular fatal recursion. In order to understand why one must understand how typeglobs work. \&foo is copying *foo{CODE} which is the code slot for the foo symbol. *foo = sub { ... } on the other hand assigns a new value to *foo{CODE}, given that the subroutine foo was defined already.

      My first approach to do what you want would be to simply require IWL::Script right away instead of putting a hook in @INC, and then you can safely wrap the subroutine. Once the module is loaded it won't reload upon a new require (unless someone has fiddled with %INC).

      A big problem with your technique is that someone somewhere might unshift elements into @INC and therefore your hook may miss to wrap the subroutine. I'm not sure if tieing @INC is supported, but that could be a solution. However, if someone else ties @INC your tie will be superceeded, and again you miss to wrap the subroutine. Or if someone else already has tied @INC you'll have trouble too.

      I really recommend you to just load the module straight away. If you for some reason want to delay the loading of the module you need to do two things, one of which you've already solved:

      • Make sure your hook doesn't interfere with your require in it.
      • Stop the @INC search.
      The second point is done by returning a code ref. (perldoc -f require has all the details about that.) Here's an example:
      use strict; require lib; lib::->import( sub { my ($self, $filename) = @_; lib::->unimport($self); # Enable require below. require Benchmark; # Some module that exists, just as demo. lib::->import($self); # Put it back in business. my $rc = 1; return sub { $_ = 1 if $rc; return $rc--; }; } ); print "Trying to require some modules that doesn't exist, "; require This::Does::Not::Exist; require Cwd; require This::Does::Not::Exist::Either; print "and it worked.\n"; __END__ Trying to require some modules that doesn't exist, and it worked.
      The code reference returned by the hook first sets $_ to a true value that is taken as the source code for the module that's required, and returns 1 to signal success. Then it returns 0 to signal EOF.

      Applying this to your case, you change require Benchmark; to require $filename; in the hook, and after that you wrap the subroutine.

      Now you hopefully have the bits and pieces to make this work the way you want it to work.

      lodin

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://665609]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (3)
As of 2024-04-25 20:35 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found