Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Dynamic exports with Exporter::Easy

by wanna_code_perl (Friar)
on Oct 22, 2019 at 02:06 UTC ( [id://11107805]=perlquestion: print w/replies, xml ) Need Help??

wanna_code_perl has asked for the wisdom of the Perl Monks concerning the following question:

I'm working on a non-OO module that has a number of subs that are dynamically created and added to the symbol table. Their names are known at compile time, so I could certainly hard-code them in, but I'm a fan of DRY and would rather just specify them once.

When the module is loaded I have several subs that are string-eval'd into existence. In reality the body of the subs is more complex than a simple tr//, but this demonstrates the problem well enough. Everything works fine, but I'm stuck on the simple task of convincing Exporter::Easy to export the subs. I can make it work with Exporter just fine:

package Foo; use strict; use warnings; use Carp; my %subs = ( subA => _gen_tr('abc' => 'xyz'), subB => _gen_tr('ijk' => 'qrs'), ); $subs{reverse($_)} = $subs{$_} for keys %subs; # Stand-in example for +aliases no strict 'refs'; *$_ = $subs{$_} for keys %subs; require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = keys %subs; sub _gen_tr { /^\w+$/ or confess "Non-word characters in argument: <$_>" for @_; eval "sub { shift =~ tr/$_[0]/$_[1]/r }" } 1;

The above works fine; all subs and their aliases are exported as expected (passes test code at the end of this post).

But when I replace the require Exporter; our @ISA ...; our @EXPORT_OK ...; lines with a similar Exporter::Easy incantation:

use Exporter::Easy ( OK => [ keys %subs ], TAGS => [ all => [ keys %subs ], ], );

Code using Foo sees "subA" is not exported by the Foo module, whether I use Foo ':all' or specify subA directly. I've even tried wrapping the exports and actual %subs initialization in a BEGIN block, but that did not make a difference. Hardcoding the sub names directly in the OK => [ ... ] or all => [ ... ] lines does of course work, but that's exactly what I'm trying to avoid doing.

Why Exporter::Easy? One reason is, I like it. The "real" reason is, my full code has more tags and more complex exports. I can do what I want with Exporter, but it's ugly. I'm willing to consider other Exporter::* modules if Exporter::Easy can't handle it. Or, I suppose, to suck it up and use Exporter if there's no other way.

So how would you do it? $cake->have->eat;

Test code:

use strict; use warnings; use Test::More; # Pick one: #use Foo qw/subA subB Abus Bbus/; use Foo ':all'; is subA('abra-cadabra'), 'xyrx-zxdxyrx'; is subB('i like stuff'), 'q lqse stuff'; is Abus('abra-cadabra'), 'xyrx-zxdxyrx'; is Bbus('i like stuff'), 'q lqse stuff'; done_testing;

Replies are listed 'Best First'.
Re: Dynamic exports with Exporter::Easy
by Athanasius (Archbishop) on Oct 22, 2019 at 03:22 UTC

    Hello wanna_code_perl,

    Note that use MODULE LIST is equivalent to BEGIN { require MODULE; MODULE->import( LIST ); }; so, if you have:

    use Exporter::Easy ( OK => [ keys %subs ], TAGS => all => [ keys %subs ], ], );

    the implicit BEGIN block invokes a reference to the %subs hash before it has been initialised. Changing to require works for me (i.e., it passes your test script with either use Foo qw/subA subB Abus Bbus/; or use Foo ':all';):

    require Exporter::Easy; Exporter::Easy->import( OK => [ keys %subs ], TAGS => [ all => [ keys %subs ], ], );

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Thanks for the solution, but most of all, for breaking down the use semantics. You could have told me to RTFM and I probably would have figured it out from there, but I appreciate you taking the extra time to tie it back to my specific example.

Re: Dynamic exports with Exporter::Easy
by Anonymous Monk on Oct 22, 2019 at 10:18 UTC

    Their names are known at compile time, so I could certainly hard-code them in, but I'm a fan of DRY and would rather just specify them once.

    yes such repetition feels annoying but DRY is about authority not keyboard clicks

    package Foo is the authority however you code it

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11107805]
Approved by huck
Front-paged by huck
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others browsing the Monastery: (7)
As of 2024-04-18 07:18 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found