Introducing Exporter::Almighty
Consider a simple module like this:
package MyApp::Util::Maths; use strict; use warnings; use constant PI => 3.14159265358979; use constant EULER => 2.71828182845905; use base 'Exporter'; our @EXPORT_OK = qw( PI EULER add ); our %EXPORT_TAGS = ( arithmetic => [ qw( add ) ], numbers => [ qw( PI EULER ) ], all => \@EXPORT_OK, ); sub add { my ( $x, $y ) = @_; return $x + $y; } 1;
You might use it like:
use MyApp::Util::Maths qw( PI add ); my $pi_plus_one = add( PI, 1 );
Exporter::Almighty is a module designed to reduce boilerplate in your utils-like modules, and increase their functionality.
The initial module can be rewritten as:
package MyApp::Util::Maths; use Exporter::Almighty -setup => { const => { numbers => { PI => 3.14159265358979, EULER => 2.71828182845905, }, }, tag => { arithmetic => [ 'add' ], }, }; sub add { my ( $x, $y ) = @_; return $x + $y; } 1;
Exporter::Almighty sets up your exporting automatically (but using Exporter::Tiny instead of Exporter), and calls use strict
and use warnings
on your behalf.
Exporter::Almighty creates your constants for you, so you don't need to duplicate your list of constants anywhere.
A bonus for your caller is that they can do:
use MyApp::Util::Maths qw( $PI $EULER );
To import read-only $PI
and $EULER
variables instead of traditional constants.
Your caller can also import things lexically:
my $pi_plus_one = do { use MyApp::Util::Maths -lexical, qw( add $PI ); add( $PI, 1 ); }; # add() and $PI are not defined outside the above block
Your caller can also rename any imported functions:
use MyApp::Util::Maths 'add' => { -as => 'sum_of' };
Exporter::Almighty has integrations with Type::Tiny making it easy to define and export Type::Tiny type constraints as part of your module. For example:
package MyApp::Util::Maths; use Exporter::Almighty -setup => { const => { ... }, tag => { ... }, type => { 'Types::Standard' => [ 'Int', 'Num' ] }, class => [ 'Calc' => { class => 'MyApp::Calculator' } ], }; ...; 1;
Now people can import the Int and Num type constraints from your module:
use MyApp::Util::Maths qw( Int );
They can even import a is_Int
function:
use MyApp::Util::Maths qw( is_Int );
You've also defined a Calc class type constraint which can be used like this:
has calculator => ( is => 'ro', isa => Calc, default => sub { Calc->new }, );
Exporter::Almighty makes defining enum-like data types easy:
package My::Properties { use Exporter::Almighty -setup => { enum => { 'Status' => [ 'alive', 'dead', 'undead' ] }, }; } package Story::Character { use Moo; use My::Properties -lexical, '+Status'; use experimental 'signatures'; has status => ( is => 'ro', isa => Status, default => STATUS_ALIVE, ); sub meet ( $self, $other ) { if ( $self->status eq STATUS_ALIVE and $other->status eq STATUS_UNDEAD ) { print "Help!\n"; } return $self; } }
Next time you're writing a module that needs to export things, consider Exporter::Almighty. It could make things very easy for you, while adding a bunch of useful features for your caller.
Leave a comment