Subject: | Moose::Exporter generated init_meta won't init a new package |
The subject line is a lousy description, but it turned out to be fairly
difficult to explain the problem, even after I spent the last couple of
hours tracking it down.
Basically what it means is this. Assuming you have a role and a
Moose::Exporter created package like this:
package MyApp::Role;
use Moose::Role;
sub foo { print "FOO!\n" }
package MyApp::Moose;
use Moose::Exporter;
Moose::Exporter->setup_export_methods(
base_class_roles => ['MyApp::Role'],
also => ['Moose'],
);
And you then use it to create a new package, like so:
package MyApp::Foo;
use MyApp::Moose;
And then you call the method that the role provided, what do you think
will happen?
MyApp::Foo->foo;
Did you expect it to print "FOO!"? If you did, you'll be sorry, what
you get instead is:
Can't locate object method "foo" via package "MyApp::Foo" at ...
Strange?
What's happening is that when the Moose::Exporter provided import method
is called, it includes this chunk of code:
for my $c ( grep { $_->can('init_meta') } $class, @{$exports_from} ) {
# snip...
$c->init_meta( for_class => $CALLER, metaclass => $metaclass );
In our example this results in the following calls:
MyApp::Moose->init_meta(
for_class => 'MyApp::Foo',
metaclass => undef
);
Moose->init_meta(
for_class => 'MyApp::Foo',
metaclass => undef,
);
The problem is that the generated init_meta method begins with:
return unless Class::MOP::class_of( $options{ 'for_class' } );
So when MyApp::Moose->init_meta gets called, Class::MOP::class_of(
'MyApp::Foo' ) returns undef and the calls to Moose::Util::MetaRole get
skipped. Then Moose->init_meta gets called, and MyApp::Foo gets
initialized correctly, but by then it's too late, it already missed it's
chance to get the roles applied.
I've attached a git patch to lib/Moose/Exporter.pm with a fix that seems
to work for me without breaking anything else in the test suite, as well
as a patch to t/050_metaclasses/023_easy_init_meta.t that adds a test
that demonstrates the problem.
--
www.jasonkohles.com
Subject: | 0001-Fixed-init_meta-bug.patch |
From 5faf7fb624bf2196e64f82878e9515b9abdd5a76 Mon Sep 17 00:00:00 2001
From: Jason Kohles <email@jasonkohles.com>
Date: Sun, 15 Nov 2009 19:10:44 -0800
Subject: [PATCH] Fixed init_meta bug
---
lib/Moose/Exporter.pm | 3 ++-
t/050_metaclasses/023_easy_init_meta.t | 12 +++++++++++-
2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/lib/Moose/Exporter.pm b/lib/Moose/Exporter.pm
index 0e07062..095db96 100644
--- a/lib/Moose/Exporter.pm
+++ b/lib/Moose/Exporter.pm
@@ -513,7 +513,8 @@ sub _make_init_meta {
shift;
my %options = @_;
- return unless Class::MOP::class_of( $options{for_class} );
+ Moose->init_meta( %options )
+ unless Class::MOP::class_of( $options{for_class} );
Moose::Util::MetaRole::apply_metaclass_roles(
for_class => $options{for_class},
diff --git a/t/050_metaclasses/023_easy_init_meta.t b/t/050_metaclasses/023_easy_init_meta.t
index 200d734..1fb90b1 100644
--- a/t/050_metaclasses/023_easy_init_meta.t
+++ b/t/050_metaclasses/023_easy_init_meta.t
@@ -3,7 +3,7 @@
use strict;
use warnings;
-use Test::More tests => 13;
+use Test::More tests => 17;
use Test::Moose qw(does_ok);
{
@@ -45,6 +45,16 @@ use Test::Moose qw(does_ok);
}
{
+ package Foo3;
+ Foo::Exporter->import;
+
+ ::isa_ok('Foo3', 'Moose::Object');
+ ::isa_ok(Foo3->meta, 'Moose::Meta::Class');
+ ::does_ok(Foo3->meta, 'Foo::Trait::Class');
+ ::does_ok('Foo3', 'Foo::Role::Base');
+}
+
+{
package Foo::Exporter::WithMoose;
use Moose ();
use Moose::Exporter;
--
1.6.2.5