Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

multi-level exporting from within module

Status
Not open for further replies.

fishiface

IS-IT--Management
Feb 24, 2003
687
GB
I am trying to write a module which exports functions from another module as well as a number of functions of it's own. For example, given the code below, I would like a script calling
[tt]use CGI::MyWidgets;[/tt]
to have access to all the functions exported by CGI as implied by the [tt]use CGI[/tt] line as well as those in @EXPORT.

[tt]package CGI::MyWidgets;

BEGIN {
use strict;
use CGI qw/:standard :html3 start_table font shortcuts/;
use CGI::Carp qw/ fatalsToBrowser warningsToBrowser /;
use Exporter ();
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
$VERSION = 0.01;
@ISA = qw{Exporter}; # I is a exporter ?!?
@EXPORT = qw{ wt wd wn wm wx wc }; # always exported
%EXPORT_TAGS = (); # eg TAG=>[qw! name1 name2 !],
@EXPORT_OK = (); # exportable package globals and subs
}
# Function Definitions...
sub wt {
....[/tt]

If I list the CGI functions individually in @EXPORT then everything works as I wish but: (i) there are many of them and alarm bells go off in my head every time I find I'm copy-typing; and (ii) they may change and I would like my code to be robust enough to cope with this.

I have read about the [tt]export_to_level[/tt] method of the Exporter module but think that although this could be used by CGI to export functions up through my module, it could not be used by my module to push the CGI functions up to main.

I have tried code like this in my module
[tt]package main;
use CGI qw/:standard :html3 start_table font shortcuts/;
use CGI::Carp qw/ fatalsToBrowser warningsToBrowser /;
package CGI::MyWidgets;

BEGIN {
use strict;
use CGI qw/:standard :html3 start_table font shortcuts/;
use CGI::Carp qw/ fatalsToBrowser warningsToBrowser /;
....
[/tt]
This works but looks ugly and is (probably) inefficient: does CGI.pm get parsed twice?

The only solution that I have found that looks vaguely reasonable is to grabs CGI's symbol table entries directly and include a line like
[tt]push @EXPORT, keys %CGI::;[/tt]
but I can't help feeling that there is an elegant solution just around the corner....

Yours, "As soon as we started programming, we found to our surprise that it wasn't as
easy to get programs right as we had thought. Debugging had to be discovered.
I can remember the exact instant when I realized that a large part of my life
from then on was going to be spent in finding mistakes in my own programs."
--Maurice Wilk
 
Well, I thought that it would be possible to just add all of CGI's EXPORT* variables to the CGI::MyWidgets EXPORT* variables using
Code:
    push @EXPORT, @CGI::EXPORT;
    push @EXPORT_OK, @CGI::EXPORT_OK;
    %EXPORT_TAGS = %CGI::EXPORT_TAGS;
# Or if you have already defined %EXPORT_TAGS
#   %EXPORT_TAGS = ( %EXPORT_TAGS, %CGI::EXPORT_TAGS );
Then you could envoke the module as
Code:
use CGI::MyWidgets qw(:standard);
and import all the same functions you would with '[tt]use CGI qw:)standard)[/tt]'.

Unfortunately, CGI.pm doesn't use a standard format of $EXPORT_TAGS. The module overloads the import function to preprocess the %EXPORT_TAGS. The only way to get this to work was to invoke CGI's import function, but CGI's import() calls another function as well. To patch that up I had to use AUTOLOAD to handle any unknow functions that might get called by import(). I think my idea turned out not to be elegant at all but here it is:
Code:
# Add these to the BEGIN block
    push @EXPORT, @CGI::EXPORT;
    push @EXPORT_OK, @CGI::EXPORT_OK;
    %EXPORT_TAGS = %CGI::EXPORT_TAGS;
    *import = *CGI::import;

# Add somewhere in the CGI::MyWidgets package
# Pass unknown function calls to CGI
sub AUTOLOAD {

    (my $name = $AUTOLOAD) =~ s/.*:://;
    my $cgi_method = "CGI::$name";
    $cgi_method->(@_);
}
Perhaps trying to inherit namespaces without using an OO interface isn't the most elegant method. It was a pretty enlightening exercise, though.

jaa

 
Thanks for the ideas - I learnt a lot trying to get my head around what CGI.pm's [tt]import[/tt] was doing but didn't experience any epiphanies.

I found a small improvement to my last attempt:
[tt]push @EXPORT, keys %CGI::;[/tt]
ought to be
[tt]push @EXPORT, keys %CGI::MyWidgets::;[/tt]
so that I export everything that CGI has already exported to me rather than everything in it's own namespace, some of which is intended to be private.

I think that your point about mixing a namespace paradigm and an OO paradigm is probably the key point. If I create a CGI object and stick to calling it's methods explicitly then I can simply subclass it to add my own methods. It's when I get bored typing $q-> for the umpteenth time that it starts falling apart;-)

I think, given that it's currently working to spec, I'll just forget the implementation, comment the thing thoroughly and move on.

Thanks for your help anyway,



"As soon as we started programming, we found to our surprise that it wasn't as
easy to get programs right as we had thought. Debugging had to be discovered.
I can remember the exact instant when I realized that a large part of my life
from then on was going to be spent in finding mistakes in my own programs."
--Maurice Wilk
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top