Invincible Moose

Today in the Moose-Pen I am going to have a quick look at some very Moosish behavior when playing with types. I known you must be getting tired for Moose Types, but I think today you will see something that may save you time if you ever go deeply into Moose Types and coercion.

So to set this up say I have a look a new test I added to the 33_condtions.t test case


$da = Database::Accessor->new({view => {name => 'People'}});
$in_hash = {conditions=>[{left =>{name =>'last_name2',
view =>'People'},
right =>{value=>'test'},
operator =>'=',
open_parenthes =>1,
close_parenthes=>0,
condition =>'AND',},
{condition =>'AND',
left =>{name=>'first_name3',
view=>'People'},
right =>{ value=>'test'},
operator =>'=',
open_parenthes =>0,
close_parenthes=>1}]};
foreach my $condition (@{$in_hash->{conditions}}){
ok($da->add_condition($condition),"can add an single Dynamic condition");
}

This is testing that new dynamic_conditions attribute on Accessor I talked about adding in my last post.
Now this test will fail with this coercion code

coerce 'ArrayRefofConditions', from 'ArrayRef', via {
    my $objects = [];
    foreach my $object (@$_) {
        push( @{$objects},  Database::Accessor::Condition->new({predicates=>[$object]}) ); 
     }
    return $objects  
  },
   from 'HashRef', via {
    my $objects = [];
    push( @{$objects}, Database::Accessor::Condition->new({predicates=>[$_]}) );
    return $objects;
}

and the reason is a little obtuse. It will work on the first pass of a condition in but die with this error

Single parameters to new() must be a HASH ref at C:\Dwimperl\perl\site\lib\Moose\Object.pm line 22

on the second add attempt;

To find out what is going on here is I had to ref what is coming in via the $_ one would see its an Array both time the difference being on the first run there is 1 item and on the second there are 2.

What we are seeing is on the first iteration

$_ = [{in-hash 1 }];

and on the second iteration

 $_ = [{Condition class 1},
       {in-hash 2}];
So what you are seeing here are all of the present values in the 'dynamic_conditions' attribute are returning with the $_. Now of course my old code will no longer work as it is trying to take 'condition class 1' and create a new Condition with it ,not a good thing.

Fortunately I have come across this before and the simple (re-factored, so I can use it on more than one Class) solution is


coerce 'ArrayRefofConditions', from 'ArrayRef', via {
return _predicate_array_or_object("Database::Accessor::Condition",$_);
};

sub _predicate_array_or_object {
my ($class,$in) = @_;
my $objects =[];
foreach my $object (@{$_}) {
if ( ref( $object) eq $class ) {
push( @{$objects}, $object );
}
else {
push( @{$objects},$class->new( {predicates=>[$object]} ) );
}
}
return $objects;
}


So all I do is iterate over my in-coming $_ and test each value if it is an instance of the required class, and if it does match up I just add it to my $objects output. Otherwise I create a new instance of my class and add that to my $objects and just return that array ref when done.

And now in my tests I get

ok 8 - can add an array of Dynamic conditions
ok 9 - DA Array Dynamic condition 0->predicates 0 correct
ok 10 - DAD Array Dynamic condition 0->predicates 0 correct
ok 11 - DA Array Dynamic condition 0->predicates 1 correct
ok 12 - DAD Array Dynamic condition 0->predicates 1 correct

As a bonus I no longer need the from 'HahsRef' in my coerce.

Moose sometimes seems invincible if you know what is going on.

Thumbnail image for 80b3e6d1dda30b145f07d26ce8117bc4.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