# Creation
sub initialize {
- my ( $class, @args ) = @_;
-
- unshift @args, 'package' if @args % 2 == 1;
+ my $class = shift;
- my (%options) = @args;
- my $package_name = $options{package};
+ my $package_name;
+
+ if ( @_ % 2 ) {
+ $package_name = shift;
+ } else {
+ my %options = @_;
+ $package_name = $options{package};
+ }
- (defined $package_name && $package_name && !blessed($package_name))
+ (defined $package_name && $package_name && !ref($package_name))
|| confess "You must pass a package name and it cannot be blessed";
return Class::MOP::get_metaclass_by_name($package_name)
- || $class->construct_class_instance(%options);
+ || $class->construct_class_instance(package => $package_name, @_);
}
sub reinitialize {
# normal &construct_instance.
sub construct_class_instance {
my $class = shift;
- my %options = @_;
- my $package_name = $options{'package'};
+ my $options = @_ == 1 ? $_[0] : {@_};
+ my $package_name = $options->{package};
(defined $package_name && $package_name)
|| confess "You must pass a package name";
# NOTE:
# we need to deal with the possibility
# of class immutability here, and then
# get the name of the class appropriately
- $class = (blessed($class)
+ $class = (ref($class)
? ($class->is_immutable
? $class->get_mutable_metaclass_name()
- : blessed($class))
+ : ref($class))
: $class);
# now create the metaclass
my $meta;
if ($class eq 'Class::MOP::Class') {
no strict 'refs';
- $meta = bless {
- # inherited from Class::MOP::Package
- 'package' => $package_name,
-
- # NOTE:
- # since the following attributes will
- # actually be loaded from the symbol
- # table, and actually bypass the instance
- # entirely, we can just leave these things
- # listed here for reference, because they
- # should not actually have a value associated
- # with the slot.
- 'namespace' => \undef,
- # inherited from Class::MOP::Module
- 'version' => \undef,
- 'authority' => \undef,
- # defined in Class::MOP::Class
- 'superclasses' => \undef,
-
- 'methods' => {},
- 'attributes' => {},
- 'attribute_metaclass' => $options{'attribute_metaclass'} || 'Class::MOP::Attribute',
- 'method_metaclass' => $options{'method_metaclass'} || 'Class::MOP::Method',
- 'instance_metaclass' => $options{'instance_metaclass'} || 'Class::MOP::Instance',
-
- ## uber-private variables
- # NOTE:
- # this starts out as undef so that
- # we can tell the first time the
- # methods are fetched
- # - SL
- '_package_cache_flag' => undef,
- '_meta_instance' => undef,
- } => $class;
+ $meta = $class->_new($options)
}
else {
# NOTE:
# it is safe to use meta here because
# class will always be a subclass of
# Class::MOP::Class, which defines meta
- $meta = $class->meta->construct_instance(%options)
+ $meta = $class->meta->construct_instance($options)
}
# and check the metaclass compatibility
$meta;
}
+sub _new {
+ my $class = shift;
+ my $options = @_ == 1 ? $_[0] : {@_};
+
+ bless {
+ # inherited from Class::MOP::Package
+ 'package' => $options->{package},
+
+ # NOTE:
+ # since the following attributes will
+ # actually be loaded from the symbol
+ # table, and actually bypass the instance
+ # entirely, we can just leave these things
+ # listed here for reference, because they
+ # should not actually have a value associated
+ # with the slot.
+ 'namespace' => \undef,
+ # inherited from Class::MOP::Module
+ 'version' => \undef,
+ 'authority' => \undef,
+ # defined in Class::MOP::Class
+ 'superclasses' => \undef,
+
+ 'methods' => {},
+ 'attributes' => {},
+ 'attribute_metaclass' => $options->{'attribute_metaclass'} || 'Class::MOP::Attribute',
+ 'method_metaclass' => $options->{'method_metaclass'} || 'Class::MOP::Method',
+ 'instance_metaclass' => $options->{'instance_metaclass'} || 'Class::MOP::Instance',
+ }, $class;
+}
+
sub reset_package_cache_flag { (shift)->{'_package_cache_flag'} = undef }
sub update_package_cache_flag {
my $self = shift;
my $self = shift;
# this is always okay ...
- return if blessed($self) eq 'Class::MOP::Class' &&
+ return if ref($self) eq 'Class::MOP::Class' &&
$self->instance_metaclass eq 'Class::MOP::Instance';
my @class_list = $self->linearized_isa;
# get the name of the class appropriately
my $meta_type = ($meta->is_immutable
? $meta->get_mutable_metaclass_name()
- : blessed($meta));
+ : ref($meta));
($self->isa($meta_type))
- || confess $self->name . "->meta => (" . (blessed($self)) . ")" .
+ || confess $self->name . "->meta => (" . (ref($self)) . ")" .
" is not compatible with the " .
$class_name . "->meta => (" . ($meta_type) . ")";
# NOTE:
sub is_anon_class {
my $self = shift;
no warnings 'uninitialized';
- $self->name =~ /^$ANON_CLASS_PREFIX/ ? 1 : 0;
+ $self->name =~ /^$ANON_CLASS_PREFIX/;
}
sub create_anon_class {
# really need to be handled explicitly
sub DESTROY {
my $self = shift;
+
+ return if Class::MOP::in_global_destruction; # it'll happen soon anyway and this just makes things more complicated
+
no warnings 'uninitialized';
return unless $self->name =~ /^$ANON_CLASS_PREFIX/;
my ($serial_id) = ($self->name =~ /^$ANON_CLASS_PREFIX(\d+)/);
# FIXME totally lame
$meta->add_method('meta' => sub {
- $class->initialize(blessed($_[0]) || $_[0]);
+ $class->initialize(ref($_[0]) || $_[0]);
});
$meta->superclasses(@{$options{superclasses}})
sub get_method_map {
my $self = shift;
- if (defined $self->{'_package_cache_flag'} &&
- $self->{'_package_cache_flag'} == Class::MOP::check_package_cache_flag($self->name)) {
+ my $current = Class::MOP::check_package_cache_flag($self->name);
+
+ if (defined $self->{'_package_cache_flag'} && $self->{'_package_cache_flag'} == $current) {
return $self->{'methods'};
}
-
+
+ $self->{_package_cache_flag} = $current;
+
my $map = $self->{'methods'};
my $class_name = $self->name;
$map->{$symbol} = $method_metaclass->wrap(
$code,
- package_name => $class_name,
- name => $symbol,
+ associated_metaclass => $self,
+ package_name => $class_name,
+ name => $symbol,
);
}
}
sub construct_instance {
- my ($class, %params) = @_;
+ my $class = shift;
+ my $params = @_ == 1 ? $_[0] : {@_};
my $meta_instance = $class->get_meta_instance();
my $instance = $meta_instance->create_instance();
foreach my $attr ($class->compute_all_applicable_attributes()) {
- $attr->initialize_instance_slot($meta_instance, $instance, \%params);
+ $attr->initialize_instance_slot($meta_instance, $instance, $params);
}
# NOTE:
# this will only work for a HASH instance type
sub get_meta_instance {
my $self = shift;
- $self->{'_meta_instance'} ||= $self->instance_metaclass->new(
+ $self->{'_meta_instance'} ||= $self->create_meta_instance();
+}
+
+sub create_meta_instance {
+ my $self = shift;
+
+ my $instance = $self->instance_metaclass->new(
associated_metaclass => $self,
attributes => [ $self->compute_all_applicable_attributes() ],
);
+
+ $self->add_meta_instance_dependencies()
+ if $instance->is_dependent_on_superclasses();
+
+ return $instance;
}
sub clone_object {
my $class = shift;
my $instance = shift;
(blessed($instance) && $instance->isa($class->name))
- || confess "You must pass an instance of the metaclass (" . $class->name . "), not ($instance)";
+ || confess "You must pass an instance of the metaclass (" . (ref $class ? $class->name : $class) . "), not ($instance)";
# NOTE:
# we need to protect the integrity of the
$old_metaclass = $instance->meta;
}
else {
- $old_metaclass = $self->initialize(blessed($instance));
+ $old_metaclass = $self->initialize(ref($instance));
}
my $meta_instance = $self->get_meta_instance();
# not potentially creating an issues
# we don't know about
$self->check_metaclass_compatability();
+ $self->update_meta_instance_dependencies();
}
@{$self->get_package_symbol($var_spec)};
}
)
);
}
+
+ $method->attach_to_class($self);
+
$self->get_method_map->{$method_name} = $method;
my $full_method_name = ($self->name . '::' . $method_name);
{ sigil => '&', type => 'CODE', name => $method_name },
Class::MOP::subname($full_method_name => $body)
);
- $self->update_package_cache_flag;
+
+ $self->update_package_cache_flag; # still valid, since we just added the method to the map, and if it was invalid before that then get_method_map updated it
}
{
$self->add_package_symbol(
{ sigil => '&', type => 'CODE', name => $method_name } => $body
);
- $self->update_package_cache_flag;
}
sub has_method {
$self->remove_package_symbol(
{ sigil => '&', type => 'CODE', name => $method_name }
);
-
- $self->update_package_cache_flag;
+
+ $removed_method->detach_from_class if $removed_method;
+
+ $self->update_package_cache_flag; # still valid, since we just removed the method from the map
return $removed_method;
}
return;
}
-sub compute_all_applicable_methods {
+sub get_all_methods {
my $self = shift;
- 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()) {
- next if exists $seen_method{$method_name};
- $seen_method{$method_name}++;
- push @methods => {
- name => $method_name,
- class => $class,
- code => $meta->get_method($method_name)
- };
- }
- }
- return @methods;
+ my %methods = map { %{ $self->initialize($_)->get_method_map } } reverse $self->linearized_isa;
+ return values %methods;
+}
+
+# compatibility
+sub compute_all_applicable_methods {
+ return map {
+ {
+ name => $_->name,
+ class => $_->package_name,
+ code => $_, # sigh, overloading
+ },
+ } shift->get_all_methods(@_);
}
sub find_all_methods_by_name {
return $attribute;
}
-sub invalidate_meta_instances {
+sub update_meta_instance_dependencies {
my $self = shift;
-
- my @metas = ( $self, map { Class::MOP::Class->initialize($_) } $self->subclasses );
- $_->invalidate_meta_instance() for @metas;
+ if ( $self->{meta_instance_dependencies} ) {
+ return $self->add_meta_instance_dependencies;
+ }
+}
+
+sub add_meta_instance_dependencies {
+ my $self = shift;
+
+ $self->remove_meta_instance_depdendencies;
+
+ my @attrs = $self->compute_all_applicable_attributes();
+
+ my %seen;
+ my @classes = grep { not $seen{$_->name}++ } map { $_->associated_class } @attrs;
+
+ foreach my $class ( @classes ) {
+ $class->add_dependent_meta_instance($self);
+ }
+
+ $self->{meta_instance_dependencies} = \@classes;
+}
+
+sub remove_meta_instance_depdendencies {
+ my $self = shift;
+
+ if ( my $classes = delete $self->{meta_instance_dependencies} ) {
+ foreach my $class ( @$classes ) {
+ $class->remove_dependent_meta_instance($self);
+ }
+
+ return $classes;
+ }
+
+ return;
+
+}
+
+sub add_dependent_meta_instance {
+ my ( $self, $metaclass ) = @_;
+ push @{ $self->{dependent_meta_instances} }, $metaclass;
+}
+
+sub remove_dependent_meta_instance {
+ my ( $self, $metaclass ) = @_;
+ my $name = $metaclass->name;
+ @$_ = grep { $_->name ne $name } @$_ for $self->{dependent_meta_instances};
+}
+
+sub invalidate_meta_instances {
+ my $self = shift;
+ $_->invalidate_meta_instance() for $self, @{ $self->{dependent_meta_instances} };
}
sub invalidate_meta_instance {
my ($self, $attribute_name) = @_;
(defined $attribute_name && $attribute_name)
|| confess "You must define an attribute name";
- exists $self->get_attribute_map->{$attribute_name} ? 1 : 0;
+ exists $self->get_attribute_map->{$attribute_name};
}
sub get_attribute {
keys %{$self->get_attribute_map};
}
+sub get_all_attributes {
+ shift->compute_all_applicable_attributes(@_);
+}
+
sub compute_all_applicable_attributes {
my $self = shift;
- 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()) {
- next if exists $seen_attr{$attr_name};
- $seen_attr{$attr_name}++;
- push @attrs => $meta->get_attribute($attr_name);
- }
- }
- return @attrs;
+ my %attrs = map { %{ $self->initialize($_)->get_attribute_map } } reverse $self->linearized_isa;
+ return values %attrs;
}
sub find_attribute_by_name {
sub get_immutable_transformer {
my $self = shift;
if( $self->is_mutable ){
- my $class = blessed $self || $self;
+ my $class = ref $self || $self;
return $IMMUTABLE_TRANSFORMERS{$class} ||= $self->create_immutable_transformer;
}
confess "unable to find transformer for immutable class"
Clears the package cache flag to announce to the internals that we need
to rebuild the method map.
+=item B<add_meta_instance_dependencies>
+
+Registers this class as dependent on its superclasses.
+
+Only superclasses from which this class inherits attributes will be added.
+
+=item B<remove_meta_instance_depdendencies>
+
+Unregisters this class from its superclasses.
+
+=item B<update_meta_instance_dependencies>
+
+Reregisters if necessary.
+
+=item B<add_dependent_meta_instance> $metaclass
+
+Registers the class as having a meta instance dependent on this class.
+
+=item B<remove_dependent_meta_instance> $metaclass
+
+Remove the class from the list of dependent classes.
+
=item B<invalidate_meta_instances>
-Clears the cached meta instance for this metaclass and all of its subclasses.
+Clears the cached meta instance for this metaclass and all of the registered
+classes with dependent meta instances.
Called by C<add_attribute> and C<remove_attribute> to recalculate the attribute
slots.
Returns an instance of L<Class::MOP::Instance> to be used in the construction
of a new instance of the class.
+=item B<create_meta_instance>
+
+Called by C<get_meta_instance> if necessary.
+
=item B<new_object (%params)>
This is a convience method for creating a new object of the class, and
including any inherited ones. If you want a list of all applicable
methods, use the C<compute_all_applicable_methods> method.
+=item B<get_all_methods>
+
+This will traverse the inheritance heirachy and return a list of all
+the applicable L<Class::MOP::Method> objects for this class.
+
=item B<compute_all_applicable_methods>
-This will return a list of all the methods names this class will
-respond to, taking into account inheritance. The list will be a list of
-HASH references, each one containing the following information; method
-name, the name of the class in which the method lives and a CODE
-reference for the actual method.
+Deprecated.
+
+This method returns a list of hashes describing the all the methods of the
+class.
+
+Use L<get_all_methods>, which is easier/better/faster. This method predates
+L<Class::MOP::Method>.
=item B<find_all_methods_by_name ($method_name)>
=item B<compute_all_applicable_attributes>
+=item B<get_all_attributes>
+
This will traverse the inheritance heirachy and return a list of all
-the applicable attributes for this class. It does not construct a
-HASH reference like C<compute_all_applicable_methods> because all
-that same information is discoverable through the attribute
-meta-object itself.
+the applicable L<Class::MOP::Attribute> objects for this class.
+
+C<get_all_attributes> is an alias for consistency with C<get_all_methods>.
=item B<find_attribute_by_name ($attr_name)>