jjyoung has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to get used to Moose and I'm having an issue mixing roles with subclassing. I have a role of the form:

#!/pdd/100/bin/perl5.16/bin # Filename: ChainOfResponsibilityIf package ChainOfResponsibilityIf; use Moose::Role; requires 'handleRequest'; requires 'addHandler;

A class that uses it:

#!/pdd/100/bin/perl5.16/bin # Filename: CoRHandler.pm package CoRHandler; use Moose; use Carp qw(confess croak); with 'ChainOfResponsibilityIf'; # implementation of roles not shown

and then a class that extends CoRHandler but doesn't override the roles:

#!/pdd/100/bin/perl5.16/bin # Filename: GenericCommandHandler.pm package GenericCommandHandler; use Moose; extends 'CoRHandler'; override '_processCommand' => sub { }

When I create an instance of GenericCommandHandler and try to assign it to an attribute defined as:

has 'successor' => ( is => 'rw', isa => 'ChainOfResposibilityIf', );

I get the following error at runtime:

Attribute (successor) does not pass the type constraint because: Valid +ation failed for 'ChainOfResposibilityIf' with value GenericCommandHa +ndler=HASH(0x31bb698) at (eval 333)[/pdd/100/bin/perl5.16/lib/Eval/Cl +osure.pm:125] line 8.

Any clues to what I'm doing wrong? I've tried including the role 'with' declaration in the subclass, but then I get errors about missing the 'role' required methods.

