Diab Jerius

NAME

pkg - transparently use packages and inner packages

SYNOPSIS

  # standard operations
  # works on either inner or normal packages
  # -------------------------------       ----------------
  use pkg 'A';                       #=>  use A;
  use pkg 'A', 'a', 'b';             #=>  use A 'a', 'b';
  use pkg 'A', [];                   #=>  use A ();

  # extra operations

  # default alias for a class package
  use pkg -alias => 'A::B::C';
  C->new(...); #equivalent to A::B::C->new();

  # specific alias for a class package
  use pkg 'A::B::C' => -as => 'ABC';
  ABC->new( ); # equivalent to A:B::C->new;

  # multiple packages
  use pkg [ 'A::B::C' => -as => 'ABC'],
          [ 'A::B'    => -as => 'AB' ];

  # operate on A and its inner packages
  use pkg 'A', '-inner';

  # operate only on the inner packages of A
  use pkg 'A', '-only_inner';

  # operate on A and its inner packages, excluding anything below A::B
  use pkg 'A', -inner, -exclude => qr/^A::B::/;

DESCRIPTION

pkg extends the standard "use" statement to more transparently handle inner packages, additionally incorporating some of the functionality of the aliased and use pragmata with a few extra twists.

An inner package is one which does not have the same name as the (fully qualified) module in which it is defined. For example, if A.pm contains

  package A;

  sub a { ... }

  package A::B;

  sub ab { ... }

  package A::C;

  sub ac { ... }

  1;

packages A::B and A::C are inner packages. The use statement (as well as most pragmata dealing with modules) does not handle inner packages. Some, such as parent, do, but require the user (via the -norequire option) to know if the package is inner or not.

For example, after loading the above module:

  use A;

You could simply call

  A::a();
  A::B::ab();
  A::C::ac();

But, what if package A::B exported ab? Its import routine is not automatically called when A is loaded. If you try to do this

  use A::B 'ab';

you'll get an error from Perl as it tries to search for a file named e.g., A/B.pm. It doesn't check to see if the A::B package has been loaded.

Instead, you'd need to do this:

  A::B->import( 'ab' );
  ab();

Or, using pkg:

  use pkg [ 'A' ], [ 'A::B' => qw[ ab ] ];

Simple Usage

In its simplest form, pkg accepts a list of a package name (as a string) and its imports.

  use pkg 'A::B', qw( funca funcb );

This loads the package A::B (if necessary) and imports the functions funca and funcb. Note that if A::B is an inner package, the module (file) which contains it must be loaded prior to this e.g.

  # either of these is sufficient
  use A;
  use pkg 'A';

This needs to be done only once (not every time an inner package is used). Of course it can be combined with loading A::B:

  use pkg [ 'A' ], [ 'A::B' => qw( funca funcb ) ];

Controlling imports

There is a subtlety in how the standard use statement handles empty or non-existent import lists:

   use A;           # call A->import();
   use A 'a', 'b'   # call A->import( 'a', 'b' );
   use A ();        # do *not* call A->import;

This mechanism isn't available to pkg as it cannot tell the difference between:

   use pkg 'A';
   use pkg 'A', ();

Instead, use [] instead of ():

   use pkg 'A', [];

What if you need to pass a [] to A->import()? Use the -import package option:

  use pkg 'A', -import => [];        #=> use A [];
  use pkg 'A', -import => '-import'; #=> use A '-import';

-import instructs pkg that all remaining arguments should be passed to the package's import routine.

Note that the following are equivalent

  use A (), 'a';
  use pkg 'A', [], 'a';

and result in

  A->import( 'a' );

Multiple packages

Multiple packages may be operated on by passing each package's specifications as separate array references:

  use pkg ['A'], ['A::B', qw( funca funcb ) ];

OPTIONS

pkg accepts options to modify its behavior. "Global" options (which affect more than one package) can appear in multiple places if more than one package is manipulated. Package specific options always appear directly after the package name and apply only to that package.

If there's only one package, the syntax is simple. Global options occur before the package name.

  use pkg -norequire => 'My::Package' -as => 'MyP';

-norequire is a global option, and -as is a package option.

If more than one package is specified, global options may occur both outside of the package specifications as well as inside of them. For example,

  use pkg
    -alias =>
    [ 'My::FirstClass' ],
    [ -noalias => 'My::SecondClass' ]
    [ 'My::ThirdClass' => -as => 'ThirdClassIsBetterThanFirst' ]
    -noalias =>
    [ 'My::Library1' ],
    [ 'My::Library2' ],
    [ 'My::Library3' ],
    ;

The options appearing outside of the package specifications affect all packages which follow. The options inside a specification affect that package only. As shown, some options may be negated, and package options may override global ones.

Global Options

-alias
-noalias

