NAME
Constant::Export::Lazy - Utility to write lazy exporters of constant subroutines
DESCRIPTION
This is a utility to write lazy exporters of constant subroutines. I.e. it's not meant to be a user-facing constant exporting API, it's meant to write user-facing constant exporting APIs.
There's dozens of similar constant defining modules on the CPAN, why did I need to write this one?
It's lazy
Our constants fleshened via callbacks that are guaranteed to be called only once for the lifetime of the process (not once per importer or whatever), and we only call the callbacks lazily if someone actually requests that a constant of ours be defined.
This makes it easy to have one file that runs in different environments and e.g. generates some subset of its constants with a module that you may not want to use, or may not be available in all your environments. You can just
requireit in the callback that generates the constant that requires it.It makes it easier to manage creating constants that require other constants
Maybe you have one constant indicating whether you're running in a dev environment, and a bunch of other constants that are defined differently if the dev environment constant is true.
Now imagine you have several hundred constants like that, managing the inter-dependencies and that everything is defined in the right order quickly gets messy.
Constant::Import::Lazy takes away all this complexity. When you define a constant you get a callback object that can give you the value of other constants, and will either generate them if they haven't been generated, or look them up in the symbol table if they have.
Thus we end up with a Makefile-like system where you can freely use whatever other constants you like when defining your constants, just be careful not to introduce circular dependencies.
SYNOPSIS
So how does all this work? This example demonstrates all our features. This is an example of your My::Constants package that you write using Constant::Export::Lazy:
package My::Constants;
use strict;
use warnings;
use Exporter 'import';
use constant {
X => -2,
Y => -1,
};
our @EXPORT_OK = qw(X Y);
use Constant::Export::Lazy (
constants => {
A => sub { 1 },
B => sub { 2 },
SUM => sub {
# You get a $ctx object that you can ->call() to retrieve
# the values of other constants if some of your constants
# depend on others. Constants are still guaranteed to only
# be fleshened once!
my ($ctx) = @_;
$ctx->call('A') + $ctx->call('B'),
},
# We won't call this and die unless someone requests it when
# they import us.
DIE => sub { die },
PI => {
# We can also supply a HashRef with "call" with the sub,
# and "options" with options that clobber the global
# options.
call => sub { 3.14 },
options => {
override => sub {
my ($ctx, $name) = @_;
# You can simply "return;" here to say "I don't
# want to override", and "return undef;" if you
# want the constant to be undef.
return $ENV{PI} ? "Pi is = $ENV{PI}" : $ctx->call($name);
},
},
},
},
options => {
# We're still exporting some legacy constants via Exporter.pm
wrap_existing_import => 1,
# A general override so you can override other constants in
# %ENV
override => sub {
my ($ctx, $name) = @_;
return unless exists $ENV{$name};
return $ENV{$name};
},
},
);
1;
And this is an example of using it in some user code:
package My::User::Code;
use strict;
use warnings;
use Test::More qw(no_plan);
use lib 't/lib';
BEGIN {
# Supply a more accurate PI
$ENV{PI} = 3.14159;
# Override B
$ENV{B} = 3;
}
use My::Constants qw(
X
Y
A
B
SUM
PI
);
is(X, -2);
is(Y, -1);
is(A, 1);
is(B, 3);
is(SUM, 4);
is(PI, "Pi is = 3.14159");
Things to note about this example:
We're using
$ctx-call($name)> to get the value of other constants while defining ours.That you can use either a global
overrideoption or a local per-sub one to override your constants via%ENVvariables, or anything else you can think of.We're using the global
wrap_existing_importoption, and Exporter to export some of our constants via constant.This demonstrates migrating an existing module that takes a list of constants (or labels) that don't overlap with our list of constants to
Constant::Export::Lazy.As well as supplying this option you have to
use Constant::Export::Lazyafter the other module defines itsimportsubroutine. Then we basically compose a list of constants we know we can handle, and dispatch anything we don't know about to theimportsubroutine we clobbered.
AUTHOR
Ævar Arnfjörð Bjarmason <avar@cpan.org>