The Many Roles a Moose Can Play

So today in the Moose-pen I was going to move out of the testing mode and actually do a little more coding, well some re-factoring anyway.

One of the best things about re-factoring Moose code is how easy it is to use a Role to stomp out all sort of duplicated code.

So of you may of already spotted s little anti-patter I had going on on most of my Database::Accessor classes and that was the;

 has 'name' => (
        required => 1,
        is     => 'rw',
        isa      => 'Str'
    );
and

    has 'alias' => (
        is     => 'rw',
        isa      => 'Str'
    );
attributes repeated over at least twice and even three times in the case of the Name. So this is a great candidate for a Role. Now the question is for me is not, whether there should be a Role but rather where should this role go.

As we know I have embedded my Database::Accessor in the Accessor.pm file and moved the package name to a separate line to hide all these little bits from PAUSE as I do not want programmer to play with them directly. The same goes for any Roles they may use as they are not meant for consumption by programmers.

Now my first role looks like this

{
    package 
      Database::Accessor::Roles::Base;
    BEGIN {
        $Database::Accessor::Roles::DAD::VERSION = "0.01";
    }
    use Moose::Role;
    has 'name' => (
        required => 1,
        is       => 'rw',
        isa      => 'Str'
    );

    has 'alias' => (
        is  => 'rw',
        isa => 'Str'
    );
    1;
}
which I tacked onto the tail end of my Accessor.pm file. Now in my Database::Accessor classes I simply cut out the same attributes then apply the role

{
    package 
           Database::Accessor::View;
    use Moose;
    with qw(Database::Accessor::Roles::Base);
}
{
    package 
           Database::Accessor::Element;
    use Moose;
    with qw(Database::Accessor::Roles::Base);
}
{
    package 
           Database::Accessor::Predicate;
    use Moose;
    with qw(Database::Accessor::Roles::Base);
...
Now I want to add in a little test for the above so I add in something like this

ok( does_role($view,"Database::Accessor::Roles::Base") eq 1,"View does role Database::Accessor::Roles::Base");
on the the View, Element, and Predicate tests cases and I get results like this
ok 1 - use Database::Accessor; ok 2 - use Database::Accessor::View; ok 3 - Person is a View ok 4 - View does role Database::Accessor::Roles::Base
So everything is good and fine and I can move on. You would think so but!, If I add in this to my View test case

ok( $view->name() eq 'person',"Has name Accessor");
I get

Can't locate object method "name" via package "Database::Accessor::View" at 10_view.t line 16.

Now wait a moment I consumed the Role and my test said it was consumed what is going on? Well in the first run of the tests my Database::Accessor::Roles::Base; was placed at the end of the Accessor.pm. It cannot go there as it is loaded after the Database::Accessor::View, so I simply moved it in Accessor.pm to before View and all my tests work fine. Not 100% sure why that is but there is a blog post there someplace?

So the moral of the story is perhaps is is a good idea to test to see if you can use an attribute that is being supplied by a role!

sign-fails-pics-096.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