# NAME

MooX::TaggedAttributes - Add a tag with an arbitrary value to a an attribute

# VERSION

version 0.16

# SYNOPSIS

    # 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};

# DESCRIPTION

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.

## Tagging Attributes

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;

## Accessing tags

Classes and objects are provided a **\_tags** method which returns a
[MooX::TaggedAttributes::Cache](https://metacpan.org/pod/MooX%3A%3ATaggedAttributes%3A%3ACache) 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`

    bless({ t1 => { a => 2 }, t2 => { b => "foo" } }, "MooX::TaggedAttributes::Cache")

and `C->new->_tags`

    bless({ t1 => { a => 2 }, t2 => { b => "foo" } }, "MooX::TaggedAttributes::Cache")

are identical.

# ADVANCED USE

## Experimental!

### Additional tag handlers

`MooX::TaggedAttributes` works in part by wrapping ["has" in Moo](https://metacpan.org/pod/Moo#has) 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](https://metacpan.org/pod/MooX%3A%3ATaggedAttributes):

    use MooX::TaggedAttributes -handler => $handler, -tags => ...;

`$handler` is a subroutine reference which will be called as

    $coderef = $handler->($class);

Its return value must be a coderef suitable for passing as an 'around'
modifier for 'has' to [Moo::\_Utils::\_install\_modifier](https://metacpan.org/pod/Moo%3A%3A_Utils%3A%3A_install_modifier) to wrap
`has`, e.g.

    Moo::_Utils::_install_modifier( $target, around has => $coderef );

### Automatically propagating tagging abilities

As mentioned in the main documentation, in order for a package to gain
the ability to tag attributes, it must load a tag role using the
`use` statement, not the `with` statement.

An (experimental) alternative is to pass the `-propagate` option when
defining a tag role, e.g.

    # define a Tag Role
    package T1;
    use Moo::Role;
    
    use MooX::TaggedAttributes -tags => [qw( t1 t2 )], -role;
    1;

Every class or role which comsuming this role (using the standard
`with` approach) will recieve the ability to tag attributes.

This results in different behavior than the previous (hopefully 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.

# BUGS, LIMITATIONS, TRAPS FOR THE UNWARY

## Changes to an object after instantiation are not tracked.

If a role with tagged attributes is applied to an object, the
tags for those attributes are not visible.

## An **import** routine is installed into the tag role's namespace

When a tag role imports `MooX::TaggedAttributes` via

    package My::Role;
    use MooX::TaggedAttributes;

two things happen to it:

1. a role is applied to it which adds the  methods `_tags` and `_tag_list`.
2. 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,

        package My::Module;
        use My::Role;  # <---- My::Role's import routine is called here

    This `import` does two things. In the above example, it 

    1. applies the role `My::Role` to `My::Module`;
    2. modifies the [Moo](https://metacpan.org/pod/Moo) `has` attribute creator so that calls to `has`
    in `My::Module` track attributes with tags.

# SUPPORT

## Bugs

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

Source is available at

    https://gitlab.com/djerius/moox-taggedattributes

and may be cloned from

    https://gitlab.com/djerius/moox-taggedattributes.git

# INTERNAL ROUTINES

These routines are **not** meant for public consumption, but are
documented here for posterity.

## role\_import

This import method is installed into tag roles (i.e. roles which
import [MooX::TaggedAttributes](https://metacpan.org/pod/MooX%3A%3ATaggedAttributes)).  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

    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"](#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` must contain either `tags`, an arrayref of tags, or `class`, the name of a class
which as already been registered with [MooX::TaggedAttributes](https://metacpan.org/pod/MooX%3A%3ATaggedAttributes).

## install\_tag\_handler

    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](https://metacpan.org/pod/Class%3A%3AMethod%3A%3AModifiers#around).

## \_install\_role\_modifier

Our own purloined version of code to register modifiers for roles. See
[Role::Tiny](https://metacpan.org/pod/Role%3A%3ATiny)'s `_gen_subs` or [Moo::Role](https://metacpan.org/pod/Moo%3A%3ARole)'s similarly named routine.
Unfortunately, there's no way of easily calling that code

## make\_tag\_handler

    $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.

# AUTHOR

Diab Jerius <djerius@cpan.org>

# COPYRIGHT AND LICENSE

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
