Moose in Sheepish Clothing

Now to get back on track to what I wanted before I made a little Boo-Boo, So in today's post I am going to have a quick look at Moose and its Coercion abilities. One thing about moose Coercion is it works along with Moose Types so you can't have one without the other.
So lets start wit the 'View' attribute.

Right now we have this

has View => (
is => 'rw',
isa => 'Object',

Which is fine but there is noting stopping my from entering any type of object in there so lets fix that

-- isa => 'Object',
++isa => 'Database::Accessor::View',

that will work but it is a good deal of typing that I do not really need and of course more typing means more chance for an error. Now Moose will let you define a custom type and ave you all that typing. All I have to do is create a new Moose role to hold these types. For now I am adding it to the file like this

package Database::Accessor::Types;
use Moose::Role;
use Moose::Util::TypeConstraints;
use Database::Accessor::View;

class_type 'View', { class => 'Database::Accessor::View' };

Noticed what I have done is imported the Moose::Util::TypeConstraints package and that give me the 'class_type' constructor. I also use the class I am intrested in 'Database::Accessor::View' and then I use the constructor to create the new type called 'View' that I then link with the 'class' param to link the 'Database::Accessor::View' class to that type. Quite simple

Now in my all I have to do is use that new role

use Moose;
++with qw(Database::Accessor::Types);
use Moose::Util qw(does_role);

and change my attribute to use that new class

-- isa => 'Object',
++ isa => 'View',

and fire my tests and they fail. So what went wrong?

Not to go into too much detail on the internals of Moose loading of roles and classes it is best to have the type roles in separate files. I did get it to work in the file but I had to move all the other classes about to get it to work. Now if you ever had the need to have a look in a .pm file and found it all a jumble of related packages you know how frustrating this can be. As well as types are meant to be shared it is best to have them in separate files.

Anyway once I moved the type role into a separate file I reran my test and got all ok. Just for kicks I changed my test a little to test that I could only enter an View class in the View attribute like this

eval {
if ($@) {
pass("Can only take a View Class");
else {
fail("Takes a non View Class");

where I am trying to sneak in an 'Element' class in there and I get this on the test
ok 7 - City is an Element
ok 8 - Can only take a View Class
ok 9 - Address is a Database::Accessor

Now the one thing I dislike about Moose types are the sometimes very cryptic errors they can return. The error that was returned above was;
Attribute (view) does not pass the type constraint because: Validation failed for 'View' with value Database::Accessor::Element{ name: "street" } at accessor Database::Accessor::view (defined at D:\GitHub\DA-blog\lib\Database\ line 86) line 4
Database::Accessor::view('Database::Accessor=HASH(0x557a67c)', 'Database::Accessor::Element=HASH(0x557e52c)') called at 02_base_Accessor.t line 70

and this is just a small one if the was buried deep in you code with many more lines of moose poop as I like to call it. Now there are Moose solutions for this but that will be another post.

Well now that I got the first part of working with coercion done I think I will save the rest for my next post.


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