Moose Delegates

Today in the Moose-Pen I am going to look at something new 'Native Delegations' on attributes or to say it another less wordy way 'Traits'

Native Delegation is a way to let you treat standard Perl data structure as if they where objects. So to take an example from my test cases say you have this

foreach my $index2  (0..(scalar(@{$predicates[$index]->predicates()})-1)) {
that (scalar(@{$predicates[$index]->predicates()})-1)) is not very readable. Now suppose we could just have this

($predicates [$index]->predicates()->count-1)
a little easier to read. Now one can do this kind of thing in plain Perl but it can be a little code gymnastics if you are really interested in how much code it is check out the 'DBD::_::common' package for an example of a partial work up for a Hash object. You can also have a look here to see how to set this up for an ARRAY or if you have trouble falling asleep. There is even a package called Tie::Array that my help you as well, anyway you look at it you will end up writing a slew of 'sub's to cover off the various 'functions' that work with an array. Just have a look at this from Tie::Array::Boolean;

sub TIEARRAY {
    my $class = shift;
    my $self = {
        bits  => '',
        size  => 0,
    };
    return bless $self, $class;
}
sub STORE {
    my $self  =    shift;
    my $index =    shift;
    my $value = !! shift;
    $self->STORESIZE( 1+$index ) if 1+$index > $self->FETCHSIZE();
    vec( $self->{bits}, $index, 1 ) = $value;
    return;
}
...
and this goes on for some ninety more lines. Now in Moose all we need to do is add in the 'traits' command and an array-ref of what you want your attribute to be. Now I have set one of these up in Accessor on my 'PredicateArray' Role.

 has predicates => (
        traits  => ['Array'],
        is      => 'rw',
        isa     => 'ArrayRefofPredicates',
        coerce  => 1,
        alias   => 'conditions',
        handles => {
            count => 'count',
        },
    );
So in the above I have a the 'traits' command that tell Moose that this attribute is to be handled as an 'Array' a little further down I have the 'handles' command where I delegate what I want this attribute to. There are some limitations to what you can do with Native Delegation depending on which native you select. There are for example some twenty six for array . Now lets see this in action. I can use this right away in my 'Test::Database::Accessor::Utils' 'deep_predicate' function where I can do a quick change to this

–  foreach my $index2  (0..(scalar(@{$predicates[$index]->predicates()})-1)) { 
++  foreach my $index2  (0..($predicates[$index]->count()-1)) {
I do not even need that and my test still come up 100%. Now one caveat with traits and delegation in general, if if was to try and reuse this 'count' for more than one delegation in a class say like this;

  has elements => (
        isa    => 'ArrayRefofElements',
         traits  => ['Array'],
        coerce => 1,
        is     => 'ro',
        default => sub { [] },
        handles => {
            count => 'count',
        },
    );
 has conditions => (
        is      => 'ro',
        isa     => 'ArrayRefofConditions',
        traits  => ['Array'],
        coerce  => 1,
        default => sub { [] },
        handles => {
            count => 'count',
        },
    );
You would get an error like this

Error:  You cannot overwrite a locally defined method (count) with a delegation at C:\Dwimperl\perl\site\lib\Class\MOP\Class.pm line 899
So you have to do something like this;

as elements => (
        isa    => 'ArrayRefofElements',
         traits  => ['Array'],
        coerce => 1,
        is     => 'ro',
        default => sub { [] },
        handles => {
            element_count => 'count',
        },
    );
 has conditions => (
        is      => 'ro',
        isa     => 'ArrayRefofConditions',
        traits  => ['Array'],
        coerce  => 1,
        default => sub { [] },
        handles => {
            conditions_count => 'count',
        },
    );
So I renamed my 'count' delegation on 'predicates' to predicates_count. I was afraid with this I would get such silliness as '$predicates[$index]->predicates->predicates_count()', but as these delegates are at a class level I get $predicates[$index]->predicates_count()' a little longer than just count but still usable.

So with a few Moose commands I get rid of may dozens of lines of code and add a good deal of flexibility to my API.


166504_10150949671398684_1151206689_n.jpg

Leave a comment

About byterock

user-pic Long time Perl guy, a few CPAN mods allot of work on DBD::Oracle and a few YAPC presentations