{"id":27197674,"url":"https://github.com/preaction/import-base","last_synced_at":"2025-06-10T15:07:26.398Z","repository":{"id":16438635,"uuid":"19190235","full_name":"preaction/Import-Base","owner":"preaction","description":"Import a set of modules into the calling module","archived":false,"fork":false,"pushed_at":"2018-01-16T19:19:16.000Z","size":145,"stargazers_count":0,"open_issues_count":1,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-09T20:43:04.136Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Perl","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/preaction.png","metadata":{"files":{"readme":"README","changelog":"CHANGES","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-04-26T22:35:26.000Z","updated_at":"2016-03-01T02:57:58.000Z","dependencies_parsed_at":"2022-09-14T07:11:33.147Z","dependency_job_id":null,"html_url":"https://github.com/preaction/Import-Base","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/preaction%2FImport-Base","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/preaction%2FImport-Base/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/preaction%2FImport-Base/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/preaction%2FImport-Base/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/preaction","download_url":"https://codeload.github.com/preaction/Import-Base/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/preaction%2FImport-Base/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259098499,"owners_count":22804784,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-04-09T20:29:20.121Z","updated_at":"2025-06-10T15:07:26.376Z","avatar_url":"https://github.com/preaction.png","language":"Perl","funding_links":[],"categories":[],"sub_categories":[],"readme":"=head1 SYNOPSIS\n\n    ### Static API\n    package My::Base;\n    use base 'Import::Base';\n\n    # Modules that are always imported\n    our @IMPORT_MODULES = (\n        'strict',\n        'warnings',\n        # Import only these subs\n        'My::Exporter' =\u003e [ 'foo', 'bar', 'baz' ],\n        # Disable uninitialized warnings\n        '-warnings' =\u003e [qw( uninitialized )],\n        # Test for minimum version\n        { 'Getopt::Long' =\u003e 2.31 },\n        # Callback to generate modules to import\n        sub {\n            my ( $bundles, $args ) = @_;\n            return \"My::MoreModule\" =\u003e [qw( fuzz )];\n        },\n    );\n\n    # Optional bundles\n    our %IMPORT_BUNDLES = (\n        with_signatures =\u003e [\n            'feature' =\u003e [qw( signatures )],\n            # Put this last to make sure nobody else can re-enable this warning\n            '\u003e-warnings' =\u003e [qw( experimental::signatures )]\n        ],\n        Test =\u003e [qw( Test::More Test::Deep )],\n        Class =\u003e [\n            # Put this first so we can override what it enables later\n            '\u003cMoo',\n        ],\n    );\n\n    ### Consumer classes\n    # Use only the default set of modules\n    use My::Base;\n\n    # Use one of the optional packages\n    use My::Base 'with_signatures';\n    use My::Base 'Test';\n    use My::Base 'Class';\n\n    # Exclude some things we don't want\n    use My::Base -exclude =\u003e [ 'warnings', 'My::Exporter' =\u003e [ 'bar' ] ];\n\n=head1 DESCRIPTION\n\nThis module makes it easier to build and manage a base set of imports. Rather\nthan importing a dozen modules in each of your project's modules, you simply\nimport one module and get all the other modules you want. This reduces your\nmodule boilerplate from 12 lines to 1.\n\n=head1 USAGE\n\n=head2 Base Module\n\nCreating a base module means extending Import::Base and creating an\nC\u003c@IMPORT_MODULES\u003e package variable with a list of modules to import,\noptionally with a arrayref of arguments to be passed to the module's import()\nmethod.\n\nA common base module should probably include L\u003cstrict|strict\u003e,\nL\u003cwarnings|warnings\u003e, and a L\u003cfeature|feature\u003e set.\n\n    package My::Base;\n    use base 'Import::Base';\n\n    our @IMPORT_MODULES = (\n        'strict',\n        'warnings',\n        feature =\u003e [qw( :5.14 )],\n    );\n\nNow we can consume our base module by doing:\n\n    package My::Module;\n    use My::Base;\n\nWhich is equivalent to:\n\n    package My::Module;\n    use strict;\n    use warnings;\n    use feature qw( :5.14 );\n\nNow when we want to change our feature set, we only need to edit one file!\n\n=head2 Import Bundles\n\nIn addition to a set of modules, we can also create optional bundles with the\nC\u003c%IMPORT_BUNDLES\u003e package variable.\n\n    package My::Bundles;\n    use base 'My::Base';\n\n    # Modules that will always be included\n    our @IMPORT_MODULES\n        experimental =\u003e [qw( signatures )],\n    );\n\n    # Named bundles to include\n    our %IMPORT_BUNDLES = (\n        Class =\u003e [qw( Moose MooseX::Types )],\n        Role =\u003e [qw( Moose::Role MooseX::Types )],\n        Test =\u003e [qw( Test::More Test::Deep )],\n    );\n\nNow we can choose one or more bundles to include:\n\n    # lib/MyClass.pm\n    use My::Base 'Class';\n\n    # t/mytest.t\n    use My::Base 'Test';\n\n    # t/lib/MyTest.pm\n    use My::Base 'Test', 'Class';\n\nBundles must always come before options. Bundle names cannot start with \"-\".\n\n=head2 Extended Base Module\n\nWe can further extend our base module to create more specialized modules for\nclasses and testing.\n\n    package My::Class;\n    use base 'My::Base';\n    our @IMPORT_MODULES = (\n        'Moo::Lax',\n        'Types::Standard' =\u003e [qw( :all )],\n    );\n\n    package My::Test;\n    use base 'My::Base';\n    our @IMPORT_MODULES = (\n        'Test::More',\n        'Test::Deep',\n        'Test::Exception',\n        'Test::Differences',\n    );\n\nNow all our classes just need to C\u003cuse My::Class\u003e and all our test scripts just\nneed to C\u003cuse My::Test\u003e.\n\nB\u003cNOTE:\u003e Be careful when extending base modules from other projects! If the\nmodule you are extending changes, your modules may unexpectedly break. It is\nbest to keep your base modules on a per-project scale.\n\n=head2 Unimporting\n\nSometimes instead of C\u003cuse Module\u003e we need to do C\u003cno Module\u003e, to turn off\nC\u003cstrict\u003e or C\u003cwarnings\u003e categories for example.\n\nBy prefixing the module name with a C\u003c-\u003e, Import::Base will act like C\u003cno\u003e\ninstead of C\u003cuse\u003e.\n\n    package My::Base;\n    use base 'Import::Base';\n    our @IMPORT_MODULES = (\n        'strict',\n        'warnings',\n        feature =\u003e [qw( :5.20 signatures )],\n        '-warnings' =\u003e [qw( experimental::signatures )],\n    );\n\nNow the warnings for using the 5.20 subroutine signatures feature will be\ndisabled.\n\n=head2 Version Check\n\nThe standard Perl C\u003cuse\u003e function allows for a version check at compile\ntime to ensure that a module is at least a minimum version.\n\n    # Require Getopt::Long version 2.31 or higher\n    use Getopt::Long 2.31;\n\nGenerally, you should be declaring your dependency with the correct version,\nbut some modules (like Getopt::Long) change their behavior based on what\nversion you ask for.\n\nTo ask for a specific version, use a hashref with the key is the module and\nthe value as the required version.\n\n    our @IMPORT_MODULES = (\n        # Require a minimum version\n        { 'Getopt::Long' =\u003e 2.31 },\n        # Version and imports\n        { 'File::Spec::Functions' =\u003e 3.47 } =\u003e [qw( catfile )],\n    );\n\n=head2 -exclude\n\nWhen importing a base module, you can use C\u003c-exclude\u003e to prevent certain things\nfrom being imported (if, for example, they would conflict with existing\nthings).\n\n    # Prevent the \"warnings\" module from being imported\n    use My::Base -exclude =\u003e [ 'warnings' ];\n\n    # Prevent the \"bar\" sub from My::Exporter from being imported\n    use My::Base -exclude =\u003e [ 'My::Exporter' =\u003e [ 'bar' ] ];\n\nNOTE: If you find yourself using C\u003c-exclude\u003e often, you would be better off\nremoving the module or sub and creating a bundle, or only including it in those\nmodules that need it.\n\n=head2 Control Ordering\n\nThe order you import modules can be important!\n\n    use warnings;\n    no warnings 'uninitialized';\n    # Uninitialized warnings are disabled\n\n    no warnings 'uninitialized';\n    use warnings;\n    # Uninitialized warnings are enabled!\n\nDue to modules enforcing their own strict and warnings, like L\u003cMoose\u003e and\nL\u003cMoo\u003e, you may not even know it's happening. This can make it hard to disable the\nexperimental warnings:\n\n    use feature qw( postderef );\n    no warnings 'experimental::postderef';\n    use Moo;\n    # The postderef warnings are back on!\n\nTo force a module to the front or the back of the list of imports, you can prefix\nthe module name with C\u003cE\u003clt\u003e\u003e or C\u003cE\u003cgt\u003e\u003e.\n\n    package My::Base;\n    use base 'Import::Base';\n    our @IMPORT_MODULES = (\n        feature =\u003e [qw( postderef )],\n        # Disable this warning last!\n        '\u003e-warnings' =\u003e [qw( experimental::postderef )],\n    );\n\n    our %IMPORT_BUNDLES = (\n        Class =\u003e [\n            # Import this module first!\n            '\u003cMoo',\n        ],\n    );\n\n    package main;\n    use My::Base 'Class';\n    my @foo = [ 1, 2, 3 ]-\u003e@*; # postderef!\n\nIn this case, either putting Moo first or putting C\u003cno warnings\n'experimental::postderef'\u003e last would solve the problem.\n\nB\u003cNOTE:\u003e C\u003cE\u003clt\u003e\u003e and C\u003cE\u003cgt\u003e\u003e come before C\u003c-\u003e.\n\nIf you need even more control over the order, consider the L\u003c/\"Dynamic API\"\u003e.\n\n=head2 Subref Callbacks\n\nTo get a little bit of dynamic support in the otherwise static module lists, you may\nadd sub references to generate module imports.\n\n    package My::Base;\n    use base 'Import::Base';\n    our @IMPORT_MODULES = (\n        sub {\n            my ( $bundles, $args ) = @_;\n            return (\n                qw( strict warnings ),\n                feature =\u003e [qw( :5.20 )],\n            );\n        },\n    );\n\n    # strict, warnings, and 5.20 features will be imported\n\nPlain strings are module names. Array references are arguments to import.\n\nB\u003cNOTE:\u003e Subrefs cannot return modules with C\u003cE\u003clt\u003e\u003e or C\u003cE\u003cgt\u003e\u003e to control\nordering. Subrefs are run after the order has already been determined, while\nthe imports are being executed. Subrefs can assume that imports before them\nhave already been completed.\n\n=head2 Subref Arguments\n\nSub references get an arrayref of bundles being requested, and a hashref of\nextra arguments. Arguments from the calling side start with a '-'. Arguments\nfrom Import::Base do not. Possible arguments are:\n\n    package         - The package we are exporting to\n    -exclude        - The exclusions, see L\u003c/\"-exclude\"\u003e.\n\nUsing C\u003cpackage\u003e, a subref could check or alter C\u003c@ISA\u003e, work with the object's\nmetaclass (if you're using one), or export additional symbols not set up for\nexport.\n\nHere's an example for applying a role (L\u003cMoo::Role\u003e, L\u003cRole::Tiny\u003e,\nL\u003cMoose::Role\u003e, and anything that uses C\u003cwith\u003e) when importing a bundle:\n\n    package My::Base;\n    use base 'Import::Base';\n    our %IMPORT_BUNDLES = (\n        'Plugin' =\u003e [\n            'Moo',\n            # Plugins require the \"My::Plugin\" role\n            sub {\n                my ( $bundles, $args ) = @_;\n                $args-\u003e{package}-\u003ecan( 'with' )-\u003e( 'My::Plugin' );\n                return;\n            },\n        ],\n    );\n\n    package My::Custom::Plugin;\n    use My::Base 'Plugin';\n\nB\u003cNOTE:\u003e This sub is still being called during the compile phase. If you need your\nrole to be applied later, if you get errors when trying to apply it at compile time,\nuse L\u003cthe import_bundle method|/import_bundle\u003e, below.\n\n=head2 Custom Arguments\n\nWhen using L\u003c/\"Subref Callbacks\"\u003e, you can add additional arguments to the\nC\u003cuse\u003e line. The arguments list starts after the first key that starts with a\n'-'. To avoid conflicting with any future Import::Base feature, prefix all your\ncustom arguments with '--'.\n\n    use My::Base -exclude =\u003e [qw( strict )], --custom =\u003e \"arguments\";\n    # Subrefs will get $args{--custom} set to \"arguments\"\n\n=head2 Dynamic API\n\nInstead of providing C\u003c@IMPORT_MODULES\u003e and C\u003c%IMPORT_BUNDLES\u003e, you can override the\nC\u003cmodules()\u003e method to do anything you want.\n\n    package My::Bundles;\n    use base 'My::Base';\n\n    sub modules {\n        my ( $class, $bundles, $args ) = @_;\n\n        # Modules that will always be included\n        my @modules = (\n            experimental =\u003e [qw( signatures )],\n        );\n\n        # Named bundles to include\n        my %bundles = (\n            Class =\u003e [qw( Moose MooseX::Types )],\n            Role =\u003e [qw( Moose::Role MooseX::Types )],\n            Test =\u003e [qw( Test::More Test::Deep )],\n        );\n\n        # Go to our parent class first\n        return $class-\u003eSUPER::modules( $bundles, $args ),\n            # Then the always included modules\n            @modules,\n            # Then the bundles we asked for\n            map { @{ $bundles{ $_ } } } grep { exists $bundles{ $_ } } @$bundles;\n    }\n\nUsing the above boilerplate will ensure that you start with all the basic functionality.\n\nOne advantage the dynamic API has is the ability to remove modules from superclasses, or\ncompletely control the order that modules are imported, even from superclasses.\n\n=head2 Exporting symbols from the base class\n\nImport::Base inherits from Exporter and allows for EXPORT_OK usage.\n\n    package My::Base;\n    use base 'Import::Base';\n\n    our @EXPORT_OK = qw($joy);\n\n    our @IMPORT_MODULES = (\n        'strict',\n        'warnings',\n        feature =\u003e [qw( :5.10 )],\n        'My::Base' =\u003e [qw( $joy )],\n    );\n\n    our $joy = \"Is everywhere\";\n\n    package main;\n\n    use My::Base;\n\n    say $joy;\n\nNotice how the base class 'My::Base' is in it's own IMPORT_MODULES definition.\n\n=head1 METHODS\n\n=head2 modules( $bundles, $args )\n\nPrepare the list of modules to import. $bundles is an array ref of bundles, if any.\n$args is a hash ref of generic arguments, if any.\n\nReturns a list of MODULE =\u003e [ import() args ]. MODULE may appear multiple times.\n\n=head2 import_bundle( @bundles, @args )\n\nImport a bundle at runtime. This method takes the exact same arguments as in\nthe C\u003cuse My::Base ...\u003e compile-time API, but allows it to happen at runtime,\nso that all of the current package's subs have been made available, and all\nC\u003cBEGIN\u003e blocks have been executed.\n\nThis is useful when using bundles to apply roles that have dependencies or\nother esoteric use-cases. It is not necessary for most things.\n\n=head1 DOCUMENTATION BOILERPLATE\n\nHere is an example for documenting your own base modules\n\n    =head1 SYNOPSIS\n\n        package MyModule;\n        use My::Base;\n\n        use My::Base 'Class';\n        use My::Base 'Role';\n        use My::Base 'Test';\n\n    =head1 DESCRIPTION\n\n    This is the base module that all {{PROJECT}} files should use.\n\n    This module always imports the following into your namespace:\n\n    =over\n\n    =item L\u003cstrict\u003e\n\n    =item L\u003cwarnings\u003e\n\n    =item L\u003cfeature\u003e\n\n    Currently the 5.20 feature bundle\n\n    =item L\u003cexperimental\u003e 'signatures' 'postderef'\n\n    We are using the 5.20 experimental signatures and postfix deref syntax.\n\n    =back\n\n    =head1 BUNDLES\n\n    The following bundles are available. You may import one or more of these by name.\n\n    =head2 Class\n\n    The class bundle makes your package into a class and includes:\n\n    =over 4\n\n    =item L\u003cMoo::Lax\u003e\n\n    =item L\u003cTypes::Standard\u003e ':all'\n\n    =back\n\n    =head2 Role\n\n    The role bundle makes your package into a role and includes:\n\n    =over 4\n\n    =item L\u003cMoo::Role::Lax\u003e\n\n    =item L\u003cTypes::Standard\u003e ':all'\n\n    =back\n\n    =head2 Test\n\n    The test bundle includes:\n\n    =over 4\n\n    =item L\u003cTest::More\u003e\n\n    =item L\u003cTest::Deep\u003e\n\n    =item L\u003cTest::Differences\u003e\n\n    =item L\u003cTest::Exception\u003e\n\n    =back\n\n    =head1 SEE ALSO\n\n    =over\n\n    =item L\u003cImport::Base\u003e\n\n    =back\n\n=head1 BEST PRACTICES\n\n=head2 One Per Project\n\nEvery project of at least medium size should have its own base module.\nConsolidating a bunch of common base modules into a single distribution and\nreleasing to CPAN may sound like a good idea, but it opens you up to\ndifficult-to-diagnose problems.\n\nIf many projects all depend on the same base, any change to the central base\nmodule could potentially break one of the consuming modules. In a single,\nwell-tested project, it is easy to track down and address issues due to changes\nin the base module. If the base module is released to CPAN, breakage may not\nappear until someone tries to install a module that depends on your base.\n\nVersion incompatibility, where project Foo depends on version 1 of the base,\nwhile project Bar depends on version 2, will create very frustrating situations\nfor your users.\n\nHaving to track down another project to figure out what modules are active in\nthe current package is a lot of work, creating frustration for contributing\nauthors.\n\n=head1 KNOWN ISSUES\n\n=over 4\n\n=item Moo::Role does not work if base module shares the same file as role package\n\nWhen trying to import L\u003cMoo::Role\u003e using Import::Base, the role will not\nbe applied if it shares the same file as the Import::Base module. For\nsafety and sanity, you should keep your Import::Base module separate\nfrom classes and roles.\n\n=item Dancer plugins do not work when applied with Import::Base\n\nDancer plugins check, at compile time, to see if they can be imported into\nthe consuming class by looking for the Dancer DSL. Because Import::Base uses\nModule::Runtime to load the class, Dancer::Plugin thinks Module::Runtime is\nthe calling class, sees there is no DSL to register itself with, and bails.\n\nSee L\u003chttps://github.com/PerlDancer/Dancer2/pull/1136\u003e for more information.\n\n=back\n\n=head1 SEE ALSO\n\n=over\n\n=item L\u003cImport::Into|Import::Into\u003e\n\nThe module that provides the functionality to create this module. If Import::Base\ndoesn't do what you want, look at Import::Into to build your own.\n\n=item L\u003cImporter|Importer\u003e\n\nThis module wraps the C\u003cimport\u003e method of other modules, allowing you to rename\nthe symbols you import. If you need to change a name, use this module together\nwith Import::Base.\n\n=item L\u003cperl5|perl5\u003e\n\nThis module is very similar, and has a bunch of built-in bundles and features for\nquickly importing Perl feature sets. It is also flexible, allowing you to specify\nyour own module bundles.\n\n=item L\u003cToolSet|ToolSet\u003e\n\nToolSet is very similar. Its API just uses package methods instead of package\nvariables. It also allows for exporting symbols directly from the bundle.\n\n=item L\u003csanity|sanity\u003e\n\nSanity is more consise, but not as flexible. If you don't need the wild\nflexibility of Import::Base or the above solutions, take a look.\n\n=item L\u003cToolkit|Toolkit\u003e\n\nThis one requires configuration files in a home directory, so is not shippable.\n\n=item L\u003crig|rig\u003e\n\nThis one also requires configuration files in a home directory, so is not shippable.\n\n=back\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpreaction%2Fimport-base","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpreaction%2Fimport-base","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpreaction%2Fimport-base/lists"}