Two Moose at Play
So today in the Moose pen I am going to have a closer look at my Role from my last post. So here it is
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'
);
Now the above is fine for my View and Element Database::Accessor classes but the above sort of breaks down when I have a look a the 'Database::Accessor::Predicate' class.
Now it is always good to have a class to have a 'name', makes debugging a little easier, but is the name required in this Class. Thinking a few jumps ahead when when I go to write my say my SQL DAD I can not see much use for requiring a name in a predicateand I cannot think of any case where I will need an 'alias' so to do this;
package
Database::Accessor::Predicate;
use Moose;
with qw(Database::Accessor::Roles::Base);
is not what I really want.
Now I could do this in Predicate to mitigate things
has '+name' => ( required => 0 );
Now in this case I am telling Moose to override the consumed aspect of 'required => 1' with 'required => 0' thus changing it to not required. I can test this by changing my 14_predicate.t case like so;
--my $predicate = Database::Accessor::Predicate->new({name => 'name',left=> {name=>'left'},right=>{name=>'right'}});
++my $predicate = Database::Accessor::Predicate->new({left=> {name=>'left'},right=>{name=>'right'}});
and all my test still pass
ok 1 - use Database::Accessor;
ok 2 - use Database::Accessor::Predicate;
ok 3 - predicate is a Predicate
ok 4 - View does role Database::Accessor::Roles::Base
Now that leaves the little matter of the alias. Unfortunately Moose has no easy way to fix this. I could go into a very long post about how I can use the BEGIN sub along with the MOP remove_attribute function to remove it but that is very very far from an elegant or even a good solution. So I am left with one of three choices.
- Drop 'alias' from the role
- Make two roles one for Base one for Alias
- Make two roles and try to exclude 'alias' from one?
I first start with my Base role and remove alias form it.
package
Database::Accessor::Roles::Base;
BEGIN {
$Database::Accessor::Roles::DAD::VERSION = "0.01";
}
use Moose::Role;
has 'name' => (
required => 1,
is => 'rw',
isa => 'Str'
);
next I create the second role called Alias
package
Database::Accessor::Roles::Alias;
BEGIN {
$Database::Accessor::Roles::Alias = "0.01";
}
use Moose::Role;
with 'Database::Accessor::Roles::Base';
has 'alias' => (
is => 'rw',
isa => 'Str',
);
}
and in this one I consume the 'Base' role then I change the View and Element classes to consume the 'Alias' role.
{
package
Database::Accessor::View;
use Moose;
with qw(Database::Accessor::Roles::Alias);
}
{
package
Database::Accessor::Element;
use Moose;
with qw(Database::Accessor::Roles::Alias);
}
Now when you think about it this is a much more logical way to apply and name my roles because if you have an 'alias' you must also have a 'name'.
Now to finish this off I just have the Predicate class consume the 'Base' role
package
Database::Accessor::Predicate;
use Moose;
with qw(Database::Accessor::Roles::Base);
add as always a little test for this
eval{
warn("rtest=".$predicate->alias());
};
if ($@){
pass("Predicate cannot alias");
}
else {
fail("Predicate cannot alias");
}
and a quick run
ok 1 - use Database::Accessor;
ok 2 - use Database::Accessor::Predicate;
ok 3 - predicate is a Predicate
ok 4 - View does role Database::Accessor::Roles::Base
ok 5 - Predicate cannot alias
So there you have it. With just a little jiggling about I have saved some code duplication and have a good first working role.
Leave a comment