NAME
    Import::Base - Import a set of modules into the calling module

VERSION
    version 0.011

SYNOPSIS
        ### Static API
        package My::Base;
        use base 'Import::Base';

        # Modules that are always imported
        our @IMPORT_MODULES = (
            'strict',
            'warnings',
            # Import only these subs
            'My::Exporter' => [ 'foo', 'bar', 'baz' ],
            # Disable uninitialized warnings
            '-warnings' => [qw( uninitialized )],
            # Callback to generate modules to import
            sub {
                my ( $bundles, $args ) = @_;
                return "My::MoreModule" => [qw( fuzz )];
            },
        );

        # Optional bundles
        our %IMPORT_BUNDLES = (
            with_signatures => [
                'feature' => [qw( signatures )],
                # Put this last to make sure nobody else can re-enable this warning
                '>-warnings' => [qw( experimental::signatures )]
            ],
            Test => [qw( Test::More Test::Deep )],
            Class => [
                # Put this first so we can override what it enables later
                '<Moo',
            ],
        );

        ### Consumer classes
        # Use only the default set of modules
        use My::Base;

        # Use one of the optional packages
        use My::Base 'with_signatures';
        use My::Base 'Test';
        use My::Base 'Class';

        # Exclude some things we don't want
        use My::Base -exclude => [ 'warnings', 'My::Exporter' => [ 'bar' ] ];

DESCRIPTION
    This module makes it easier to build and manage a base set of imports.
    Rather than importing a dozen modules in each of your project's modules,
    you simply import one module and get all the other modules you want.
    This reduces your module boilerplate from 12 lines to 1.

