Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight

RFC: Alias::Exporter

by runrig (Abbot)
on Sep 20, 2006 at 19:47 UTC ( #573981=perlmeditation: print w/replies, xml ) Need Help??

This is a not-yet-(and perhaps never)-ready-for-primetime idea after looking at this idea for providing a way to export functions (and perhaps other symbols) to an aliased name, except that I wanted to try to create a way to do that with a plain, already existing module which exports in a "normal" way (update: but not necessarily using Exporter). This is not meant to be production code, it is just starting to play with the idea, and so far only exports functions. Toward the end of making this, I did see Exporter::VA, which is fine, but that module requires that the exporting modules use the Exporter module, which not all modules do (such as TheDamian's), or what if a module exports a code reference which does not already exist as a symbol in the module, etc., so there still may be some purpose to this code :-)
Example: #!/usr/bin/perl use strict; use warnings; use Alias::Exporter ( "Test::Foo" => { bar => 'foo' }, "Test::Bar" => { bar => 'baz' }, ); foo(); baz(); # file Test/ package Test::Foo; use Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw(bar); sub bar { print "Test::Foo::bar!\n"; } 1; # file Test/ package Test::Bar; use Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw(bar); sub bar { print "Test::Bar::bar!\n"; } 1; # the guts: warning - ugly code ahead package Alias::Exporter; use strict; use warnings; sub import { my $class = shift; my %modules = @_; my $package = caller(); for my $module ( keys %modules ) { my %imports = %{$modules{$module}}; my @import_list = keys %imports; eval qq| use $module ( \@import_list ); for my \$var ( \@import_list ) { no strict 'refs'; *{ \$package . "::\$imports{\$var}" } = \\&{\$var}; } |; die $@ if $@; } } 1;

Replies are listed 'Best First'.
Re: RFC: Alias::Exporter
by Tanktalus (Canon) on Sep 20, 2006 at 20:03 UTC

    There just has to be a way to remove that eval. ;-)

    Why do you 'use $module ( \@import_list )'? You don't need to import anything into Alias::Exporter's namespace. So you only need to require it instead. That can pull it out of the eval if you do the required modifications:

    (my $modname = $module . '.pm') =~ s,::,/,g; require $modname;
    Next, we want to pull that loop out of the eval. You have the right idea - it pretty much could have been done without the eval already, except that I just removed the importing from the other module. So now we need to point at the code in the desired module explicitly.
    for my $var (@import_list) { no strict 'refs'; *{$package . '::' . $imports{$var}} = *{$module . '::' . $var}; }
    Of course, having gotten rid of the eval, the die isn't needed anymore, either.

    A few more variables could make things a bit easier to read. Also, I would use references a bit more. This gives us:

    sub import { my $class = shift; my @modules = @_; my $package = caller(); for my $module ( keys %modules ) { my $imports = $modules{$module}; # we need to load the module if it doesn't already exist. (my $modname = $module . '.pm') =~ s,::,/,g; require $modname; for my $var (@import_list) { no strict 'refs'; my $from = join '::', $module, $var; my $to = join '::', $package, $imports->{var}; *{$to} = *{$from}; } } }
    Unfortunately, that's completely untested. And still needs more commenting ;-) Good luck,

      Ahh, but what if the module you're importing from has a custom import, where the thing being exported does not actually exist as a symbol in the exporting module, but gets imported to a symbol in the calling module (e.g. exporting a dynamically created anonymous subroutine)? That's why I import to the Alias module first, though I may still be able to get by without the eval. I know, it's a strange arbitrary edge-case, but what the hay.

      Update: Fixed some minor things in your code, and went back to importing to this module first. One thing to fix after that was the glob assignment. If you assign glob to glob, you end up with all functions initially imported with the same name being exported as the same (last exported) function (when you import to this module first). Another thing I might consider is using Tie::IxHash or something to process the lists of modules and symbols in their original order.

      Another update: Another consideration...we're totally polluting the namespace of this module by doing it this way, so maybe we should initially import to Alias::Exporter::Junk or something...

      sub import { my $class = shift; my %modules = @_; my $package = caller(); for my $module ( keys %modules ) { my $imports = $modules{$module}; # we need to load the module if it doesn't already exist. (my $modname = $module . '.pm') =~ s,::,/,g; require $modname; my @import_list = keys(%$imports); $module->import(@import_list); for my $var (@import_list) { no strict 'refs'; my $from = join '::', __PACKAGE__, $var; my $to = join '::', $package, $imports->{$var}; *{$to} = \&{$from}; } } }
Re: RFC: Alias::Exporter
by cees (Curate) on Sep 20, 2006 at 21:28 UTC

    You might want to have a look at Sub::Install which already allows you to alias functions that you import/export. Also, look at Sub::Exporter, which is a replacement for Exporter that allows aliasing of exported functions (among many other things).

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://573981]
Approved by Corion
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (2)
As of 2021-02-25 07:54 GMT
Find Nodes?
    Voting Booth?

    No recent polls found