OOP-Perlish-Class

 view release on metacpan or  search on metacpan

lib/OOP/Perlish/Class.pm  view on Meta::CPAN

    ## $self, or do any of a dozen other things.
    ## 
    ## The key 'return' is considered magical and sacred; if you return in your tuple
    ## 'return => foo' the constructor will immediately, and before any other initialization
    ## completes, return the thing you said to return; usually a blessed reference to something;
    ## be it a singleton, multiton, another object, acme-time-bomb ala wiley coyote, etc.
    ##
    ## Your method will be passed a reference to the options passed to the constructor; and may
    ## (usually should) delete the magical key you are interested in, so that it is not
    ## considered an accessor later.
    ## 
    ## This could have been done via attributes, but then it suffers from all the annoyances of
    ## having to be seen prior to CHECK blocks running, yada yada... 
    ############################################################################################
    sub ____process_magic_arguments(@)
    {
        my ( $self, $opts ) = @_;

        my %magic = ();

        for( $self->_all_methods() ) {
            m/^_magic_constructor_arg_handler/ && do {
                my $method = $_;
                my ( $key, $value ) = $self->$method($opts);
                $magic{$key} = $value if( $key && $value );
            };
        }

        return %magic;
    }

    ############################################################################################
    ## verify that we have our required fields, even if they don't have real values but have
    ## defaults instead (a default AND required field would be odd, but is supported)
    ############################################################################################
    sub ____pre_validate_opts(@)
    {
        my ($self) = @_;

        my @required_fields = $self->____identify_required_fields();

        for(@required_fields) {
            confess("Missing required field $_")
              unless(
                      exists( $self->{____oop_perlish_class_opts}->{$_} )
                      || ( exists( $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$_} )
                           && $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$_}->default_is_set() )
                    );
        }
        return;
    }

    ############################################################################################
    ## obtain the names and references to every accessor in our inheritance
    ############################################################################################
    sub ____inherit_accessors(@)
    {
        my ($self) = @_;

        ### Protect overloaded accessors by identifying those in our top-level namespace
        ### This cascaded up through the inheritance tree
        my %top_accessors = ();
        if( scalar( keys %{ $self->____OOP_PERLISH_CLASS_ACCESSORS() } ) ) {
            # XXX: Hash slice assignment
            @top_accessors{ keys %{ $self->____OOP_PERLISH_CLASS_ACCESSORS() } } =
              ( (1) x ( ( scalar keys %{ $self->____OOP_PERLISH_CLASS_ACCESSORS() } ) ) );
        }

        ### Assimilate inherited accessor references
        #for my $parent_class ( @{ $self->{____CLASS_ISA} } ) {
        for my $parent_class ( $self->_all_isa() ) {
            if( $parent_class && bless( {}, $parent_class )->can('____OOP_PERLISH_CLASS_ACCESSORS') ) {
                while( my ( $k, $v ) = each %{ $parent_class->____OOP_PERLISH_CLASS_ACCESSORS() } ) {
                    $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$k} = $v unless( exists( $top_accessors{$k} ) );    #protect overloading
                }
            }
        }
        return;
    }

    ############################################################################################
    ## run constructors of every class we derive from, and assimilate their %{ $self } hash into
    ## our own.
    ############################################################################################
    ## FIXME: We only support deriving from blessed-hashref classes.
    ############################################################################################
    sub ____inherit_constructed_refs
    {
        my ($self) = @_;

        for my $parent_class ( @{ $self->{____CLASS_ISA} } ) {
            next if( $parent_class eq __PACKAGE__ );
            my $tclass = bless( {}, $parent_class );
            my $this;
            if( $tclass->isa(__PACKAGE__) ) {
                $this = $parent_class->new( _____oop_perlish_class__defer__required__fields__validation => 1 );
            }
            elsif( $tclass->can('new') ) {
                $this = $parent_class->new();
            }
            ### FIXME: cleanly handle non-hashref ancestors...
            if( $this && $this->isa('HASH') ) {
                while( my ( $key, $val ) = each %{$this} ) {
                    $self->{$key} = $val unless( exists( $self->{$key} ) );
                }
                if( exists( $this->{___fields} ) ) {
                    while( my ( $key, $val ) = each %{ $this->{___fields} } ) {
                        $self->$key( $val->{_Value} ) unless( exists( $self->{___fields}->{$key} ) );
                    }
                }
            }
        }
        return;
    }

    ############################################################################################
    ## figure out what fields are required for all derived ancestor classes and ourself.
    ############################################################################################
    sub ____identify_required_fields(@)
    {
        my ($self) = @_;

        my $class = ref($self) || $self;

        if( !defined( $self->{____oop_perlish_class_required_fields} ) ) {
            my %required_fields = ();

            ### Obtain REQUIRED_FIELDS static from derived class. Assign it via hashslice
            @required_fields{ @{ $class->____OOP_PERLISH_CLASS_REQUIRED_FIELDS() } } = @{ $class->____OOP_PERLISH_CLASS_REQUIRED_FIELDS() };

            while( my ( $name, $field ) = each %{ $self->____OOP_PERLISH_CLASS_ACCESSORS() } ) {
                $required_fields{$name} = $name if( $field->required() );
            }

            # FIXME: Does not cascade beyond @ISA, should traverse inheritance tree and ensure that all required fields are
            # provided for any hiararchy. ... does cascade via new, but only to ancesters who conform with us. unsure how to fix
            #for my $parent_class ( @{ $self->{____CLASS_ISA} } ) {
            for my $parent_class ( $self->_all_isa() ) {
                if( bless( {}, $parent_class )->can('____OOP_PERLISH_CLASS_REQUIRED_FIELDS') ) {
                    @required_fields{ @{ $parent_class->____OOP_PERLISH_CLASS_REQUIRED_FIELDS() } } =
                      @{ $parent_class->____OOP_PERLISH_CLASS_REQUIRED_FIELDS() };
                }
            }

            @{ $self->{____oop_perlish_class_required_fields} } = keys %required_fields;
        }
        return @{ $self->{____oop_perlish_class_required_fields} };
    }

    ############################################################################################
    ## setup required fields, using their accessors
    ############################################################################################
    sub ____initialize_required_fields(@)
    {
        my ($self) = @_;

        my @required_fields = $self->____identify_required_fields();

        for my $method (@required_fields) {
            $self->$method( $self->{____oop_perlish_class_opts}->{$method} )
              if( exists( $self->{____oop_perlish_class_opts}->{$method} ) && defined( $self->{____oop_perlish_class_opts}->{$method} ) );
            croak("Invalid required attribute for $method") unless( $self->$method() || $self->is_set($method) );
        }
        return;
    }

    ############################################################################################
    ## setup non-required-fields, using their accessors
    ############################################################################################
    sub ____initialize_non_required_fields(@)
    {
        my ($self) = @_;

        ### XXX: Hash slice assignment
        my %required_fields_lut;
        @required_fields_lut{ $self->____identify_required_fields() } = $self->____identify_required_fields();

        my %opts =
          map { ( $_ => $self->{____oop_perlish_class_opts}->{$_} ) }
          grep { !exists( $required_fields_lut{$_} ) } keys %{ $self->{____oop_perlish_class_opts} };

        # prepopulate accessors so that calls that cascade will have values assigned
        # Set everything by accessor that we ->can()
        while( my ( $method, $value ) = each %opts ) {
            $self->$method($value) if( $self->can($method) );
        }

        $self->____validate_defaults();

        # reset all accessors for actually set values, re-running cascades where applicable...
        # there must be a better way, but this works
        while( my ( $method, $value ) = each %opts ) {
            $self->$method($value) if( $self->can($method) );
        }
        return;
    }

    ############################################################################################
    ## verify all default values are valid for the class
    ############################################################################################
    ## FIXME: make this static
    ############################################################################################
    sub ____validate_defaults(@)
    {
        my ($self) = @_;

        for my $field ( keys %{ $self->____OOP_PERLISH_CLASS_ACCESSORS() } ) {
            $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$field}->self($self);
            $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$field}->__validate_default();
        }
        return;
    }
}
1;
__END__

=head1 NAME

OOP::Perlish::Class - A Base class implementation providing the fundimental infrastructure for perl OOP

=head1 DESCRIPTION

A Base class for creating Objects that conform to all common OOP practices, while still remaining very much perl.

=head2 Currently supported:

=over

=item Multiple-Inheritance

=item Mix-in

=item Meta-programming (class introspection; quite useful with mix-ins) 

=item Generational Inheritance (complex hiarchies of inheritance)

=item method overriding/overloading

=item operator overriding/overloading

=item Accessor validation

=item accessor cascading via validator subroutine

=item singletons

=item multitons (aka: multi-singletons, keyed singletons, named singletons, singleton-maps)

=item polymorphism (aka duck-typing for ruby folks)



( run in 0.714 second using v1.01-cache-2.11-cpan-99c4e6809bf )