tags:

views:

85

answers:

3

I have a Perl module and I'd like to be able to pick out the parameters that my my module's user passed in the "use" call. Whichever ones I don't recognize I'd like to pass on. I tried to do this by overriding the "import" method but I'm not having much luck.

EDIT:

To clarify, as it is, I can use my module like this:

use MyModule qw/foo bar/;

which will import the foo and bar methods of MyModule. But I want to be able to say:

use MyModule qw/foo doSpecialStuff bar/;

and look for doSpecialStuff to check if I need to do some special stuff at the beginning of the program, then pass qw/foo bar/ to the Exporter's import

+7  A: 
Ether
OK cool! Now what would the best way to pick out the symbols I recognize and leave the rest? Grep does not seem to fit the bill.
JoelFan
Unrecognized symbols don't get ignored, they just get passed on to `Exporter`, where they will kill the script if they remain unrecognized.
mobrule
I don't want to ignore unrecognized symbols... I want to process them and then remove them
JoelFan
import is 100% based on `caller`, so you can't just use SUPER::import unless you want everything to get exported to the wrong package. That's why export_to_level is there.
hobbs
At first I had the same problem, but after doing $Exporter::ExportLevel = 1 it's working for me.
JoelFan
Set $Exporter::ExportLevel = 1 before calling SUPER::import and then the symbols will get exported to the package that's calling your module (and then set it back to 0 in case any other modules want to use Exporter).
mobrule
What about local $Exporter::ExportLevel = 1; ?
JoelFan
code updated, nice catch @hobbs, I clearly didn't test quite thoroughly enough :)
Ether
I would change the comment in your answer to read... "process @symbols and remove from @symbols everything we have processed". The point is that my "import" is looking for a certain special list of parameters to act on (not symbols to import) and anything that is not on that special list should be passed on to the superclass's import
JoelFan
+3  A: 

The typical use of Exporter is to declare your module to inherit from Exporter, and to have Exporter's import method called implicitly when your module is used. But this keeps you from creating your own import method for your module.

The workaround is to use Exporter's export_to_level method, which performs Exporter's functions without explicitly going through the Exporter::import method. Here's a typical way to use it:

package My::Module;
use base 'Exporter';   # or use Exporter; our @ISA=qw(Exporter);
our @EXPORT = qw(...);
our @EXPORT_OK = qw(...);
our %EXPORT_TAGS = (...);

sub import {
    my ($class,@import_args) = @_;
    my @import_args_to_pass_on = ();
    foreach my $arg (@import_args) {
       if (... want to process this arg here ...) {
          ...
       } else {
          push @import_args_to_pass_on, $arg;
       }
    }
    My::Module->export_to_level(1, "My::Module", @import_args_to_pass_on, @EXPORT);
    #or:  $class->export_to_level(1, $class, @import_args_to_pass_on, @EXPORT);
}
mobrule
Thanks... any reason to prefer this solution to the simpler one by "ehter" above?
JoelFan
@Joel: I think in this case they are equivalent. Exporter's docs say "[use] 'export_to_level' ... in situations where you can't directly call Exporter's import method" which doesn't seem to be the case here, but it definitely works just as well as the other way.. it's just longer. :)
Ether
@Joel, I'm trying Ether's code, but I can't get it to work. Maybe I'm not doing something right, but the methods I mean to export don't show up in the main symbol table.
mobrule
OK, got it to work, but had to include the line `$Exporter::ExportLevel=1` before calling `$class->SUPER::import(...)`
mobrule
I think calling SUPER::import is better than export_to_level because that way I get all the functionality of handling @EXPORT, @EXPORT_OK, etc., right?
JoelFan
+2  A: 

I have done it this way in my modules:

sub import {
    return if not @_;
    require Exporter;
    my $pkg = shift;

    # process @_ however you want

    unshift @_, $pkg;
    goto &Exporter::import;
}

you can also inherit from Exporter if you want unimport and the like.

Eric Strom
clever -- steps into `Exporter::import` but doesn't push a new frame onto the stack (so return values from `caller` are unaffected). You can't put any code after the `goto` statement and before the end of the method, though (you know, if you wanted to do that).
mobrule
Perl's goto is tricky, but it can really be handy sometimes! (if you know what you're doing) :)
Ether