Provide (or don't provide) shortened names for class names. These are simply the last component of the original name.

The idea is borrowed from the aliased pragma; pkg constructs and exports a subroutine with the shortened name which returns the fully qualified name.

For example,

  use pkg -alias => 'A::Long:Class';

  # these are equivalent
  A::Long::Class->new();
  Class->new();

If multiple classes are loaded, no checks are performed to ensure that the shortened names are unique. Use the -as package option to specify specific names.

-strip

Created aliases by removing a prefix from the succeeding class names. The prefix may be specified in one of two ways:

-strip string

Remove a leading string from the class names. All component separators (::) are also removed. For example,

  -strip => 'A::C', 'A::C::E::F::G'

results in an alias of EFG.

-strip { pfx => string, sep => string }

Remove prefix from class names, and replace the class component separators (::) with the specified string. After prefix removal, a leading :: sequence is removed.

-require
-norequire

Try to load (or don't try to load) the packages with Class::Load::load_class. If you know that the package is an inner package and the file containing it has already been loaded, specifying -norequire can speed things up by not loading Class::Load.

By default packages are loaded (i.e. -require).

Package Options

-as => string

Create an alias named string for the package. The aliased name must be a legal subroutine name.

For example,

  use pkg 'A::Long:Class' => -as => 'ALC';

  # these are equivalent
  A::Long::Class->new();
  ALC->new();
-import

There's always a chance that a package's import list may be confused with pkg package options (perhaps it also has a -as option). To avoid this, a package's import list may be preceded with the -import option, which indicates to pkg that all of the following arguments are to be passed as is to the package's import routine.

  # these are equivalent
  use A ( '-as', 'func1', 'func2' );
  use pkg 'A' => -import => ( '-as', 'func1', 'func2' );
-require
-norequire

This has the same functionality as the similarly named global options, but as a package option may be placed after the package name for aesthetics.

-inner

In addition to the package, process any of its currently loaded inner packages. Inner packages are discovered via Devel::InnerPackage, and must fall within the "hierarchy" of the package. For example, given a module with the following contents:

  package A;
  sub a {}

  package A::B;
  sub ab {}

  package B;
  sub b {}

A::B is an inner package of A, but B is not. Inner packages must have defined symbols, otherwise they will not be identified.

-only_inner

Similar to -inner, but only the inner packages are processed, not the package itself.

This does not affect whether the package is loaded; this is controlled by the -require option.

-include specification

Check the package name against the specification using the smart match operator (~~) and ignore it if it does not match. If -inner or -only_inner are specified, inner packages are also checked.

This does not affect whether the package is loaded; this is controlled by the -require option.

This is most useful when either -inner or -only_inner is specified.

-exclude specification

Check the package name against the specification using the smart match operator (~~) and ignore it if it matches. The -exclude match is processed after -include if both are specified. If -inner or -only_inner are specified, inner packages are also checked.

This does not affect whether the package is loaded; this is controlled by the -require option.

This is most useful when either -inner or -only_inner is specified.

-version => version

Specify the minimum acceptable version of the package.

DIAGNOSTICS

global option '%s': unknown option

The specified option wasn't recognized as a global option.

package option '%s': unknown option

The specified option wasn't recognized as a package option.

option '%s': cannot be negated

An illegal negation of the specified option was specified.

option '%s': not enough values

The specified option required more values than was specified.

can't use option "%s" when looping over inner packages

The specifed option cannot be used in conjunction with -inner or -only_inner.

-strip: no prefix specified

The -strip option requires an argument specifying the prefix to remove.

internal error

Something really bad happened.

IMPLEMENTATION

pkg does very little on its own. It uses the following modules:

Class::Load

Class::Load::load_class is used to load the package. It also takes care of checking package versions.

Import::Into

This is used to call a package's import routine

aliased

This provided the inspiration for the aliasing implementation.

Devel::InnerPackages

Discover a package's inner self.

DEPENDENCIES

Class::Load, Import::Into, Devel::InnerPackages, Perl 5.10.1.

INCOMPATIBILITIES

None reported.

BUGS AND LIMITATIONS

pkg is focussed specifically on dealing with packages and is not intended as a general purpose replacement for the standard use statement. In particular it does not know how to deal with other pragmata, e.g.,

  use pkg strict;

will probably not do anything useful and will most probably advance the heat death of the universe.

Please report any bugs or feature requests to bug-pkg@rt.cpan.org, or through the web interface at http://rt.cpan.org/Public/Dist/Display.html?Name=pkg.

SEE ALSO

aliased, namespace, as, use.

VERSION

Version 0.01

LICENSE AND COPYRIGHT

Copyright (c) 2013 Diab Jerius

pkg is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

AUTHOR

Diab Jerius <djerius@cpan.org>




Hosting generously
sponsored by Bytemark