Replies are listed 'Best First'.
Re: Moose roles with extends
by tobyink (Canon) on Apr 11, 2014 at 08:52 UTC

    When Moose sees this:

    has 'successor' => ( is => 'rw', isa => 'ChainOfResposibilityIf', );

    If the ChainOfResposibilityIf role has not yet been loaded (use/require), Moose needs to make a snap decision about what kind of thing ChainOfResposibilityIf is. It needs to set up everything for the attribute straight away - it doesn't wait until you actully make use of the attribute.

    Anyway, Moose guesses that it's a class. So later, when you assign to the attribute, it checks $value->isa('ChainOfResposibilityIf') (which fails) instead of $value->DOES('ChainOfResposibilityIf') (which would pass).

    Three possible solutions. Any of them will do:

    • Load ChainOfResposibilityIf earlier. Before using it in an attribute definition. (This seems the most fragile solution to me, and is my least preferred, but it should work.)

    • Instead of isa use does:

      has 'successor' => ( is => 'rw', does => 'ChainOfResposibilityIf', # !!! );
    • Create an explicit type constraint object, and pass that to isa instead of passing the string "ChainOfResposibilityIf":

      use Moose::Util::TypeConstraints qw(role_type); has 'successor' => ( is => 'rw', isa => role_type('ChainOfResposibilityIf'), );
    use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
Re: Moose roles with extends
by kcott (Archbishop) on Apr 11, 2014 at 07:12 UTC

    G'day jjyoung,

    Welcome to the monastery.

    The short answer may simply be what the error message is telling you. An instance of GenericCommandHandler IS-A GenericCommandHandler and IS-A CoRHandler (both of which are classes), but it IS-NOT-A ChainOfResponsibilityIf which is a role.

    If that doesn't answer your question, then a longer answer may require some further input from you.

    I created a './PM/1081903/ directory and populated it with:

    $ cat ChainOfResponsibilityIf.pm package PM::1081903::ChainOfResponsibilityIf; use Moose::Role; requires 'handleRequest'; requires 'addHandler'; 1;
    $ cat CoRHandler.pm package PM::1081903::CoRHandler; use Moose; use Carp qw(confess croak); with 'PM::1081903::ChainOfResponsibilityIf'; sub handleRequest { 1 } sub addHandler { 1 } sub _processCommand { 1 } 1;
    $ cat GenericCommandHandler.pm package PM::1081903::GenericCommandHandler; use Moose; extends 'PM::1081903::CoRHandler'; override '_processCommand' => sub { }; 1;
    $ cat GCHSuccessor.pm package PM::1081903::GCHSuccessor; use Moose; has 'successor' => ( is => 'rw', isa => 'PM::1081903::ChainOfResposibilityIf', ); 1;

    I then ran this script:

    #!/usr/bin/env perl use strict; use warnings; use PM::1081903::GenericCommandHandler; use PM::1081903::GCHSuccessor; my $gchs = PM::1081903::GCHSuccessor::->new( successor => PM::1081903::GenericCommandHandler::->new() );

    I got a lot of error output which started the same as you reported "Attribute (successor) does not pass the type constraint ...". See the spoiler for the full message.

    Attribute (successor) does not pass the type constraint because: Valid +ation failed for 'PM::1081903::ChainOfResposibilityIf' with value PM: +:1081903::GenericCommandHandler=HASH(0x7fa22882a610) (not isa PM::108 +1903::ChainOfResposibilityIf) at /Users/ken/perl5/perlbrew/perls/perl +-5.18.1t/lib/site_perl/5.18.1/darwin-thread-multi-2level/Moose/Meta/A +ttribute.pm line 1279. Moose::Meta::Attribute::verify_against_type_constraint('Moose::Met +a::Attribute=HASH(0x7fa2292b89e0)', 'PM::1081903::GenericCommandHandl +er=HASH(0x7fa22882a610)', 'instance', 'PM::1081903::GCHSuccessor=HASH +(0x7fa2288525c8)') called at /Users/ken/perl5/perlbrew/perls/perl-5.1 +8.1t/lib/site_perl/5.18.1/darwin-thread-multi-2level/Moose/Meta/Attri +bute.pm line 1266 Moose::Meta::Attribute::_coerce_and_verify('Moose::Meta::Attribute +=HASH(0x7fa2292b89e0)', 'PM::1081903::GenericCommandHandler=HASH(0x7f +a22882a610)', 'PM::1081903::GCHSuccessor=HASH(0x7fa2288525c8)') calle +d at /Users/ken/perl5/perlbrew/perls/perl-5.18.1t/lib/site_perl/5.18. +1/darwin-thread-multi-2level/Moose/Meta/Attribute.pm line 536 Moose::Meta::Attribute::initialize_instance_slot('Moose::Meta::Att +ribute=HASH(0x7fa2292b89e0)', 'Moose::Meta::Instance=HASH(0x7fa228843 +e80)', 'PM::1081903::GCHSuccessor=HASH(0x7fa2288525c8)', 'HASH(0x7fa2 +290d5108)') called at /Users/ken/perl5/perlbrew/perls/perl-5.18.1t/li +b/site_perl/5.18.1/darwin-thread-multi-2level/Class/MOP/Class.pm line + 525 Class::MOP::Class::_construct_instance('Moose::Meta::Class=HASH(0x +7fa22929a2e8)', 'HASH(0x7fa2290d5108)') called at /Users/ken/perl5/pe +rlbrew/perls/perl-5.18.1t/lib/site_perl/5.18.1/darwin-thread-multi-2l +evel/Class/MOP/Class.pm line 498 Class::MOP::Class::new_object('Moose::Meta::Class=HASH(0x7fa22929a +2e8)', 'HASH(0x7fa2290d5108)') called at /Users/ken/perl5/perlbrew/pe +rls/perl-5.18.1t/lib/site_perl/5.18.1/darwin-thread-multi-2level/Moos +e/Meta/Class.pm line 284 Moose::Meta::Class::new_object('Moose::Meta::Class=HASH(0x7fa22929 +a2e8)', 'HASH(0x7fa2290d5108)') called at /Users/ken/perl5/perlbrew/p +erls/perl-5.18.1t/lib/site_perl/5.18.1/darwin-thread-multi-2level/Moo +se/Object.pm line 28 Moose::Object::new('PM::1081903::GCHSuccessor', 'successor', 'PM:: +1081903::GenericCommandHandler=HASH(0x7fa22882a610)') called at ./pm_ +example.pl line 9

    I then changed this line in GCHSuccessor.pm

    isa => 'PM::1081903::ChainOfResposibilityIf',

    to

    isa => 'PM::1081903::CoRHandler',

    I reran the same script and no output at all was generated. Perhaps this is the (type of) change you need.

    I then added this line to the end of that script:

    print $gchs->successor->handleRequest(), "\n";

    This produced 1 as its only output. So, the value of the successor attribute of the PM::1081903::GCHSuccessor object DOES the PM::1081903::ChainOfResposibilityIf role. Is that what you were trying to test?

    If some, or all, of that hasn't answered your question, or my guesses at your class stucture were wrong, please provide actual code that we can run, along with a clear description of what trying to test or learn, such that we can point you in the right direction.

    -- Ken