Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things

Moose role with requirement consuming another role

by Boldra (Deacon)
on Jul 20, 2011 at 09:45 UTC ( #915620=perlquestion: print w/replies, xml ) Need Help??

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

I want a role to consume another role, and provide an attribute that the consumed role requires, but Moose won't see the attribute provided by the first role.

An example:

package Role::Logger; use Moose::Role; requires qw<smeg>; package Role::App; use Moose::Role; has smeg => ( is => 'rw' ); with 'Role::Logger'; package App::FixIt; use Moose; with "Role::App"; sub run { print "smeg=" . shift->smeg; } package main; use Moose; App::FixIt->new( { smeg => 1 } )->run;

This code returns the error 'Role::App' requires the method 'smeg' to be implemented by 'App::FixIt'. But Role::App doesn't have any requirements, it's Role::Logger which requires smeg, and smeg is provided by Role::App.

The Moose documentation to required attributes warns that a requirement met by an attribute in a consuming class must be met before the role is consumed, so:

package Role::App; use Moose::Role; requires 'gazpacho'; package App::FixIt; use Moose; with Role::App; has gazpacho => ( is => 'rw' );
breaks because the requirement is met after the role is consumed. Here, swapping the order of with and has solves the problem. OTOH, requirement met by a subroutine would also be ok, because the subroutine is defined before with imposes the required constraints at runtime.

In summary: If a role requires a method and the requirement is met by:
a sub in a consuming classNo problem
a sub in a consuming roleNo problem
an attribute in a consuming classbe careful of the has/with order
an attribute in a consuming rolecan't be done?

Thanks for your thoughts!

Replies are listed 'Best First'.
Re: Moose role with requirement consuming another role
by moritz (Cardinal) on Jul 20, 2011 at 10:09 UTC
    I don't quite understand your approach. If role Role::App requires the attribute gazpacho that role App::FixIt provides, why not not simply compose App::FixIt into Role::App?
      My second example (with gazpacho), was really just meant to show that I understand the importance of the with/has order when a class consumes a role. Perhaps I over-emphasized it.

      The real problem is not with class<-role(requirement), but, similar to my first example, class<-role<-role<-role(requirements). Or to go into yet more detail, I have about 30 app classes which consume one of two specialized App roles, these both consume a generic App role, which consumes three further roles, MooseX::Getopt, MooseX::SimpleConfig plus our own app-logger role.

      I can easily drop the requirement in the app-logger role and everything works fine. I'm just curious if there is a better solution.

      - Boldra
Re: Moose role with requirement consuming another role
by Arunbear (Prior) on Jul 20, 2011 at 11:11 UTC
    Your Role::Logger role looks like an "interface role" in the sense described in Roles Versus Abstract Base Classes, and your Role::App is being used like an abstract base class.

    But this type of usage doesn't seem to be (officially) supported

      I can see how my simplified example matches that description of an interface role, but in reality the Logger Role has six attributes and 200 lines of code, it's not just a list of 'requires'.

      "this type of usage doesn't seem to be (officially) supported" == "I can't find it in the doc either"


      - Boldra
        Role::App being used like an abstract base class is what is at odds with the documentation (regardless of whether or not the Logger Role is an interface type role).
Re: Moose role with requirement consuming another role
by nysus (Vicar) on Feb 25, 2017 at 01:51 UTC

    I'm almost 6 years late to this conversation but this worked for me:

    { package MyOpenSSH 0.000001; use Moose::Role; has 'ssh' => (is => 'rw', isa => 'Str', required => 0, lazy => 0 ); } { package Apache2Info 0.000001; use Moose::Role; requires 'ssh'; } { package WebServerRemote 0.000001; with 'MyOpenSSH'; with 'Apache2Info'; }

    The trick seems to have been to put the with statements on two different lines and putting the role with the required attribute first in the list.

    $PM = "Perl Monk's";
    $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate";
    $nysus = $PM . ' ' . $MCF;
    Click here if you love Perl Monks

Re: Moose role with requirement consuming another role
by preaction (Beadle) on Feb 01, 2013 at 22:13 UTC

    I just ran into this problem myself, and I fixed it by simply removing the requires. They're just guidelines, I guess.

    If there's a way to get this working, I'd love to know.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://915620]
Front-paged by Arunbear
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (4)
As of 2020-02-29 12:52 GMT
Find Nodes?
    Voting Booth?
    What numbers are you going to focus on primarily in 2020?

    Results (128 votes). Check out past polls.