use Sub::Name 'subname';
use B 'svref_2object';
-our $VERSION = '0.06';
+our $VERSION = '0.12';
# Self-introspection
# Creation
-{
+#{
# Metaclasses are singletons, so we cache them here.
# there is no need to worry about destruction though
# because they should die only when the program dies.
# After all, do package definitions even get reaped?
- my %METAS;
+ my %METAS;
+
+ # means of accessing all the metaclasses that have
+ # been initialized thus far (for mugwumps obj browser)
+ sub get_all_metaclasses { %METAS }
+ sub get_all_metaclass_instances { values %METAS }
+ sub get_all_metaclass_names { keys %METAS }
sub initialize {
my $class = shift;
my $package_name = $options{':package'};
(defined $package_name && $package_name)
|| confess "You must pass a package name";
- return $METAS{$package_name} if exists $METAS{$package_name};
+ # NOTE:
+ # return the metaclass if we have it cached,
+ # and it is still defined (it has not been
+ # reaped by DESTROY yet, which can happen
+ # annoyingly enough during global destruction)
+ return $METAS{$package_name}
+ if exists $METAS{$package_name} && defined $METAS{$package_name};
$class = blessed($class) || $class;
# now create the metaclass
my $meta;
$class_name . "->meta => (" . (blessed($meta)) . ")";
}
}
-}
+#}
sub create {
my ($class, $package_name, $package_version, %options) = @_;
return $meta;
}
+{
+ # NOTE:
+ # this should be sufficient, if you have a
+ # use case where it is not, write a test and
+ # I will change it.
+ my $ANON_CLASS_SERIAL = 0;
+
+ sub create_anon_class {
+ my ($class, %options) = @_;
+ my $package_name = 'Class::MOP::Class::__ANON__::SERIAL::' . ++$ANON_CLASS_SERIAL;
+ return $class->create($package_name, '0.00', %options);
+ }
+}
+
## Attribute readers
# NOTE:
my ($class, %params) = @_;
my $instance = {};
foreach my $attr ($class->compute_all_applicable_attributes()) {
- my $init_arg = $attr->init_arg();
- # try to fetch the init arg from the %params ...
- my $val;
- $val = $params{$init_arg} if exists $params{$init_arg};
- # if nothing was in the %params, we can use the
- # attribute's default value (if it has one)
- $val ||= $attr->default($instance) if $attr->has_default();
- $instance->{$attr->name} = $val;
+ $attr->initialize_instance_slot($class, $instance, \%params);
}
return $instance;
}
sub superclasses {
my $self = shift;
+ no strict 'refs';
if (@_) {
my @supers = @_;
- @{$self->get_package_variable('@ISA')} = @supers;
+ @{$self->name . '::ISA'} = @supers;
}
- @{$self->get_package_variable('@ISA')};
+ @{$self->name . '::ISA'};
}
sub class_precedence_list {
# up otherwise. Yes, it's an ugly hack, better
# suggestions are welcome.
{ $self->name->isa('This is a test for circular inheritance') }
- # ... and no back to our regularly scheduled program
+ # ... and now back to our regularly scheduled program
(
$self->name,
map {
- $self->initialize($_)->class_precedence_list()
+ ($METAS{$_} || $self->initialize($_))->class_precedence_list()
} $self->superclasses()
);
}
sub add_before_method_modifier {
my ($self, $method_name, $method_modifier) = @_;
(defined $method_name && $method_name)
- || confess "You must pass in a method name";
- my $full_method_modifier_name = ($self->name . '::' . $method_name . ':before');
+ || confess "You must pass in a method name";
my $method = $fetch_and_prepare_method->($self, $method_name);
- $method->add_before_modifier(subname $full_method_modifier_name => $method_modifier);
+ $method->add_before_modifier(subname ':before' => $method_modifier);
}
sub add_after_method_modifier {
my ($self, $method_name, $method_modifier) = @_;
(defined $method_name && $method_name)
- || confess "You must pass in a method name";
- my $full_method_modifier_name = ($self->name . '::' . $method_name . ':after');
+ || confess "You must pass in a method name";
my $method = $fetch_and_prepare_method->($self, $method_name);
- $method->add_after_modifier(subname $full_method_modifier_name => $method_modifier);
+ $method->add_after_modifier(subname ':after' => $method_modifier);
}
sub add_around_method_modifier {
my ($self, $method_name, $method_modifier) = @_;
(defined $method_name && $method_name)
|| confess "You must pass in a method name";
- my $full_method_modifier_name = ($self->name . '::' . $method_name . ':around');
my $method = $fetch_and_prepare_method->($self, $method_name);
- $method->add_around_modifier(subname $full_method_modifier_name => $method_modifier);
+ $method->add_around_modifier(subname ':around' => $method_modifier);
}
+ # NOTE:
+ # the methods above used to be named like this:
+ # ${pkg}::${method}:(before|after|around)
+ # but this proved problematic when using one modifier
+ # to wrap multiple methods (something which is likely
+ # to happen pretty regularly IMO). So instead of naming
+ # it like this, I have chosen to just name them purely
+ # with their modifier names, like so:
+ # :(before|after|around)
+ # The fact is that in a stack trace, it will be fairly
+ # evident from the context what method they are attached
+ # to, and so don't need the fully qualified name.
}
sub alias_method {
my ($self, $attribute_name) = @_;
(defined $attribute_name && $attribute_name)
|| confess "You must define an attribute name";
- return $self->get_attribute_map->{$attribute_name}
- if $self->has_attribute($attribute_name);
+ # OPTIMIZATION NOTE:
+ # we used to say `if $self->has_attribute($attribute_name)`
+ # here, but since get_attribute is called so often, we
+ # eliminate the function call here
+ return $self->{'%:attributes'}->{$attribute_name}
+ if exists $self->{'%:attributes'}->{$attribute_name};
return;
}
sub get_attribute_list {
my $self = shift;
- keys %{$self->get_attribute_map};
+ # OPTIMIZATION NOTE:
+ # We don't use get_attribute_map here because
+ # we ask for the attribute list quite often
+ # in compute_all_applicable_attributes, so
+ # eliminating the function call helps
+ keys %{$self->{'%:attributes'}};
}
sub compute_all_applicable_attributes {
next if $seen_class{$class};
$seen_class{$class}++;
# fetch the meta-class ...
- my $meta = $self->initialize($class);
+ my $meta = ($METAS{$class} || $self->initialize($class));
foreach my $attr_name ($meta->get_attribute_list()) {
next if exists $seen_attr{$attr_name};
$seen_attr{$attr_name}++;
return @attrs;
}
+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}++;
+ # fetch the meta-class ...
+ my $meta = $self->initialize($class);
+ return $meta->get_attribute($attr_name)
+ if $meta->has_attribute($attr_name);
+ }
+ return;
+}
+
# Class attributes
sub add_package_variable {
*{$self->name . '::' . $name} = $initial_value;
}
else {
- eval $sigil . $self->name . '::' . $name;
- confess "Could not create package variable ($variable) because : $@" if $@;
+ my $e;
+ {
+ # NOTE:
+ # We HAVE to localize $@ or all
+ # hell breaks loose. It is not
+ # good, believe me, not good.
+ local $@;
+ eval $sigil . $self->name . '::' . $name;
+ $e = $@ if $@;
+ }
+ confess "Could not create package variable ($variable) because : $e" if $e;
}
}
(defined $variable && $variable =~ /^[\$\@\%]/)
|| confess "variable name does not have a sigil";
my ($sigil, $name) = ($variable =~ /^(.)(.*)$/);
- no strict 'refs';
- # try to fetch it first,.. see what happens
- my $ref = eval '\\' . $sigil . $self->name . '::' . $name;
- confess "Could not get the package variable ($variable) because : $@" if $@;
+ my ($ref, $e);
+ {
+ # NOTE:
+ # We HAVE to localize $@ or all
+ # hell breaks loose. It is not
+ # good, believe me, not good.
+ local $@;
+ $ref = eval '\\' . $sigil . $self->name . '::' . $name;
+ $e = $@ if $@;
+ }
+ confess "Could not get the package variable ($variable) because : $e" if $e;
# if we didn't die, then we can return it
return $ref;
}
=head1 SYNOPSIS
+ # assuming that class Foo
+ # has been defined, you can
+
# use this for introspection ...
# add a method to Foo ...
into it's metaclass. This will allow this class to reap all the benifits
of the MOP when subclassing it.
+=item B<get_all_metaclasses>
+
+This will return an hash of all the metaclass instances that have
+been cached by B<Class::MOP::Class> keyed by the package name.
+
+=item B<get_all_metaclass_instances>
+
+This will return an array of all the metaclass instances that have
+been cached by B<Class::MOP::Class>.
+
+=item B<get_all_metaclass_names>
+
+This will return an array of all the metaclass names that have
+been cached by B<Class::MOP::Class>.
+
=back
=head2 Class construction
C<$package_version>, C<@superclasses>, C<%methods> and C<%attributes>
to it.
+=item B<create_anon_class (superclasses =E<gt> ?@superclasses,
+ methods =E<gt> ?%methods,
+ attributes =E<gt> ?%attributes)>
+
+This will create an anonymous class, it works much like C<create> but
+it does not need a C<$package_name>. Instead it will create a suitably
+unique package name for you to stash things into.
+
=item B<initialize ($package_name)>
This initializes and returns returns a B<Class::MOP::Class> object
that same information is discoverable through the attribute
meta-object itself.
+=item B<find_attribute_by_name ($attr_name)>
+
+This method will traverse the inheritance heirachy and find the
+first attribute whose name matches C<$attr_name>, then return it.
+It will return undef if nothing is found.
+
=back
=head2 Package Variables