Augmenting Exporter

TL;DR

Exporter can be easily “augmented”.

One of the things I’m looking into for deciding a slick interface for App::Easer is the import mechanism that happens when some module is used.

Which brings us to Exporter, of course.

My idea is to allow specifying something in the import list, while still allowing it to have its usual meaning of list of stuff that can be imported, according to @EXPORT_OK.

In the following example, we intercept import option -spec and its following companion hash reference, while letting the rest go to Exporter’s import mechanism:

package Augmenter;
use v5.24;
use warnings;
use experimental 'signatures';
no warnings 'experimental::signatures';
use Carp ();

our %spec_for;
our @EXPORT_OK = qw< foo bar >;

sub import ($package, @args) {
   my $target = caller;
   my @args_for_exporter;
   while (@args) {
      my $request = shift @args;
      if ($request eq '-spec') {
         Carp::croak "no specification provided"
            unless @args;
         Carp::croak "invalid specification provided"
            unless ref($args[0]) eq 'HASH';
         $spec_for{$target} = shift @args;
      }
      else { push @args_for_exporter, $request }
   }
   $package->export_to_level(1, $package, @args_for_exporter);
}

sub foo { ... }
sub bar { ... }

1;

The basic trick is to filter the “extension” parts out of the array that is eventually passed down to export_to_level (which is Exporter’s suggested method for doing this kind of intermediate stuff).

It’s interesting that we have to put the $package value between the 1 and @args_for_exporter. Well, I’m not 100% sure that it MUST always by $package, probably any scalar value will do; in any case, the function is supposed to expect a list of parameter in the same shape as that that arrives to import() itself, which includes the package name. In my perl installation, for example, I find this in Exporter::Heavy (which carries the actual implementation for export_to_level):

sub heavy_export_to_level
{
      my $pkg = shift;
      my $level = shift;
      (undef) = shift;                  # XXX redundant arg
      my $callpkg = caller($level);
      $pkg->export($callpkg, @_);
}

The (undef) = shift line tells us that anything can do… but I’ll stick to pass in the $package name, just to be on the safe side 🙄

So… I invite you too to remain on the safe side, as always!


Comments? Octodon, , GitHub, Reddit, or drop me a line!