USAGE
  Base Module
    Creating a base module means extending Import::Base and creating an
    @IMPORT_MODULES package variable with a list of modules to import,
    optionally with a arrayref of arguments to be passed to the module's
    import() method.

    A common base module should probably include strict, warnings, and a
    feature set.

        package My::Base;
        use base 'Import::Base';

        our @IMPORT_MODULES = (
            'strict',
            'warnings',
            feature => [qw( :5.14 )],
        );

    Now we can consume our base module by doing:

        package My::Module;
        use My::Base;

    Which is equivalent to:

        package My::Module;
        use strict;
        use warnings;
        use feature qw( :5.14 );

    Now when we want to change our feature set, we only need to edit one
    file!

  Import Bundles
    In addition to a set of modules, we can also create optional bundles
    with the %IMPORT_BUNDLES package variable.

        package My::Bundles;
        use base 'My::Base';

        # Modules that will always be included
        our @IMPORT_MODULES
            experimental => [qw( signatures )],
        );

        # Named bundles to include
        our %IMPORT_BUNDLES = (
            Class => [qw( Moose MooseX::Types )],
            Role => [qw( Moose::Role MooseX::Types )],
            Test => [qw( Test::More Test::Deep )],
        );

    Now we can choose one or more bundles to include:

        # lib/MyClass.pm
        use My::Base 'Class';

        # t/mytest.t
        use My::Base 'Test';

        # t/lib/MyTest.pm
        use My::Base 'Test', 'Class';

    Bundles must always come before options. Bundle names cannot start with
    "-".

  Extended Base Module
    We can further extend our base module to create more specialized modules
    for classes and testing.

        package My::Class;
        use base 'My::Base';
        our @IMPORT_MODULES = (
            'Moo::Lax',
            'Types::Standard' => [qw( :all )],
        );

        package My::Test;
        use base 'My::Base';
        our @IMPORT_MODULES = (
            'Test::More',
            'Test::Deep',
            'Test::Exception',
            'Test::Differences',
        );

    Now all our classes just need to "use My::Class" and all our test
    scripts just need to "use My::Test".

    NOTE: Be careful when extending base modules from other projects! If the
    module you are extending changes, your modules may unexpectedly break.
    It is best to keep your base modules on a per-project scale.

  Unimporting
    Sometimes instead of "use Module" we need to do "no Module", to turn off
    "strict" or "warnings" categories for example.

    By prefixing the module name with a "-", Import::Base will act like "no"
    instead of "use".

        package My::Base;
        use base 'Import::Base';
        our @IMPORT_MODULES = (
            'strict',
            'warnings',
            feature => [qw( :5.20 )],
            '-warnings' => [qw( experimental::signatures )],
        );

    Now the warnings for using the 5.20 subroutine signatures feature will
    be disabled.

  -exclude
    When importing a base module, you can use "-exclude" to prevent certain
    things from being imported (if, for example, they would conflict with
    existing things).

        # Prevent the "warnings" module from being imported
        use My::Base -exclude => [ 'warnings' ];

        # Prevent the "bar" sub from My::Exporter from being imported
        use My::Base -exclude => [ 'My::Exporter' => [ 'bar' ] ];

    NOTE: If you find yourself using "-exclude" often, you would be better
    off removing the module or sub and creating a bundle, or only including
    it in those modules that need it.

  Control Ordering
    The order you import modules can be important!

        use warnings;
        no warnings 'uninitialized';
        # Uninitialized warnings are disabled

        no warnings 'uninitialized';
        use warnings;
        # Uninitialized warnings are enabled!

    Due to modules enforcing their own strict and warnings, like Moose and
    Moo, you may not even know it's happening. This can make it hard to
    disable the experimental warnings:

        use feature qw( postderef );
        no warnings 'experimental::postderef';
        use Moo;
        # The postderef warnings are back on!

    To force a module to the front or the back of the list of imports, you
    can prefix the module name with "<" or ">".

        package My::Base;
        use base 'Import::Base';
        our @IMPORT_MODULES = (
            feature => [qw( postderef )],
            # Disable this warning last!
            '>-warnings' => [qw( experimental::postderef )],
        );

        our %IMPORT_BUNDLES = (
            Class => [
                # Import this module first!
                '<Moo',
            ],
        );

        package main;
        use My::Base 'Class';
        my @foo = [ 1, 2, 3 ]->@*; # postderef!

    In this case, either putting Moo first or putting "no warnings
    'experimental::postderef'" last would solve the problem.

    NOTE: "<" and ">" come before "-".

    If you need even more control over the order, consider the "Dynamic
    API".

  Subref Callbacks
    To get a little bit of dynamic support in the otherwise static module
    lists, you may add sub references to generate module imports.

        package My::Base;
        use base 'Import::Base';
        our @IMPORT_MODULES = (
            sub {
                my ( $bundles, $args ) = @_;
                return (
                    qw( strict warnings ),
                    feature => [qw( :5.20 )],
                );
            },
        );

        # strict, warnings, and 5.20 features will be imported

    Plain strings are module names. Array references are arguments to
    import.

    NOTE: Subrefs cannot return modules with "<" or ">" to control ordering.
    Subrefs are run after the order has already been determined, while the
    imports are being executed. Subrefs can assume that imports before them
    have already been completed.

  Subref Arguments
    Sub references get an arrayref of bundles being requested, and a hashref
    of extra arguments. Arguments from the calling side start with a '-'.
    Arguments from Import::Base do not. Possible arguments are:

        package         - The package we are exporting to
        -exclude        - The exclusions, see L</"-exclude">.

    Using "package", a subref could check or alter @ISA, work with the
    object's metaclass (if you're using one), or export additional symbols
    not set up for export.

    Here's an example for applying a Moo::Role when importing a bundle:

        package My::Base;
        use base 'Import::Base';
        our %IMPORT_BUNDLES = (
            'Plugin' => [
                'Moo',
                # Plugins require the "My::Plugin" role
                sub {
                    my ( $bundles, $args ) = @_;
                    Moo::Role->apply_role_to_package( $args->{package}, 'My::Plugin' );
                    return;
                },
            ],
        );

        package My::Custom::Plugin;
        use My::Base 'Plugin';

  Custom Arguments
    When using "Subref Callbacks", you can add additional arguments to the
    "use" line. The arguments list starts after the first key that starts
    with a '-'. To avoid conflicting with any future Import::Base feature,
    prefix all your custom arguments with '--'.

        use My::Base -exclude => [qw( strict )], --custom => "arguments";
        # Subrefs will get $args{--custom} set to "arguments"

  Dynamic API
    Instead of providing @IMPORT_MODULES and %IMPORT_BUNDLES, you can
    override the "modules()" method to do anything you want.

        package My::Bundles;
        use base 'My::Base';

        sub modules {
            my ( $class, $bundles, $args ) = @_;

            # Modules that will always be included
            my @modules = (
                experimental => [qw( signatures )],
            );

            # Named bundles to include
            my %bundles = (
                Class => [qw( Moose MooseX::Types )],
                Role => [qw( Moose::Role MooseX::Types )],
                Test => [qw( Test::More Test::Deep )],
            );

            # Go to our parent class first
            return $class->SUPER::modules( $bundles, $args ),
                # Then the always included modules
                @modules,
                # Then the bundles we asked for
                map { @{ $bundles{ $_ } } } grep { exists $bundles{ $_ } } @$bundles;
        }

    Using the above boilerplate will ensure that you start with all the
    basic functionality.

    One advantage the dynamic API has is the ability to remove modules from
    superclasses, or completely control the order that modules are imported,
    even from superclasses.

