Danny
Danny

Reputation: 14154

How do Roles and Traits differ in Moose?

I have written a set of classes and interfaces that are implemented in Moose also using roles. What I am having trouble understanding is the exact differences in both usage and implementation of Moose traits vs. roles.

The Moose documentation states:

It is important to understand that roles and traits are the same thing. A role can be used as a trait, and a trait is a role. The only thing that distinguishes the two is that a trait is packaged in a way that lets Moose resolve a short name to a class name. In other words, with a trait, the caller can refer to it by a short name like "Big", and Moose will resolve it to a class like MooseX::Embiggen::Meta::Attribute::Role::Big.

It is my understanding that traits and roles are "the same". However, when implementing a basic test of the idea using the use Moose -traits 'Foo' syntax does not seem to do what I would expect. Surely I must be missing something here.

This first example fails with "Can't locate object method 'foo'"

package MyApp::Meta::Class::Trait::HasTable;
use Moose::Role;
sub foo { warn 'foo' }

package Moose::Meta::Class::Custom::Trait::HasTable;
sub register_implementation { 'MyApp::Meta::Class::Trait::HasTable' }

package MyApp::User;
use Moose -traits => 'HasTable';
__PACKAGE__->foo();  #Can't locate object method 'foo'

Compared to this one (which does work):

package MyApp::Meta::Class::Trait::HasTable;
use Moose::Role;
sub foo { warn 'foo' }

package Moose::Meta::Class::Custom::Trait::HasTable;
sub register_implementation { 'MyApp::Meta::Class::Trait::HasTable' }

package MyApp::User;
use Moose;
with 'MyApp::Meta::Class::Trait::HasTable';
__PACKAGE__->foo();  #foo

Upvotes: 21

Views: 6360

Answers (2)

perigrin
perigrin

Reputation: 4433

This is the only difference in how Moose uses the terms "Trait" and "Role". Moose's documentation and APIs often use the term "traits" as "Roles applied to Metaclasses". In your revised answer your first example applies the Role to MyApp::User's metaclass via -traits, the second example applies it to the class.

If you change your first example to:

package MyApp::Meta::Class::Trait::HasTable;
use Moose::Role;
sub foo { warn 'foo' }

package Moose::Meta::Class::Custom::Trait::HasTable;
sub register_implementation { 'MyApp::Meta::Class::Trait::HasTable' }

package MyApp::User;
use Moose -traits => 'HasTable';
__PACKAGE__->meta->foo();

You'll see "foo at [script]. line 3." Which is exactly what it supposed to be doing.

UPDATE: Apparently I'm not exactly correct here. Traits are roles applied to instances. The -traits hook applies HasTable to the metaclass instance for MyApp::User. I have updated the relevant Moose docs.

Upvotes: 12

Axeman
Axeman

Reputation: 29854

You don't define a package 'x::Foo' with any role. Ripped straight from the documentation, we see that register_implementation returns the name of an actually defined package:

package MyApp::Meta::Class::Trait::HasTable;
use Moose::Role;

has table => (
  is  => 'rw',
  isa => 'Str',
);

package Moose::Meta::Class::Custom::Trait::HasTable;
sub register_implementation { 'MyApp::Meta::Class::Trait::HasTable' }

package MyApp::User;
use Moose -traits => 'HasTable';

__PACKAGE__->meta->table('User');

The "shortcut" is achieved by Moose looking for "Moose::Meta::Class::Trait::$trait_name" (when called in a "class context"), not just passing back a shorter name.

Upvotes: 0

Related Questions