NAME
    MooX::AttributeFilter - Implements 'filter' option for Moo-class
    attributes

VERSION
    version 0.001000

SYNOPSIS
        package My::Class {
            use Moo;
            use MooX::AttributeFilter;
        
            has field => (
                is     => 'rw',
                filter => 'filterField',
            );
        
            has lazyField => (
                is      => 'rw',
                lazy    => 1,
                builder => sub { [1, 2, 3 ] },
                filter  => 1,
            );
        
            has incremental => (
                is => 'rw',
                filter => sub {
                    my $this = shift;
                    my ($val, $oldVal) = @_;
                    if ( @_ > 1 && defined $oldVal ) {
                        die "incremental attribute value may only increase"
                            unless $val > $oldVal;
                    }
                    return $_[0];
                }
            );
        
            sub filterField {
                my $this = shift;
                return "filtered($_[0])";
            }
        
            sub _filter_lazyField {
                my $this = shift;
                my @a = @{$_[0]};
                push @a, -1;
                return \@a;
            }
        }
    
        my $obj = My::Class->new( field => "initial" );
        ($obj->field eq "filtered(initial)")  # True!
        $obj->lazyField;                      # [ 1, 2, 3, -1 ]
        $obj->field( "value" );               # "filtered(value)"
        $obj->incremental( -1 );              # -1
        $obj->incremental( 10 );              # 10
        $obj->incremental( 9 );               # dies...
    
        $obj = My::Class->new( incremental => 1 ); # incremental is set to 1
        $obj->incremental( 0 );                    # dies too.

DESCRIPTION
    The idea behind this extension is to overcome the biggest deficiency of
    coercion: its ignorance about the object it is acting for. Triggers are
    executed as methods but they don't receive the previous value and
    they're called after attribute is set.

    A filter is a method which is called right before attribute value is
    about to be set and receives 1 or two arguments of which the first one
    is the new attribute value; the second one is the old value. Number of
    arguments passed depends on when the filter get called: on the
    construction stage it is one, and it is two when set manually. Note that
    in the latter case if it's the first time the attribute is set the old
    value would be undefined.

    It is also worth mentioning that filter is called always when attribute
    is set, including initialization from constructor arguments or lazy
    builders. See the SYNOPSIS. In both cases filter gets called with a
    single argument.

    I.e.:

        package LazyOne {
            use Moo;
            use MooX::AttributeFilter;
        
            has lazyField => (
                is => 'rw',
                lazy => 1,
                default => "value",
                filter => sub {
                    my $this = shift;
                    say "Arguments: ", scalar(@_);
                    return $_[0];
                },
            );
        }
    
        my $obj = LazyOne->new;
        $obj->lazyField;        # Arguments: 1
        $obj->lazyField("foo"); # Arguments: 2
    
        $obj = LazyOne->new( lazyField => "bar" );  # Arguments: 1
        $obj->lazyField( "foobar" );                # Arguments: 2

    A filter method must always return a (possibly changed) value to be
    stored in the attribute.

    Filter called *before* anything else on the attribute. Its return value
    is then subject for passing through "isa" and "coerce".

  Use cases
    Filters are of the most use when attribute value (or allowed values)
    depends on other attributes of its object. The dependency could be hard
    (or "isa"-like) – i.e. when an exception must be thrown if value doesn't
    pass validation. Or it could be soft when by storing a vlue code
    *suggest* what it would like to see in the attribute but the resulting
    value might be changed depending on the current environment. For
    example:

        package ChDir {
            use File::Spec;
            use Moo;
            extends qw<Project::BaseClass>;
            use MooX::AttributeFilter;
        
            has curDir => (
                is => 'rw',
                filter => 'chDir',
            );
        
            sub chDir {
                my $this = shift;
                my ( $subdir ) = @_;
            
                return File::Spec->catdir(
                    $this->testMode ? $this->baseTestDir : $this->baseDir,
                    $subdir
                );
            }
        }

CAVEATS
    * This module doesn't inflate into Moose.

    * The code relies on very low-level functionality of Method::Generate
    family of modules. For this reason it may become incompatible with a
    future versions of the modules.

ACKNOWLEDGEMENT
    This module is a result of rejection to include filtering functionality
    into the Moo core. Since the reasoning behind the rejection was really
    convincing but the functionality is badly wanted I had no choices
    left... So, my great thanks to Graham Knopp <haarg@haarg.org> for his
    advises, sample code, and Moo itself, of course!

AUTHOR
    Vadim Belman <vrurg@cpan.org>

COPYRIGHT AND LICENSE
    This software is copyright (c) 2018 by Vadim Belman.

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

