NAME Module::Features - Define features for modules SPECIFICATION VERSION 0.1 VERSION This document describes version 0.1.0 of Module::Features (from Perl distribution Module-Features), released on 2021-02-21. DESCRIPTION This document specifies a way to define and declare features for modules. SPECIFICATION STATUS The series 0.1.x version is still unstable. GLOSSARY definer module Module in the namespace of "Module::Features::"*FeatureSetName* that contains "feature set specification". This module defines what each feature in the feature set means. A "user module" follows the specification and declares features. user module A regular Perl module that wants to define some features defined by "definer module". feature name A string, preferrably an identifier matching regex pattern /\A\w+\z/. feature value The value of a feature. feature specification A DefHash, containing the feature's summary, description, schema for value, and other things. feature set name A string following regular Perl namespace name, e.g. "JSON::Encoder" or "TextTable". feature set specification A collection of "feature name"s along with each feature's specification. SPECIFICATION Defining feature set Definer module defines feature set by putting it in %FEATURES_DEF package variable. Defining feature set does not require any module dependency. For example, in "Module::Features::TextTable": # a DefHash our %FEATURES_DEF = ( summary => 'Features of a text table generator', description => <<'_', This feature set defines features of a text table generator. By declaring these features, the module author makes it easier for module users to choose an appropriate module. _ features => { # each key is a feature name. each value is the feature's specification. align_cell_containing_color_codes => { # a regular DefHash with common properties like 'summary', # 'description', 'tags', etc. can also contain these properties: # 'schema', 'req' (whether the feature must be declared by user # module). summary => 'Whether the module can align cells that contain ANSI color codes', # schema => 'bool*', # Sah schema. if not specified, the default is 'bool*' }, align_cell_containing_multiple_lines => { summary => 'Whether the module can align cells that contain multiple lines of text', }, align_cell_containing_wide_characters => { summary => 'Whether the module can align cells that contain wide Unicode characters', }, speed => { summary => 'The speed of the module, according to the author', schema => ['int', in=>['slow', 'medium', 'fast']], }, }, ); Declaring features User module declares features that it supports (or does not support) via putting it in %FEATURES package variable. Declaring features does not require any module dependency, but a helper module can be written to help check that declared feature sets and features are known and the feature values conform to defined schemas. For example, in Text::Table::More: our %FEATURES = ( # each key is a feature set name. TextTable => { # each key is a feature name defined in the feature set. each value is # either a feature value, or a DeHash that contains the feature value # in the 'feature' property, and notes in 'summary', and other things. align_cell_containing_color_codes => 1, align_cell_containing_wide_characters => 1, align_cell_containing_multiple_lines => 1, speed => { value => 'slow', summary => "It's certainly slower than Text::Table::Tiny, etc; and it can still be made faster after some optimization", }, }, ); While in Text::Table::Sprintf: our %FEATURES = ( TextTable => { align_cell_containing_color_codes => 1, align_cell_containing_wide_characters => 1, align_cell_containing_multiple_lines => 1, speed => 'fast', }, ); and in Text::Table::Any: our %FEATURES = ( TextTable => { align_cell_containing_color_codes => {value => undef, summary => 'Depends on the backend used'}, align_cell_containing_wide_characters => {value => undef, summary => 'Depends on the backend used'}, align_cell_containing_multiple_lines => {value => undef, summary => 'Depends on the backend used'}, speed => {value => undef, summary => 'Depends on the backend used'}, }, ); Checking whether a module has a certain feature A "user module" user can check whether a user module has a certain feature simply by checking the user module's %FEATURES. For example, to check whether Text::Table::Sprintf supports aligning cells that contain multiple lines: if (do { my $val = $Text::Table::Sprintf::FEATURES{TextTable}{align_cell_containing_multiple_lines}; ref $val eq 'HASH' ? $val->{value} : $val }) { ... } A utility module can be written to help make this more convenient. Selecting modules by its feature Each module that one wants to select can be loaded and its %FEATURES read. To avoid loading lots of modules, the features declaration can also be put somewhere else if wanted, like database, or per-distribution shared files, or distribution metadata. Currently no specific recommendation is given. FAQ Why not roles? Role frameworks like Role::Tiny allow you to require a module to have certain subroutines, i.e. to follow some kind of interface. This can be used to achieve the same goal of defining and declaring features, by representing features as required subroutines and feature sets as roles. However, Module::Features wants declaring features to have negligible overhead, including no extra runtime dependency. HOMEPAGE Please visit the project's homepage at . SOURCE Source repository is at . BUGS Please report any bugs or feature requests on the bugtracker website When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. SEE ALSO DefHash Sah AUTHOR perlancar COPYRIGHT AND LICENSE This software is copyright (c) 2021 by perlancar@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.