- If you try to wrap/export a subroutine which doesn't actually exist,
Moose::Exporter will warn you about this. (doy)
+ * Moose::Meta::Class
+ - Warn when calling make_immutable on a class with mutable ancestors.
+ (doy)
0.89_01 Wed Sep 2, 2009
* Moose::Meta::Attribute
=back
+Moose now warns if you call C<make_immutable> for a class with mutable
+ancestors. This is dangerous because modifying a class after a subclass has
+been immutablized will lead to incorrect results in the subclass, due to
+inlining, caching, etc. This occasionally happens accidentally, when a class
+loads one of its subclasses in the middle of its class definition, so pointing
+out that this may cause issues should be helpful. Metaclasses (classes that
+inherit from L<Class::MOP::Object>) are currently exempt from this check, since
+at the moment we aren't very consistent about which metaclasses we immutablize.
+
=head1 Version 0.89_01
L<Moose::Meta::Attribute::Native> has been moved into the Moose core from
push @{$self->roles} => $role;
}
+sub make_immutable {
+ my $self = shift;
+
+ # we do this for metaclasses way too often to do this check for them
+ if (!$self->name->isa('Class::MOP::Object')) {
+ my @superclasses = grep { $_ ne 'Moose::Object' && $_ ne $self->name }
+ $self->linearized_isa;
+ for my $superclass (@superclasses) {
+ my $meta = Class::MOP::class_of($superclass);
+ next unless $meta && $meta->isa('Moose::Meta::Class');
+ next unless $meta->is_mutable;
+ Carp::cluck("Calling make_immutable on "
+ . $self->name
+ . ", which has a mutable ancestor ($superclass)");
+ last;
+ }
+ }
+ $self->SUPER::make_immutable(@_);
+}
+
sub role_applications {
my ($self) = @_;
__PACKAGE__->meta->add_attribute(
'squeegee' => ( accessor => 'squeegee' ) );
+ __PACKAGE__->meta->make_immutable(inline_constructor => 0);
+
package Old::Bucket::Nose;
# see http://www.moosefoundation.org/moose_facts.htm
}
lives_ok {
+ Foo->meta->make_immutable;
+} 'Foo->meta->make_immutable';
+
+is( Foo->meta->get_method('DESTROY')->package_name, 'Foo',
+ 'Foo has a DESTROY method in the Foo class (not inherited)' );
+
+lives_ok {
Bar->new();
} 'Bar->new()';
is( Bar->meta->get_method('DESTROY')->package_name, 'Bar',
'Bar has a DESTROY method in the Bar class (not inherited)' );
-
-lives_ok {
- Foo->meta->make_immutable;
-} 'Foo->meta->make_immutable';
-
-is( Foo->meta->get_method('DESTROY')->package_name, 'Foo',
- 'Foo has a DESTROY method in the Bar class (not inherited)' );
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Exception;
+use lib 't/lib';
+
+BEGIN {
+ eval "use Test::Output;";
+ plan skip_all => "Test::Output is required for this test" if $@;
+ plan tests => 4;
+}
+
+{
+ package Foo;
+ use Moose;
+}
+
+{
+ package Foo::Sub;
+ use Moose;
+ extends 'Foo';
+
+ ::stderr_like {
+ __PACKAGE__->meta->make_immutable
+ } qr/^Calling make_immutable on Foo::Sub, which has a mutable ancestor \(Foo\)/,
+ "warning when making a class with mutable ancestors immutable";
+}
+
+Foo->meta->make_immutable;
+
+{
+ package Foo::Sub2;
+ use Moose;
+ extends 'Foo';
+
+ ::stderr_is {
+ __PACKAGE__->meta->make_immutable
+ } '', "no warning when all ancestors are immutable";
+}
+
+{
+ package Foo::Sub3;
+ use Moose;
+ extends 'Foo';
+}
+
+{
+ package Foo::Sub3::Sub;
+ use Moose;
+ extends 'Foo::Sub3';
+}
+
+{
+ package Foo::Sub3::Sub::Sub;
+ use Moose;
+ extends 'Foo::Sub3::Sub';
+
+ ::stderr_like {
+ __PACKAGE__->meta->make_immutable
+ } qr/^Calling make_immutable on Foo::Sub3::Sub::Sub, which has a mutable ancestor \(Foo::Sub3::Sub\)/,
+ "warning when making a class with mutable ancestors immutable";
+}
+
+stderr_like {
+ require Recursive::Parent
+} qr/^Calling make_immutable on Recursive::Child, which has a mutable ancestor \(Recursive::Parent\)/,
+ "circular dependencies via use are caught properly";
--- /dev/null
+package Recursive::Child;
+use Moose;
+extends 'Recursive::Parent';
+
+has parent => (
+ is => 'ro',
+ isa => 'Recursive::Parent',
+);
+
+__PACKAGE__->meta->make_immutable;
+
+1;
--- /dev/null
+package Recursive::Parent;
+use Moose;
+
+use Recursive::Child;
+
+has child => (
+ is => 'ro',
+ isa => 'Maybe[Recursive::Child]',
+);
+
+__PACKAGE__->meta->make_immutable;
+
+1;