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.
Leave a comment