NAME
Code::Style::Kit - build composable bulk exporters
VERSION
version 1.0.1
SYNOPSIS
To build a "part":
package My::Kit::Part;
use strict;
use warnings;
sub feature_trytiny_default { 1 }
sub feature_trytiny_export_list { 'Try::Tiny' }
1;
To build the kit:
package My::Kit;
use parent qw(Code::Style::Kit My::Kit::Part My::Kit::OtherPart);
1;
To use the kit:
package My::App;
use My::Kit;
# you now have Try::Tiny imported, plus whatever OtherPart did
DESCRIPTION
This package simplifies writing "code style kits". A kit (also known as a "policy") is a module that encapsulates the common pragmas and modules that every package in a project should use. For instance, it might be a good idea to always use strict
, enable method signatures, and use true
, but it's cumbersome to put that boilerplate in every single file in your project. Now you can do that with a single line of code.
Code::Style::Kit
is not to be use
d directly: you must write a package that inherits from it. Your package can (and probably should) also inherit from one or more "parts". See Code::Style::Kit::Parts
for information about the parts included in this distribution.
Please don't use this for libraries you intend to distribute on CPAN: you'd be forcing a bunch of dependencies on every user. These kits are intended for applications, or "internal" libraries that don't get released publicly.
Features
A kit provides a set of "features" (like "tags" in Exporter
or "groups" in Sub::Exporter
). Feature names must match ^\w+$
. Some features may be exported by default.
A simple example of a feature, from the synopsis:
sub feature_trytiny_default { 1 }
sub feature_trytiny_export_list { 'Try::Tiny' }
or, equivalently:
sub feature_trytiny_default { 1 }
sub feature_trytiny_export {
my ($self, $caller) = @_;
require Try::Tiny;
Try::Tiny->import::into($caller);
}
The feature_*_default
method says that this feature should always be exported (unless the user explicitly asks us not to). The feature_*_export_list
is a shortcut for the simple case of re-exporting one or more entire packages. Alternatively, the feature_*_export
sub provides full flexibility, when you need it.
Feature ordering
Sometimes you need features to be exported in a certain order:
package My::Kit;
use parent qw(Code::Style::Kit Code::Style::Kit::Parts::Common);
sub feature_class_export_list { 'Moo' }
sub feature_class_order { 200 }
sub feature_singleton_export {
require Role::Tiny;
Role::Tiny->apply_roles_to_package($_[1], 'MooX::Singleton');
}
sub feature_singleton_order { 210 }
If someone says either:
package My::Class;
use My::Kit 'class', 'singleton';
or:
package My::Class;
use My::Kit 'singleton', 'class';
then Moo
will be imported first, then MooX::Singleton
will be applied.
All features that don't have a feature_*_order
sub are assumed to have order 100.
Dependencies
Sometimes you want to make sure that a certain feature is exported whenever another one is.
sub feature_class_export_list { 'Moo' }
sub feature_singleton_export {
my ($self, $caller) = @_;
$self->also_export('class');
require Role::Tiny;
Role::Tiny->apply_roles_to_package($caller, 'MooX::Singleton');
}
Now:
package My::Class;
use My::Kit 'singleton';
will work. Notice that you don't have to worry whether the feature was defined via "export" or "export_list": it just works.
Also, a feature can be imported only once, so
package My::Class;
use My::Kit 'class', 'singleton';
will not create problems.
Optional dependencies
Maybe you'd like for another feature to be exported, but you're not sure if it's provided by the kit. This can happen when writing reusable parts.
sub feature_class_export {
my ($self, $caller) = @_;
require Moo; Moo->import::into($caller);
$self->maybe_also_export('types');
}
now, if the final kit provides a "types" feature, it will be exported whenever the "class" feature is requested.
Extending features
Different "parts" can provide the same feature. Their export functions will be invoked in method resolution order (usually, the order they appear in @ISA
).
So, having:
package My::Kit::Part;
sub feature_test_export_list { 'Test::Most' }
and:
package My::Kit::OtherPart;
sub feature_test_export_list { 'Test::Failure' }
this kit:
package My::Kit;
use parent qw(Code::Style::Kit My::Kit::Part My::Kit::OtherPart);
1;
will export Test::Most
first, then Test::Failure
, when used as use My::Kit 'test'
.
Defaults are also affected by this, and the last one wins: if My::Kit::OtherPart::feature_test_default
returned 1, the feature would be exported by default.
Mutually exclusive features
You may want to prevent two features from being exported at the same time:
sub feature_class_export {
my ($self, $caller) = @_;
croak "can't be a class and a role" if $self->is_feature_requested('role');
...
}
sub feature_role_export {
my ($self, $caller) = @_;
croak "can't be a class and a role" if $self->is_feature_requested('class');
...
}
Arguments to features
Sometimes you need to have a bit more information than just "import this feature". For example, Mojo::Base
needs a superclass name on its import list. In that case you can do:
sub feature_mojo_takes_arguments { 1 }
sub feature_mojo_export {
my ($self, $caller, @arguments) = @_;
require Mojo::Base;
Mojo::Base->import::into(
$caller,
@arguments ? @arguments : '-base',
);
}
and the user can do:
use My::Kit mojo => [ 'Some::Base::Class' ];
(the arrayref is needed to distinguish argument lists from feature names).
METHODS
import
use My::Kit;
use My::Kit 'feature_i_want', '-feature_i_dont_want';
When a package inheriting Code::Style::Kit
get use
d, this method:
collects all the features that the kit exports by default
adds the features listed in the arguments
removes the features listed in the arguments with a
-
in frontexports the resulting set of features
is_feature_requested
if ($self->is_feature_requested($name)) { ... }
Returns true if the named feature is being exported (either because it's exported by default and not removed, or because it was asked for explicitly).
also_export
$self->also_export($name);
$self->also_export($name, \@arguments);
Export the named feature to the caller (optionally with arguments). Dies if the feature is not provided by the kit.
maybe_also_export
$self->maybe_also_export($name);
$self->maybe_also_export($name, \@arguments);
Export the named feature to the caller, same as "also_export
", but if the feature is not provided by the kit, this method just returns.
AUTHOR
Gianni Ceccarelli <gianni.ceccarelli@broadbean.com>
COPYRIGHT AND LICENSE
This software is copyright (c) 2019 by BroadBean UK, a CareerBuilder Company.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.