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

NAME

Mic::Impl

SYNOPSIS

    package Example::Construction::Acme::Counter;

    use Mic::Impl
        has  => {
            COUNT => { init_arg => 'start' },
        }, 
    ;

    sub next {
        my ($self) = @_;

        $self->[COUNT]++;
    }

    1;

DESCRIPTION

An implementation is a package containing subroutines implementing the behaviours described by the class interface as well as attribute definitions (object state).

CONFIGURATION

A implementation package is configured using Mic::Impl and providing a hash with the following keys:

has => HASHREF

This declares attributes (or instance variables) of the implementation, mapping the name of an attribute to a hash with keys described in the following sub sections.

An attribute called "FOO" can be accessed via it's object using the symbol FOO which is created by Mic::Impl

    $self->[FOO]

default => SCALAR | CODEREF

The default value assigned to the attribute when the object is created. This can be an anonymous sub, which will be excecuted to build the the default value (this would be needed if the default value is a reference, to prevent all objects from sharing the same reference).

handles => ARRAYREF | HASHREF

This declares that methods can be forwarded from the object to this attribute in one of two ways described below.

handles => ARRAYREF

All methods in the given array will be forwarded.

handles => HASHREF

Method forwarding will be set up such that a method whose name is a key in the given hash will be forwarded to a method whose name is the corresponding value in the hash.

init_arg => SCALAR

This causes the attribute to be populated with the value of a similarly named constructor parameter.

reader => SCALAR

This must be a string which defines the name of a generated reader (or accessor) method.

Readers should only be created if they are needed by end users of the class.

writer => SCALAR

This must be a string which defines the name of a generated writer (or mutator) method.

Writers should only be created if they are needed by end users of the class.

classmethod => ARRAYREF

A list of methods that are intended to be called via the class (package), rather than via an object. See Mic::Manual::Construction for an example.

PRIVATE ROUTINES

An implementation package will typically contain subroutines that are for internal use in the package and therefore ought not to be declared in the interface. These won't be callable using the $object->command(...) syntax.

As an example, suppose we want to print an informational message whenever the Set's has or add methods are called. A first cut may look like:

    sub has {
        my ($self, $e) = @_;

        warn sprintf "[%s] I have %d element(s)\n", scalar(localtime), scalar(keys %{ $self->[SET] });
        exists $self->[SET]{$e};
    }

    sub add {
        my ($self, $e) = @_;

        warn sprintf "[%s] I have %d element(s)\n", scalar(localtime), scalar(keys %{ $self->[SET] });
        ++$self->[SET]{$e};
    }

But this duplication of code is not good, so we factor it out:

    sub has {
        my ($self, $e) = @_;

        log_info($self);
        exists $self->[SET]{$e};
    }

    sub add {
        my ($self, $e) = @_;

        log_info($self);
        ++$self->[SET]{$e};
    }

    sub size {
        my ($self) = @_;
        scalar(keys %{ $self->[SET] });
    }

    sub log_info {
        my ($self) = @_;

        warn sprintf "[%s] I have %d element(s)\n", scalar(localtime), $self->size;
    }

Notice how the log_info routine is called as a regular subroutine rather than as a method.

Here is a transcript of using this object via reply

    5:51% reply -I t/lib
    0> use Example::Construction::Set_v1
    1> my $set = Example::Construction::Set_v1::->new
    $res[0] = bless( [
         {}
       ], 'Example::Construction::Acme::Set_v1::__Assembled' )

    2> $set->can
    $res[1] = [
      'add',
      'has',
      'size'
    ]

    3> $set->add(1)
    [Thu Aug 10 16:16:15 2017] I have 0 element(s)
    $res[2] = 1

    4> $set->add(1)
    [Thu Aug 10 16:16:36 2017] I have 1 element(s)
    $res[3] = 2

    5> $set->log_info()
    Can't locate object method "log_info" via package "Example::Construction::Acme::Set_v1::__Assembled" at reply input line 1.
    6>

OBJECT COMPOSITION

Composition allows us to create new objects incorporating the functionality of existing ones.

As an example, consider a queue which we would use like this:

    use strict;
    use Test::More;
    use Example::Delegates::Queue;

    my $q = Example::Delegates::Queue->new;

    is $q->size => 0;

    $q->push(1);
    is $q->size => 1;

    $q->push(2);
    is $q->size => 2;

    $q->pop;
    is $q->size => 1;
    done_testing();

Now suppose we need a queue which maintains a fixed maximum size by evicting the oldest items:

    use strict;
    use Test::More;
    use Example::Delegates::BoundedQueue;

    my $q = Example::Delegates::BoundedQueue::->new({max_size => 3});

    $q->push($_) for 1 .. 3;
    is $q->size => 3;

    $q->push($_) for 4 .. 6;
    is $q->size => 3;
    is $q->pop => 4;
    done_testing();

Here is the interface for this fixed size queue

    package Example::Delegates::BoundedQueue;

    use Mic::Class
        interface => {
            object => {
                push => {},
                pop  => {},
                size => {},
            },
            class => { new => {} }
        },

        implementation => 'Example::Delegates::Acme::BoundedQueue_v1',
    ;

    1;

And it is implemented like this

    package Example::Delegates::Acme::BoundedQueue_v1;

    use Example::Delegates::Queue;

    use Mic::Impl
        has  => {
            Q => {
                default => sub { Example::Delegates::Queue::->new },
                handles => [qw( size pop )],
            },

            MAX_SIZE => {
                init_arg => 'max_size',
            },
        },
    ;

    sub push {
        my ($self, $val) = @_;

        $self->[Q]->push($val);

        if ($self->size > $self->[MAX_SIZE]) {
            $self->pop;
        }
    }

    1;

The bounded queue is composed out of the regular queue, which handles the size and pop methods.