Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Composing Roles into a Moose class not working

Tags:

oop

perl

moose

Aloha!

I have a role that I'm busy defining in a Moose class called Authable that is essentially composed into any class that might potentially require some form of authentication in the future; it's a rather simple role, here's the entirety:

package Trello::API::Roles::Authable;

use Moose::Role;

#authentication information
has key => (
    is => "rw",
    isa => "Str",
);

has token => (
    is => "rw",
    isa => "Str",
);

1;

For whatever reason, when I attempt to compose it into a class using multiple different statements, i.e., with "Trello::API::Roles::Authable"; or with "Roles::Authable";

I consistently get this same error message: You can only consume roles, Roles::Authable is not a Moose role.

Any idea why this might be happening?

Edit!

Just a side note, I checked the actual source for Moose::Role, and saw this bit:

    unless ($meta && $meta->isa('Moose::Meta::Role') ) {
        require Moose;
        Moose->throw_error( "You can only consume roles, "
                . $role->[0]
                . " is not a Moose role" );
    }

This seems to be where the error is occuring, so it almost seems that for some reason, the role I'm implementing isn't stating that it's a role in the metaclass. Though I could be mistaken! Any help would be appreciated.

Another convenient EDIT!

Bonus: Code context wherein the with routine is called.

package Trello::API::Resource;

use Moose;
use URI::Escape;
use LWP::UserAgent;


with 'Roles::Authable';

which when I do this, it intelligently knows to try and consume Roles/Authable.pm but for whatever reason, it just fails to function!

like image 458
ozzmotik Avatar asked Nov 14 '22 05:11

ozzmotik


1 Answers

First of all, I have to agree with Piers that technically, you really should be calling it as with 'Trello::API::Roles::Authable'.

So, you're asking for something that I don't find to be implemented in basic Moose. I have used the ideas of generic namespace pools before. They are sort of universal namespaces to which you can offer your semi-anonymous services--without the lock-in of a fixed namespace. I refined my basic idea of the namespace pool with Moose (really MOP) support.

In the Wild West days of Perl, all you would have to do is assign one stash to the symbol for the other, like so:

{   no strict 'refs'; 
    *{$short_pkg_name.'::'} = \*{$full_pkg_name.'::'};
};

And, those two packages were exactly the same things!

But now, we guard our data with lexicals a bit more. And because Class::MOP jealously guards its meta objects in a lexical hash, you have to add something else:

Class::MOP::store_metaclass_by_name( 
       $short_pkg_name
     , Class::MOP::get_metaclass_by_name( $full_pkg_name )
     );

Now they are the exact same thing to Perl and to MOP.

Thus you can create packages that are simply a namespace repository for other packages -- Now with MOP support!

package Namespace::Pool;
use strict;
use warnings;
use Params::Util qw<_POSINT>;

sub import { 
    shift; # It's just me.
    my $full_pkg_name = caller();
    Carp::croak( "'$full_pkg_name' is short enough!" ) 
        unless my $pool_name 
            = shift // [ split /::/, $full_pkg_name ]->[-2]
            ;
    Carp::croak( "'::$pool_name\::' not found in '$full_pkg_name'" ) 
        unless (  _POSINT( my $pos = rindex( $full_pkg_name, "::$pool_name\::" ))
               or my $is_short = _POSINT( index( $pool_name, '::' ))
               ); 
    my $short_pkg_name 
        = $is_short ? $poll_name 
        :             substr( $full_pkg_name, $pos + 2 )
        ;
    {   no strict 'refs'; 
        if ( %{$short_pkg_name.'::'} ) { 
            Carp::croak( "You have already defined $short_pkg_name!" );
        }
        *{$short_pkg_name.'::'} = \*{$full_pkg_name.'::'};
    };

    if ( my $meta = Class::MOP::get_metaclass_by_name( $full_pkg_name )) { 
        Class::MOP::store_metaclass_by_name( $short_pkg_name, $meta );
    }
    return;
}

Thus in your Role package you can do the following:

package Trello::API::Roles::Authable;
use strict;
use warnings;
use Moose::Role;
use Namespace::Pool 'Roles';
...

And know that it will be available from the namespace of 'Roles'.

like image 161
Axeman Avatar answered Feb 05 '23 00:02

Axeman