The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Enbld::Target::Attribute;

use strict;
use warnings;

use Carp;

use Module::Load;
use Module::Load::Conditional qw/can_load/;

sub new {
    my ( $class, $name, $param ) = @_;

    if ( ! $name ) {
        require Enbld::Exception;
        croak( Enbld::Exception->new( "'$class' requires name" ) );
    }

    my $module = "Enbld::Target::Attribute::$name";

    if ( can_load( modules => { $module => undef } ) ) {
        load $module;

        my $self = {
            attributes      =>  undef,
            value           =>  undef,
            callback        =>  undef,
            is_evaluated    =>  undef,
        };

        bless $self, $module;

        $self->initialize( $param );

        return $self;
    }

    require Enbld::Exception;
    croak( Enbld::Exception->new( "Attribute '$name' is invalid name" ) );
}


sub initialize {
    my ( $self, $param ) = @_;

    if ( ref( $param ) eq 'CODE' ) {
        $self->{callback} = $param;
        return $self;
    }

    $self->{value} = $param;
    $self->{is_evaluated}++;
    return $self;
}

sub validate {
    my ( $self, $string ) = @_;

    if ( ! $string ) {
        my $type = ref( $self );
        $type =~ s/.*:://;
        require Enbld::Exception;
        croak( Enbld::Exception->new( "Attribute '$type' is empty string" ));
    }

    if ( ref( $string ) ) {
        my $type = ref( $self );
        $type =~ s/.*:://;

        my $err = "Attribute '$type' isn't scalar value";
        require Enbld::Exception;
        croak( Enbld::Exception->new( $err, $string ));
    }

    return $string;
}

sub evaluate {
    my $self = shift;

    return $self->{value} if $self->{is_evaluated};
    
    $self->{value} = $self->{callback}->( $self->{attributes} );
    $self->{is_evaluated}++;

    return $self->{value};
}

sub to_value {
    my $self = shift;

    my $value = $self->evaluate;

    return '' if ( ( ! $value ) && $self->is_omitable );

    $self->validate( $value );

    return $value;
}

sub is_omitable {
    return; # if attribute is not omitable, override this method in sub class.
}

sub link_to_collector {
    my ( $self, $collector ) = @_;

    $self->{attributes} = $collector;
}

1;