Moose Loose Ends Part the IX
Another postette day here in the Moose-Pen
I had only one little goal today and that is come up with a really nice single construction error message. After a good hour or so of mucking about I finally came up with;
The following Attribute is required: (view->name)
The following Attribute did not pass validation:
'view->alias' Constraint: Validation failed for 'Str' with value [ ]
With constructor hash:
{
'elements' => [{ name => 'first_name', }, { name => 'last_name', }],
'view' => {
'alias' => [],
'xname' => 'ss'
}
}
at 30_view.t line 54#
Now how did I accomplish the above?
It did take a little while for me to figure it all out but in the end the solution was quite simple. The “AllErrors” class really gives me very little to work with besides the type of fail 'missing or constraint', the name of the attribute and if a constraint the constraint.
The most I could normaly get out of the 'ALLErrors' object is;
The following Attribute is required: (name)
The following Attribute did not pass type constraints:
'alias' Constraint: Validation failed for 'Str' with value [ ]
Which left me with the problem of tying to indicate where that 'name' attribute was in the constructor. Is it in the 'elements' or the 'view' hash-ref?
I did start off by tying to plug my way though the 'AllErrors' object looking to find what was the 'name' attribute belonged to. Even avter a good hour or two transiting the Mo0se 'MOP' attributes of the 'AllErrors' instaces, I never did find a way to link the 'attribute' back to its class.
In the end I did come up with a little Moose-magic that provided me wiht an easy solution to get that 'view' in there. All I had to do was use the 'meta data' of the attribute itself to help me out. For example all I did for the 'view' was modidy its class like this;
package
Database::Accessor::View;
use Moose;
extends 'Database::Accessor::Base';
with qw(Database::Accessor::Roles::Alias);
has '+name' => ( required => 1,
++ documentation => "view" );
…
I added in the 'documentation' meta data on the 'name' attribute so now I know where that name comes from.
The rest of the change is just standard perl;
...
die $@;
}
else {
my @errors;
my $error_msg;
my $error;
if ($@->missing()) {
foreach my $error ($@->missing()){
warn(Dumper($error->attribute->documentation));
push(@errors,sprintf('%s%s'
,($error->attribute->documentation) ? $error->attribute->documentation."->" : ""
,$error->attribute->name()));
}
$error = "The following Attribute"
.$class->_is_are_msg(scalar(@errors))
."required: ("
.join(",",@errors)
.")\n";
}
if ($@->invalid()) {
@errors= ();
foreach my $error ($@->invalid()){
push(@errors, sprintf("'%s%s' Constraint: %s"
,($error->attribute->documentation) ? $error->attribute->documentation."->" : ""
,$error->attribute->name
,$error->attribute->type_constraint->get_message($error->data)
));
}
$error .= "The followling Attribute"
.$class->_did_do_msg(scalar(@errors))
." not pass validation: \n"
.join("\n",@errors)
;
}
my $misc = "Database::Accessor new Error:\n"
. $error
. "\nWith constructor hash:\n"
. Dumper($ops);
my $die = MooseX::Constructor::AllErrors::Error::Constructor->new(
caller => [$package, $filename, $line, $subroutine ],
);
$die->add_error(MooseX::Constructor::AllErrors::Error::Misc->new(
{message =>$misc}));
die $die;
}
I even keep the 'AllErrors' as an error object.
Now just have to go though the rest of my 'classes' and add in the documentation, of course now anyone one who uses that 'documentation' will be a little surprised at what they get from my code.
Something for tomorrow I guess.
Leave a comment