The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Method::Traits - Apply traits to your methods

VERSION

version 0.03

SYNOPSIS

    package Person;
    use strict;
    use warnings;

    use Method::Traits qw[ MyAccessor::Trait::Provider ];

    use parent 'UNIVERSAL::Object';

    our %HAS = (
        fname => sub { "" },
        lname => sub { "" },
    );

    sub first_name : Accessor('ro', 'fname');
    sub last_name  : Accessor('rw', 'lname');

    package MyAccessor::Trait::Provider;
    use strict;
    use warnings;

    use Method::Traits ':for_providers';

    sub Accessor : OverwritesMethod {
        my ($meta, $method_name, $type, $slot_name) = @_;

        $meta->add_method( $method_name => sub {
            die 'ro accessor' if $_[1];
            $_[0]->{$slot_name};
        })
            if $type eq 'ro';

        $meta->add_method( $method_name => sub {
            $_[0]->{$slot_name} = $_[1] if $_[1];
            $_[0]->{$slot_name};
        })
            if $type eq 'rw';
    }

DESCRIPTION

Traits are subroutines that are run at compile time to modify the behavior of a method. This can be something as drastic as replacing the method body, or something as unintrusive as simply tagging the method with metadata.

TRAITS

A trait is simply a callback which is associated with a given subroutine and fired during compile time.

How are traits registered?

Traits are registered via a mapping of trait providers, which are just packages containing trait subroutines, and the class in which you intend to apply the traits.

This is done by passing in the provider package name when using the Method::Traits package, like so:

    package My::Class;
    use Method::Traits 'My::Trait::Provider';

This will make available all the traits in My::Trait::Provider for use inside My::Class.

How are traits associated?

Traits are associated to a subroutine using the "attribute" feature of Perl. When the "attribute" mechanism is triggered for a given method, we extract the name of the attribute and then attempt to find a trait of that name in the associated providers.

This means that in the following code:

    package My::Class;
    use Method::Traits 'My::Trait::Provider';

    sub foo : SomeTrait { ... }

We will encounter the foo method and see that it has the SomeTrait "attribute". We will then look to see if there is a SomeTrait trait available in the My::Trait::Provider provider, and if found, will call that trait.

How are traits called?

The traits are called immediately when the "attribute" mechanism is triggered. The trait callbacks receieve at least two arguments, the first being a MOP::Class instance representing the subroutine's package, the next being the name of that subroutine, and then, if there are any arguments passed to the trait, they are also passed along.

UNDER CONSTRUCTION

This module is still heavily under construction and there is a high likielihood that the details will change, bear that in mind if you choose to use it.

METHODS

These are all class methods.

add_trait_providers_for( $meta, @providers )

This will register the given @providers with the package pointed to by $meta, which is a MOP::Class instance.

get_trait_providers_for( $meta )

This will return any @providers registered with the package pointed to by $meta, which is a MOP::Class instance.

add_traits_for( $method, @traits )

This will associate the given @traits with the method pointed to by the $method, which is a MOP::Method instance.

get_traits_for( $method )

This will return any @traits associated with the method pointed to by the $method, which is a MOP::Method instance.

PERL VERSION COMPATIBILITY

For the moment I am going to require 5.14.5 because of the following quote by Zefram in the Sub::WhenBodied documentation:

  Prior to Perl 5.15.4, attribute handlers are executed before the body
  is attached, so see it in that intermediate state. (From Perl 5.15.4
  onwards, attribute handlers are executed after the body is attached.)
  It is otherwise unusual to see the subroutine in that intermediate
  state.

I am also using the ${^GLOBAL_PHASE} variable, which was introduced in 5.14.

It likely is possible using Devel::GlobalPhase and Sub::WhenBodied to actually implment this all for pre-5.14 perls, but for now I am not going to worry about that.

AUTHOR

Stevan Little <stevan@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2017 by Stevan Little.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.