METHODS
  modules( $bundles, $args )
    Prepare the list of modules to import. $bundles is an array ref of
    bundles, if any. $args is a hash ref of generic arguments, if any.

    Returns a list of MODULE => [ import() args ]. MODULE may appear
    multiple times.

DOCUMENTATION BOILERPLATE
    Here is an example for documenting your own base modules

        =head1 SYNOPSIS

            package MyModule;
            use My::Base;

            use My::Base 'Class';
            use My::Base 'Role';
            use My::Base 'Test';

        =head1 DESCRIPTION

        This is the base module that all {{PROJECT}} files should use.

        This module always imports the following into your namespace:

        =over

        =item L<strict>

        =item L<warnings>

        =item L<feature>

        Currently the 5.20 feature bundle

        =item L<experimental> 'signatures' 'postderef'

        We are using the 5.20 experimental signatures and postfix deref syntax.

        =back

        =head1 BUNDLES

        The following bundles are available. You may import one or more of these by name.

        =head2 Class

        The class bundle makes your package into a class and includes:

        =over 4

        =item L<Moo::Lax>

        =item L<Types::Standard> ':all'

        =back

        =head2 Role

        The role bundle makes your package into a role and includes:

        =over 4

        =item L<Moo::Role::Lax>

        =item L<Types::Standard> ':all'

        =back

        =head2 Test

        The test bundle includes:

        =over 4

        =item L<Test::More>

        =item L<Test::Deep>

        =item L<Test::Differences>

        =item L<Test::Exception>

        =back

        =head1 SEE ALSO

        =over

        =item L<Import::Base>

        =back

BEST PRACTICES
  One Per Project
    Every project of at least medium size should have its own base module.
    Consolidating a bunch of common base modules into a single distribution
    and releasing to CPAN may sound like a good idea, but it opens you up to
    difficult-to-diagnose problems.

    If many projects all depend on the same base, any change to the central
    base module could potentially break one of the consuming modules. In a
    single, well-tested project, it is easy to track down and address issues
    due to changes in the base module. If the base module is released to
    CPAN, breakage may not appear until someone tries to install a module
    that depends on your base.

    Version incompatibility, where project Foo depends on version 1 of the
    base, while project Bar depends on version 2, will create very
    frustrating situations for your users.

    Having to track down another project to figure out what modules are
    active in the current package is a lot of work, creating frustration for
    contributing authors.

SEE ALSO
    Import::Into
        The module that provides the functionality to create this module. If
        Import::Base doesn't do what you want, look at Import::Into to build
        your own.

    perl5
        This module is very similar, and has a bunch of built-in bundles and
        features for quickly importing Perl feature sets.

    ToolSet
        This is very similar, but does not appear to allow subclasses to
        remove imports from the list of things to be imported. By having the
        module list be a static array, we can modify it further in more
        levels of subclasses.

    Toolkit
        This one requires configuration files in a home directory, so is not
        shippable.

    rig This one also requires configuration files in a home directory, so
        is not shippable.

AUTHOR
    Doug Bell <preaction@cpan.org>

COPYRIGHT AND LICENSE
    This software is copyright (c) 2015 by Doug Bell.

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

