MooX::TaggedAttributes - Add a tag with an arbitrary value to a an attribute
version 0.18
# define a Tag Role package T1; use Moo::Role; use MooX::TaggedAttributes -tags => [qw( t1 t2 )]; 1; # Apply a tag role directly to a class package C1; use Moo; use T1; has c1 => ( is => 'ro', t1 => 1 ); 1; # use a tag role in another Role package R1; use Moo::Role; use T1; has r1 => ( is => 'ro', t2 => 2 ); 1; # Use a tag role which consumes a tag role in a class package C2; use Moo; use R1; has c2 => ( is => 'ro', t2 => sub { } ); 1; # Use our tags use C1; use C2; use 5.01001; # get the value of the tag t1, applied to attribute a1 say C1->new->_tags->{t1}{a1}; # get the value of the tag t2, applied to attribute c2 say C2->new->_tags->{t2}{c2};
This module attaches a tag-value pair to an attribute in a Moo class or role, and provides a interface to query which attributes have which tags, and what the values are. It keeps track of tags for attributes through role composition as well as class inheritance.
To define a set of tags, create a special tag role:
package T1; use Moo::Role; use MooX::TaggedAttributes -tags => [ 't1' ]; has a1 => ( is => 'ro', t1 => 'foo' ); 1;
If there's only one tag, it can be passed directly without being wrapped in an array:
package T2; use Moo::Role; use MooX::TaggedAttributes -tags => 't2'; has a2 => ( is => 'ro', t2 => 'bar' ); 1;
A tag role is a standard Moo::Role with added machinery to track attribute tags. As shown, attributes may be tagged in the tag role as well as in modules which consume it.
Tag roles may be consumed just as ordinary roles, but in order for role consumers to have the ability to assign tags to attributes, they need to be consumed with the Perl use statement, not with the with statement.
Consuming with the with statement will propagate attributes with existing tags, but won't provide the ability to tag new attributes.
This is correct:
package R2; use Moo::Role; use T1; has r2 => ( is => 'ro', t1 => 'foo' ); 1; package R3; use Moo::Role; use R3; has r3 => ( is => 'ro', t1 => 'foo' ); 1;
The same goes for classes:
package C1; use Moo; use T1; has c1 => ( is => 'ro', t1 => 'foo' ); 1;
Combining tag roles is as simple as use'ing them in the new role:
package T12; use Moo::Role; use T1; use T2; 1; package C2; use Moo; use T12; has c2 => ( is => 'ro', t1 => 'foo', t2 => 'bar' ); 1;
Classes and objects are provided a _tags method which returns a MooX::TaggedAttributes::Cache object. For backwards compatibility, it can be dereferenced as a hash, providing a hash of hashes keyed off of the tags and attribute names. For example, for the following code:
package T; use Moo::Role; use MooX::TaggedAttributes -tags => [qw( t1 t2 )]; 1; package C; use Moo; use T; has a => ( is => 'ro', t1 => 2 ); has b => ( is => 'ro', t2 => 'foo' ); 1;
The tag structure returned by C->_tags
C->_tags
bless({ t1 => { a => 2 }, t2 => { b => "foo" } }, "MooX::TaggedAttributes::Cache")
and C->new->_tags
C->new->_tags
are identical.
MooX::TaggedAttributes works in part by wrapping "has" in Moo in logic which handles the association of tags with attributes. This wrapping is automatically applied when a module uses a tag role, and its mechanism may be used to apply an additional wrapper by passing the -handler option to MooX::TaggedAttributes:
MooX::TaggedAttributes
-handler
use MooX::TaggedAttributes -handler => $handler, -tags => ...;
$handler is a subroutine reference which will be called as
$handler
$coderef = $handler->($class);
Its return value must be a coderef suitable for passing as an 'around' modifier for 'has' to Moo::_Utils::_install_modifier to wrap has, e.g.
has
Moo::_Utils::_install_modifier( $target, around has => $coderef );
As mentioned previously, a package load a tag role using the use statement (not the with statement) to be able tag attributes.
use
with
An (experimental) alternative is to pass the -propagate option when defining a tag role, e.g.
-propagate
# define a Tag Role package T1; use Moo::Role; use MooX::TaggedAttributes -tags => [qw( t1 t2 )], -propagate; 1;
Classes or roles consuming this role via with will be able to tag attributes, and will pass that capability on to classes which consume them.
This results in different behavior than the previous (soon to be deprecated) mode. There, consuming a role using with does not convey tagging abilities to the consumer. That is done with the use command.
If a role with tagged attributes is applied to an object, the tags for those attributes are not visible.
When a tag role imports MooX::TaggedAttributes via
package My::Role; use MooX::TaggedAttributes;
two things happen to it:
a role is applied to it which adds the methods _tags and _tag_list.
_tags
_tag_list
An import() method is installed (e.g. in the above example, that becomes My::Role::import). This may cause conflicts if My::Role has an import method. (It's exceedingly rare that a role would have an import method.) This import method is used when the tag role is itself imported, e.g. in the above example,
import()
My::Role::import
My::Role
import
package My::Module; use My::Role; # <---- My::Role's import routine is called here
This import does two things. In the above example, it
applies the role My::Role to My::Module;
My::Module
modifies the Moo has attribute creator so that calls to has in My::Module track attributes with tags.
Please report any bugs or feature requests to bug-moox-taggedattributes@rt.cpan.org or through the web interface at: https://rt.cpan.org/Public/Dist/Display.html?Name=MooX-TaggedAttributes
Source is available at
https://gitlab.com/djerius/moox-taggedattributes
and may be cloned from
https://gitlab.com/djerius/moox-taggedattributes.git
These routines are not meant for public consumption, but are documented here for posterity.
This import method is installed into tag roles (i.e. roles which import MooX::TaggedAttributes). The result is that when a tag role is imported, via e.g.
package My::Module use My::TagRole;
The role will be applied to the importing module (e.g., My::Module), providing the _tags and _tag_list methods.
The Moo has routine in My::Module will be modified to track attributes with tags.
install_tags( $target, %opt );
This subroutine associates a list of tags with a class. The first time this is called on a class it also calls "install_tag_handler". For subsequent calls it appends the tags to the class' list of tags.
%opt may contain tag_handler which is a coderef for a tag handler.
%opt
tag_handler
%opt must contain either tags, an arrayref of tags, or class, the name of a class which as already been registered with MooX::TaggedAttributes.
tags
class
install_tag_handler( $class, $factory );
This installs a wrapper around the has routine in $class. $factory is called as $factory->($class) and should return a wrapper compatible with "around" in Class::Method::Modifiers.
$class
$factory
$factory->($class)
Our own purloined version of code to register modifiers for roles. See Role::Tiny's _gen_subs or Moo::Role's similarly named routine. Unfortunately, there's no way of easily calling that code
_gen_subs
$coderef = make_tag_handler( $target_class );
A tag handler factory returning a coderef which wraps the $target_class::_tag_list method to add the tags in $TAGSTORE{$target} to its return value.
$target_class::_tag_list
$TAGSTORE{$target}
Diab Jerius <djerius@cpan.org>
This software is Copyright (c) 2018 by Smithsonian Astrophysical Observatory.
This is free software, licensed under:
The GNU General Public License, Version 3, June 2007
To install MooX::TaggedAttributes, copy and paste the appropriate command in to your terminal.
cpanm
cpanm MooX::TaggedAttributes
CPAN shell
perl -MCPAN -e shell install MooX::TaggedAttributes
For more information on module installation, please visit the detailed CPAN module installation guide.