Constant-Export-Lazy

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN


                    state $export_tags = do {
                        my %export_tags;
                        for my $constant (keys %$constants) {
                            my @export_tags = @{$constants->{$constant}->{options}->{stash}->{export_tags} || []};
                            push @{$export_tags{$_}} => $constant for @export_tags;
                        }
                        \%export_tags;
                    };

                    my @gimme = map {
                        /^:/ ? @{$export_tags->{$_}} : $_
                    } @$import_args;

                    return \@gimme;
                },
            },
        );

        1;

    And this is an example of using it in some user code (from
    t/synopsis_tags.t in the source distro):

        package My::More::User::Code;
        use strict;
        use warnings;
        use Test::More qw(no_plan);
        use lib 't/lib';
        use My::Constants::Tags qw(
            KG_TO_MG
            :math
            :alphabet
        );

        is(KG_TO_MG, 10**6);
        is(A, "A");
        is(B, "B");
        is(C, "C");
        like(PI, qr/^3\.14/);

    And running it gives:

        $ perl -Ilib t/synopsis_tags.t
        ok 1
        ok 2
        ok 3
        ok 4
        ok 5
        1..5

DESCRIPTION
    This is a library to write lazy exporters of constant subroutines. It's
    not meant to be a user-facing constant exporting API, it's something you
    use to write user-facing constant exporting APIs.

    There's dozens of modules on the CPAN that define constants in one way
    or another, why did I need to write this one?

  It's lazy
    Our constants are 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 constant exporting module that runs in
    different environments, and generates some subset of its constants
    depending on what the program that's using it actually needs.

    Some data that you may want to turn into constants may require modules
    that aren't available everywhere, queries to databases that aren't
    available everywhere, or make certain assumptions about the environment
    they're running under that may not be true across all your environments.

    By only defining those constants you actually need via callbacks
    managing all these special-cases becomes a lot easier.

  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 say you have several hundred constants like that, managing the
    inter-dependencies and ensuring that they're all defined in the right
    order with dependencies before dependents quickly gets messy.

    All this complexity becomes a non-issue when you use this module. When
    you define a constant you get a callback object that can give you the
    value of other constants.

    When you look up another constant we'll either generate it if it hasn't
    been materialized yet, or look up the materialized value in the symbol
    table if it has.

    Thus we end up with a Makefile-like system where you can freely use
    whatever other constants you like when defining your constants, and
    we'll lazily define the entire tree of constants on-demand.

    You only have to be careful not to introduce circular dependencies.

API
    Our API is exposed via a nested key-value pair list passed to "use", see
    the "SYNOPSIS" for an example. Here's description of the data structure
    you can pass in:

  constants
    This is a key-value pair list of constant names to either a subroutine
    or a hash with "call" and optional options. Internally we just convert
    the former type of call into the latter, i.e. "CONST => sub {...}"
    becomes "CONST => { call => sub { ... } }".

   call
    The subroutine we'll call with a context object to fleshen the constant.

    It's guaranteed that this sub will only ever be called once for the
    lifetime of the process, except if you manually call it multiple times
    during an "override".

   options (local)
    Our options hash to override the global "options". The semantics are
    exactly the same as for the global hash.

  options
    We support various options, most of these can be defined either globally
    if you want to use them for all the constants, or locally to one
    constant at a time with the more verbose hash invocation to "constants".

    The following options are supported:

   buildargs
    A callback that can only be supplied as a global option. If you provide
    this the callback we'll call it to munge any parameters to import we
    might get. This can be used (as shown in the synopsis) to strip or map
    parameters to e.g. implement support for %EXPORT_TAGS, or to do any
    other arbitrary mapping.

README  view on Meta::CPAN

    after they're defined, or push known constants to a hash somewhere so
    they can all be retrieved by some complimentary API that e.g. spews out
    "all known settings".

    You must "return:" from this subroutine, if anything's returned from it
    we'll die, this is to reserve any returning of values for future use.

   stash
    This is a reference that you can provide for your own use, we don't care
    what's in it. It'll be accessible via the context object's "stash"
    method (i.e. "my $stash = $ctx->stash") for "call", "override" and
    "after" calls relevant to its scope, i.e. global if you define it
    globally, otherwise local if it's defined locally.

   private_name_munger
    This callback can be defined either globally or locally. When it's
    provided it'll be used to munge the internal name of the subroutine we
    define in the exporting package.

    This allows for preventing the anti-pattern of user code not importing
    constants before using them. To take the example in the synopsis it's
    for preventing "My::Constants::PI" and "My::User::Code::PI"
    interchangeably, using this facility we can change "My::Constants::PI"
    to e.g. "My::Constants::SOME_OPAQUE_VALUE_PI".

    This is useful because users used to using other constant modules might
    be in the habit of using non-imported and imported names
    interchangeably.

    This is fine when the constant exporting module isn't lazy, however with
    Constant::Export::Lazy this relies on someone else having previously
    defined the constant at a distance, and if that someone goes away
    this'll silently turn into an error at a distance.

    By using the "private_name_munger" option you can avoid this happening
    in the first place by specifying a subroutine like:

        private_name_munger => sub {
            my ($gimme) = @_;

            # We guarantee that these constants are always defined by us,
            # and we don't want to munge them because legacy code calls
            # them directly for historical reasons.
            return if $gimme =~ /^ALWAYS_DEFINED_/;

            state $now = time();
            return $gimme . '_TIME_' . $now;
        },

    Anyone trying to call that directly from your exporting package as
    opposed to importing into their package will very quickly discover that
    it doesn't work.

    Because this is called really early on this routine doesn't get passed a
    $ctx object, just the name of the constant you might want to munge. To
    skip munging it return the empty list, otherwise return a munged name to
    be used in the private symbol table.

    We consider this a purely functional subroutine and you MUST return the
    same munged name for the same $gimme because we might resolve that
    $gimme multiple times. Failure to do so will result your callbacks being
    redundantly re-defined.

CONTEXT OBJECT
    As discussed above we pass around a context object to all callbacks that
    you can define. See $ctx in the "SYNOPSIS" for examples.

    This objects has only two methods:

    *   "call"

        This method will do all the work of fleshening constants via the sub
        provided in the "call" option, taking the "override" callback into
        account if provided, and if applicable calling the "after" callback
        after the constant is defined.

        If you call a subroutine you haven't defined yet (or isn't being
        imported directly) we'll fleshen it if needed, making sure to only
        export it to a user's namespace if explicitly requested.

        See "override" for caveats with calling this inside the scope of an
        override callback.

    *   "stash"

        An accessor for the "stash" reference, will return the empty list if
        there's no stash reference defined.

AUTHOR
    Ævar Arnfjörð Bjarmason <avar@cpan.org>

COPYRIGHT AND LICENSE
    This software is copyright (c) 2013 by Ævar Arnfjörð Bjarmason
    <avar@cpan.org>

    This is free software; you can redistribute it and/or modify it under
    the same terms as the Perl 5 programming language system itself.



( run in 0.564 second using v1.01-cache-2.11-cpan-39bf76dae61 )