Skip Menu |

This queue is for tickets about the Moo CPAN distribution.

Report information
The Basics
Id: 80492
Status: resolved
Priority: 0/
Queue: Moo

People
Owner: Nobody in particular
Requestors: AVENJ [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



Subject: Consuming a Moo::Role from a Moose class doesn't bring in methods consumed by the role
Latest $VERSION of Moo, Role::Tiny, Moose. If I consume a Moo::Role ('A') from a Moo::Role ('B') and then consume role 'B' from a Moo class, life is grand \o/ ... but the same deal except consuming 'B' from a Moose class appears to never copy methods defined in role 'A' The failing test in question (nothing in the author suite seems to exactly test this scenario): use Test::More tests => 4; use strict; use warnings FATAL => 'all'; { package My::Role::A; use Moo::Role; sub things { 1 } } { package My::Role::B; use Moo::Role; with 'My::Role::A'; } { package My::Moo::Class; use Test::More; use Moo; with 'My::Role::B'; } { package My::Moose::Class; use Test::More; use Moose; with 'My::Role::B'; } my $moo = My::Moo::Class->new; ok( $moo->does('My::Role::A'), "Moose class does My::Role::A" ); ok( $moo->can('things'), "can things() with Moo" ); my $moose = My::Moose::Class->new; ## does() passes: ok( $moose->does('My::Role::A'), "Moose class does My::Role::A" ); ## but can() fails and the method is not found: ok( $moose->can('things'), "can things() with Moose" );
Moo::HandleMoose doesn't give the Moose::Meta::Role (or the Moose::Meta::Class for that matter) a list of the role's methods, so Moose has to discover the list of methods itself using its own heuristics. These heuristics include get_code_info() which tells Moose what package the method was originally declared in. This helps it tell the difference between methods and imported helper subs. Because things() is declared in My::Role::A, it's not seen as a method in My::Role::B. Solutions are: 1. When constructing the Moose metaclass/metarole, explicitly tell it what subs to consider to be methods; or 2. Trick get_code_info() into believing that the things() sub was defined in My::Role::B. I can think of two ways of doing this; one involves the non-core XS module Sub::Name; the other involves using stringy eval to create a My::Role::B::things() sub which is a wrapper that does a goto to the original method. Which is not pretty. So solution #1 is probably the winner.
This small patch seems to fix the issue.
Subject: rt-80492-fix.patch
diff --git a/lib/Moo/HandleMoose.pm b/lib/Moo/HandleMoose.pm index e03e49c..21af12c 100644 --- a/lib/Moo/HandleMoose.pm +++ b/lib/Moo/HandleMoose.pm @@ -66,9 +66,13 @@ sub inject_real_metaclass_for { ); } }; - + my %methods = %{Role::Tiny->_concrete_methods_of($name)}; + while (my ($meth_name, $meth_code) = each %methods) { + $meta->add_method($meth_name, $meth_code) if $meth_code; + } + # if stuff gets added afterwards, _maybe_reset_handlemoose should # trigger the recreation of the metaclass but we need to ensure the # Role::Tiny cache is cleared so we don't confuse Moo itself.
On Tue Oct 30 16:17:27 2012, TOBYINK wrote: Show quoted text
> This small patch seems to fix the issue.
Works here, thank you sir.
On Wed Oct 31 18:59:17 2012, AVENJ wrote: Show quoted text
> On Tue Oct 30 16:17:27 2012, TOBYINK wrote:
> > This small patch seems to fix the issue.
> > Works here, thank you sir.
Shipped in 1.0.7. Thanks guys.