use Class::MOP::Method::Wrapped;
use Carp 'confess';
-use Scalar::Util 'blessed', 'reftype', 'weaken', 'refaddr';
+use Scalar::Util 'blessed', 'reftype', 'weaken';
use Sub::Name 'subname';
use B 'svref_2object';
-our $VERSION = '0.22';
+our $VERSION = '0.24';
our $AUTHORITY = 'cpan:STEVAN';
use base 'Class::MOP::Module';
my $package_name = shift;
(defined $package_name && $package_name && !blessed($package_name))
|| confess "You must pass a package name and it cannot be blessed";
+ if (defined(my $meta = Class::MOP::get_metaclass_by_name($package_name))) {
+ return $meta;
+ }
$class->construct_class_instance('package' => $package_name, @_);
}
# and it is still defined (it has not been
# reaped by DESTROY yet, which can happen
# annoyingly enough during global destruction)
- return Class::MOP::get_metaclass_by_name($package_name)
- if Class::MOP::does_metaclass_exist($package_name);
+
+ if (defined(my $meta = Class::MOP::get_metaclass_by_name($package_name))) {
+ return $meta;
+ }
# NOTE:
# we need to deal with the possibility
# now create the metaclass
my $meta;
- if ($class =~ /^Class::MOP::Class$/) {
+ if ($class eq 'Class::MOP::Class') {
no strict 'refs';
$meta = bless {
# inherited from Class::MOP::Package
return if blessed($self) eq 'Class::MOP::Class' &&
$self->instance_metaclass eq 'Class::MOP::Instance';
- my @class_list = $self->class_precedence_list;
+ my @class_list = $self->linearized_isa;
shift @class_list; # shift off $self->name
foreach my $class_name (@class_list) {
@{$self->get_package_symbol('@ISA')} = @supers;
# NOTE:
# we need to check the metaclass
- # compatability here so that we can
+ # compatibility here so that we can
# be sure that the superclass is
# not potentially creating an issues
# we don't know about
@{$self->get_package_symbol('@ISA')};
}
+sub linearized_isa {
+ my %seen;
+ grep { !($seen{$_}++) } (shift)->class_precedence_list
+}
+
sub class_precedence_list {
my $self = shift;
# NOTE:
- # We need to check for ciruclar inheirtance here.
+ # We need to check for circular inheritance here.
# This will do nothing if all is well, and blow
# up otherwise. Yes, it's an ugly hack, better
# suggestions are welcome.
my ($self, $method_name) = @_;
(defined $method_name && $method_name)
|| confess "You must define a method name to find";
- # keep a record of what we have seen
- # here, this will handle all the
- # inheritence issues because we are
- # using the &class_precedence_list
- my %seen_class;
- my @cpl = $self->class_precedence_list();
- foreach my $class (@cpl) {
- next if $seen_class{$class};
- $seen_class{$class}++;
+ foreach my $class ($self->linearized_isa) {
# fetch the meta-class ...
my $meta = $self->initialize($class);
return $meta->get_method($method_name)
sub compute_all_applicable_methods {
my $self = shift;
- my @methods;
- # keep a record of what we have seen
- # here, this will handle all the
- # inheritence issues because we are
- # using the &class_precedence_list
- my (%seen_class, %seen_method);
- foreach my $class ($self->class_precedence_list()) {
- next if $seen_class{$class};
- $seen_class{$class}++;
+ my (@methods, %seen_method);
+ foreach my $class ($self->linearized_isa) {
# fetch the meta-class ...
my $meta = $self->initialize($class);
foreach my $method_name ($meta->get_method_list()) {
(defined $method_name && $method_name)
|| confess "You must define a method name to find";
my @methods;
- # keep a record of what we have seen
- # here, this will handle all the
- # inheritence issues because we are
- # using the &class_precedence_list
- my %seen_class;
- foreach my $class ($self->class_precedence_list()) {
- next if $seen_class{$class};
- $seen_class{$class}++;
+ foreach my $class ($self->linearized_isa) {
# fetch the meta-class ...
my $meta = $self->initialize($class);
push @methods => {
my ($self, $method_name) = @_;
(defined $method_name && $method_name)
|| confess "You must define a method name to find";
- # keep a record of what we have seen
- # here, this will handle all the
- # inheritence issues because we are
- # using the &class_precedence_list
- my %seen_class;
- my @cpl = $self->class_precedence_list();
+ my @cpl = $self->linearized_isa;
shift @cpl; # discard ourselves
foreach my $class (@cpl) {
- next if $seen_class{$class};
- $seen_class{$class}++;
# fetch the meta-class ...
my $meta = $self->initialize($class);
return $meta->get_method($method_name)
sub compute_all_applicable_attributes {
my $self = shift;
- my @attrs;
- # keep a record of what we have seen
- # here, this will handle all the
- # inheritence issues because we are
- # using the &class_precedence_list
- my (%seen_class, %seen_attr);
- foreach my $class ($self->class_precedence_list()) {
- next if $seen_class{$class};
- $seen_class{$class}++;
+ my (@attrs, %seen_attr);
+ foreach my $class ($self->linearized_isa) {
# fetch the meta-class ...
my $meta = $self->initialize($class);
foreach my $attr_name ($meta->get_attribute_list()) {
sub find_attribute_by_name {
my ($self, $attr_name) = @_;
- # keep a record of what we have seen
- # here, this will handle all the
- # inheritence issues because we are
- # using the &class_precedence_list
- my %seen_class;
- foreach my $class ($self->class_precedence_list()) {
- next if $seen_class{$class};
- $seen_class{$class}++;
+ foreach my $class ($self->linearized_isa) {
# fetch the meta-class ...
my $meta = $self->initialize($class);
return $meta->get_attribute($attr_name)
sub is_mutable { 1 }
sub is_immutable { 0 }
-#Why I changed this (groditi)
-# - One Metaclass may have many Classes through many Metaclass instances
-# - One Metaclass should only have one Immutable Metaclass instance
-# - Each Class may have different Immutabilizing options
-# - Therefore each Metaclass instance may have different Immutabilizing options
-# - We need to store one Immutable Metaclass instance per Metaclass
-# - We need to store one set of Immutable Metaclass options per Class
-# - Upon make_mutable we may delete the Immutabilizing options
-# - We could clean the immutable Metaclass instance when there is no more
-# immutable Classes with this Metaclass, but we can also keep it in case
-# another class with this same Metaclass becomes immutable. It is a case
-# of trading of storing an instance to avoid unnecessary instantiations of
-# Immutable Metaclass instances. You may view this as a memory leak, however
-# Because we have few Metaclasses, in practice it seems acceptable
-# - To allow Immutable Metaclass instances to be cleaned up we could weaken
-# the reference stored in $IMMUTABLE_METACLASSES{$class} and ||= should DWIM
+# NOTE:
+# Why I changed this (groditi)
+# - One Metaclass may have many Classes through many Metaclass instances
+# - One Metaclass should only have one Immutable Transformer instance
+# - Each Class may have different Immutabilizing options
+# - Therefore each Metaclass instance may have different Immutabilizing options
+# - We need to store one Immutable Transformer instance per Metaclass
+# - We need to store one set of Immutable Transformer options per Class
+# - Upon make_mutable we may delete the Immutabilizing options
+# - We could clean the immutable Transformer instance when there is no more
+# immutable Classes of that type, but we can also keep it in case
+# another class with this same Metaclass becomes immutable. It is a case
+# of trading of storing an instance to avoid unnecessary instantiations of
+# Immutable Transformers. You may view this as a memory leak, however
+# Because we have few Metaclasses, in practice it seems acceptable
+# - To allow Immutable Transformers instances to be cleaned up we could weaken
+# the reference stored in $IMMUTABLE_TRANSFORMERS{$class} and ||= should DWIM
{
- # NOTE:
- # the immutable version of a
- # particular metaclass is
- # really class-level data so
- # we don't want to regenerate
- # it any more than we need to
- my %IMMUTABLE_METACLASSES;
+ my %IMMUTABLE_TRANSFORMERS;
my %IMMUTABLE_OPTIONS;
sub make_immutable {
my $self = shift;
my %options = @_;
- my $class = blessed $self || $self;;
-
- $IMMUTABLE_METACLASSES{$class} ||= Class::MOP::Immutable->new($self, {
- read_only => [qw/superclasses/],
- cannot_call => [qw/
- add_method
- alias_method
- remove_method
- add_attribute
- remove_attribute
- add_package_symbol
- remove_package_symbol
- /],
- memoize => {
- class_precedence_list => 'ARRAY',
- compute_all_applicable_attributes => 'ARRAY',
- get_meta_instance => 'SCALAR',
- get_method_map => 'SCALAR',
- }
- });
-
- $IMMUTABLE_METACLASSES{$class}->make_metaclass_immutable($self, %options);
- $IMMUTABLE_OPTIONS{refaddr $self} =
- { %options, IMMUTABLE_METACLASS => $IMMUTABLE_METACLASSES{$class} };
+ my $class = blessed $self || $self;
+
+ $IMMUTABLE_TRANSFORMERS{$class} ||= $self->create_immutable_transformer;
+ my $transformer = $IMMUTABLE_TRANSFORMERS{$class};
+
+ $transformer->make_metaclass_immutable($self, %options);
+ $IMMUTABLE_OPTIONS{$self->name} =
+ { %options, IMMUTABLE_TRANSFORMER => $transformer };
if( exists $options{debug} && $options{debug} ){
- print STDERR "# of Metaclass options: ", keys %IMMUTABLE_OPTIONS;
- print STDERR "# of Immutable metaclasses: ", keys %IMMUTABLE_METACLASSES;
+ print STDERR "# of Metaclass options: ", keys %IMMUTABLE_OPTIONS;
+ print STDERR "# of Immutable transformers: ", keys %IMMUTABLE_TRANSFORMERS;
}
}
sub make_mutable{
my $self = shift;
return if $self->is_mutable;
- my $options = delete $IMMUTABLE_OPTIONS{refaddr $self};
- my $immutable_metaclass = delete $options->{IMMUTABLE_METACLASS};
- $immutable_metaclass->make_metaclass_mutable($self, %$options);
+ my $options = delete $IMMUTABLE_OPTIONS{$self->name};
+ confess "unable to find immutabilizing options" unless ref $options;
+ my $transformer = delete $options->{IMMUTABLE_TRANSFORMER};
+ $transformer->make_metaclass_mutable($self, %$options);
}
+}
+sub create_immutable_transformer {
+ my $self = shift;
+ my $class = Class::MOP::Immutable->new($self, {
+ read_only => [qw/superclasses/],
+ cannot_call => [qw/
+ add_method
+ alias_method
+ remove_method
+ add_attribute
+ remove_attribute
+ add_package_symbol
+ remove_package_symbol
+ /],
+ memoize => {
+ class_precedence_list => 'ARRAY',
+ linearized_isa => 'ARRAY',
+ compute_all_applicable_attributes => 'ARRAY',
+ get_meta_instance => 'SCALAR',
+ get_method_map => 'SCALAR',
+ }
+ });
+ return $class;
}
1;
in which method dispatch will be done. This is similair to
what B<Class::ISA::super_path> does, but we don't remove duplicate names.
+=item B<linearized_isa>
+
+This returns a list based on C<class_precedence_list> but with all
+duplicates removed.
+
=back
=head2 Methods
This method will reverse tranforamtion upon the class which
made it immutable.
+=item B<create_immutable_transformer>
+
+Create a transformer suitable for making this class immutable
+
=back
=head1 AUTHORS