use MRO::Compat;
use Carp 'confess';
-use Scalar::Util 'weaken';
-
-BEGIN {
- local $@;
- eval {
- require Sub::Name;
- Sub::Name->import(qw(subname));
- 1
- } or eval 'sub subname { $_[1] }';
-
- # this is either part of core or set up appropriately by MRO::Compat
- *check_package_cache_flag = \&mro::get_pkg_gen;
-
- eval {
- require Devel::GlobalDestruction;
- Devel::GlobalDestruction->import("in_global_destruction");
- 1;
- } or *in_global_destruction = sub () { !1 };
-}
+use Scalar::Util 'weaken', 'reftype';
use Class::MOP::Class;
*HAVE_ISAREV = defined(&mro::get_isarev)
? sub () { 1 }
: sub () { 1 };
+
+ # this is either part of core or set up appropriately by MRO::Compat
+ *check_package_cache_flag = \&mro::get_pkg_gen;
}
-our $VERSION = '0.65';
+our $VERSION = '0.78';
our $XS_VERSION = $VERSION;
$VERSION = eval $VERSION;
our $AUTHORITY = 'cpan:STEVAN';
-
-# after that everything is loaded, if we're allowed try to load faster XS
-# versions of various things
+
_try_load_xs() or _load_pure_perl();
sub _try_load_xs {
# for some reason
local $^W = 0;
__PACKAGE__->XSLoader::load($XS_VERSION);
+
+ require Sub::Name;
+ Sub::Name->import(qw(subname));
+
+ require Devel::GlobalDestruction;
+ Devel::GlobalDestruction->import("in_global_destruction");
+
+ *USING_XS = sub () { 1 };
};
$@;
};
sub _load_pure_perl {
require Sub::Identify;
Sub::Identify->import('get_code_info');
+
+ *subname = sub { $_[1] };
+ *in_global_destruction = sub () { !1 };
+
+ *USING_XS = sub () { 0 };
}
# because I don't yet see a good reason to do so.
}
-sub load_class {
- my $class = shift;
+sub load_first_existing_class {
+ my @classes = @_
+ or return;
- unless ( _is_valid_class_name($class) ) {
- my $display = defined($class) ? $class : 'undef';
- confess "Invalid class name ($display)";
+ foreach my $class (@classes) {
+ unless ( _is_valid_class_name($class) ) {
+ my $display = defined($class) ? $class : 'undef';
+ confess "Invalid class name ($display)";
+ }
}
- # if the class is not already loaded in the symbol table..
- unless (is_class_loaded($class)) {
- # require it
- my $e = do { local $@; eval "require $class"; $@ };
- confess "Could not load class ($class) because : $e" if $e;
+ my $found;
+ my %exceptions;
+ for my $class (@classes) {
+ my $e = _try_load_one_class($class);
+
+ if ($e) {
+ $exceptions{$class} = $e;
+ }
+ else {
+ $found = $class;
+ last;
+ }
}
- get_metaclass_by_name($class) || $class if defined wantarray;
+ return $found if $found;
+
+ confess join(
+ "\n",
+ map {
+ sprintf(
+ "Could not load class (%s) because : %s", $_,
+ $exceptions{$_}
+ )
+ } @classes
+ );
+}
+
+sub _try_load_one_class {
+ my $class = shift;
+
+ return if is_class_loaded($class);
+
+ my $file = $class . '.pm';
+ $file =~ s{::}{/}g;
+
+ return do {
+ local $@;
+ eval { require($file) };
+ $@;
+ };
+}
+
+sub load_class {
+ my $class = load_first_existing_class($_[0]);
+ return get_metaclass_by_name($class) || $class;
}
sub _is_valid_class_name {
sub is_class_loaded {
my $class = shift;
- return 0 if ref($class) || !defined($class) || !length($class);
+ return 0 unless _is_valid_class_name($class);
# walk the symbol table tree to avoid autovififying
# \*{${main::}{"Foo::"}} == \*main::Foo::
$pack = \*{${$$pack}{"${part}::"}};
}
- # check for $VERSION or @ISA
- return 1 if exists ${$$pack}{VERSION}
- && defined *{${$$pack}{VERSION}}{SCALAR};
+ # We used to check in the package stash, but it turns out that
+ # *{${$$package}{VERSION}{SCALAR}} can end up pointing to a
+ # reference to undef. It looks
+
+ my $version = do {
+ no strict 'refs';
+ ${$class . '::VERSION'};
+ };
+
+ return 1 if ! ref $version && defined $version;
+ # Sometimes $VERSION ends up as a reference to undef (weird)
+ return 1 if ref $version && reftype $version eq 'SCALAR' && defined ${$version};
+
return 1 if exists ${$$pack}{ISA}
&& defined *{${$$pack}{ISA}}{ARRAY};
);
Class::MOP::Class->meta->add_attribute(
+ Class::MOP::Attribute->new('wrapped_method_metaclass' => (
+ reader => {
+ # NOTE:
+ # we just alias the original method
+ # rather than re-produce it here
+ 'wrapped_method_metaclass' => \&Class::MOP::Class::wrapped_method_metaclass
+ },
+ default => 'Class::MOP::Method::Wrapped',
+ ))
+);
+
+Class::MOP::Class->meta->add_attribute(
Class::MOP::Attribute->new('instance_metaclass' => (
reader => {
# NOTE: we need to do this in order
);
Class::MOP::Attribute->meta->add_attribute(
+ Class::MOP::Attribute->new('definition_context' => (
+ reader => { 'definition_context' => \&Class::MOP::Attribute::definition_context },
+ ))
+);
+
+Class::MOP::Attribute->meta->add_attribute(
Class::MOP::Attribute->new('writer' => (
reader => { 'writer' => \&Class::MOP::Attribute::writer },
predicate => { 'has_writer' => \&Class::MOP::Attribute::has_writer },
))
);
+Class::MOP::Method->meta->add_attribute(
+ Class::MOP::Attribute->new('original_method' => (
+ reader => { 'original_method' => \&Class::MOP::Method::original_method },
+ writer => { '_set_original_method' => \&Class::MOP::Method::_set_original_method },
+ ))
+);
+
Class::MOP::Method->meta->add_method('clone' => sub {
my $self = shift;
- $self->meta->clone_object($self, @_);
+ my $clone = $self->meta->clone_object($self, @_);
+ $clone->_set_original_method($self);
+ return $clone;
});
## --------------------------------------------------------
))
);
+Class::MOP::Method::Generated->meta->add_attribute(
+ Class::MOP::Attribute->new('definition_context' => (
+ reader => { 'definition_context' => \&Class::MOP::Method::Generated::definition_context },
+ ))
+);
+
## --------------------------------------------------------
## Class::MOP::Method::Accessor
## --------------------------------------------------------
## Now close all the Class::MOP::* classes
-# NOTE:
-# we don't need to inline the
-# constructors or the accessors
-# this only lengthens the compile
-# time of the MOP, and gives us
-# no actual benefits.
+# NOTE: we don't need to inline the the accessors this only lengthens
+# the compile time of the MOP, and gives us no actual benefits.
$_->meta->make_immutable(
inline_constructor => 1,
Whether or not C<mro> provides C<get_isarev>, a much faster way to get all the
subclasses of a certain class.
+=item I<USING_XS>
+
+Whether or not the running C<Class::MOP> is using its XS version.
+
=back
=head2 Utility functions
+Note that these are all called as B<functions, not methods>.
+
=over 4
=item B<load_class ($class_name)>
=item B<check_package_cache_flag ($pkg)>
+B<NOTE: DO NOT USE THIS FUNCTION, IT IS FOR INTERNAL USE ONLY!>
+
This will return an integer that is managed by C<Class::MOP::Class>
to determine if a module's symbol table has been altered.
=item B<get_code_info ($code)>
+B<NOTE: DO NOT USE THIS FUNCTION, IT IS FOR INTERNAL USE ONLY!>
+
This function returns two values, the name of the package the C<$code>
is from and the name of the C<$code> itself. This is used by several
elements of the MOP to detemine where a given C<$code> reference is from.
=item B<in_global_destruction>
+B<NOTE: DO NOT USE THIS FUNCTION, IT IS FOR INTERNAL USE ONLY!>
+
If L<Devel::GlobalDestruction> is available, this returns true under global
destruction.
Otherwise it's a constant returning false.
+=item B<load_first_existing_class ($class_name, [$class_name, ...])>
+
+B<NOTE: DO NOT USE THIS FUNCTION, IT IS FOR INTERNAL USE ONLY!>
+
+Given a list of class names, this function will attempt to load each
+one in turn.
+
+If it finds a class it can load, it will return that class' name.
+If none of the classes can be loaded, it will throw an exception.
+
=back
=head2 Metaclass cache functions
-Class::MOP holds a cache of metaclasses, the following are functions
+Class::MOP holds a cache of metaclasses. The following are functions
(B<not methods>) which can be used to access that cache. It is not
-recommended that you mess with this, bad things could happen. But if
-you are brave and willing to risk it, go for it.
+recommended that you mess with these. Bad things could happen, but if
+you are brave and willing to risk it: go for it!
=over 4
=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.
+This will return a 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
+This will return a list 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
+This will return a list of all the metaclass names that have
been cached by B<Class::MOP::Class>.
=item B<get_metaclass_by_name ($name)>
-This will return a cached B<Class::MOP::Class> instance of nothing
-if no metaclass exist by that C<$name>.
+This will return a cached B<Class::MOP::Class> instance, or nothing
+if no metaclass exists with that C<$name>.
=item B<store_metaclass_by_name ($name, $meta)>
=item B<weaken_metaclass ($name)>
-In rare cases it is desireable to store a weakened reference in
-the metaclass cache. This function will weaken the reference to
-the metaclass stored in C<$name>.
+In rare cases (e.g. anonymous metaclasses) it is desirable to
+store a weakened reference in the metaclass cache. This
+function will weaken the reference to the metaclass stored
+in C<$name>.
=item B<does_metaclass_exist ($name)>
This will return true of there exists a metaclass stored in the
-C<$name> key and return false otherwise.
+C<$name> key, and return false otherwise.
=item B<remove_metaclass_by_name ($name)>
-This will remove a the metaclass stored in the C<$name> key.
+This will remove the metaclass stored in the C<$name> key.
=back
Brandon (blblack) Black
+Florian (rafl) Ragwitz
+
Guillermo (groditi) Roditi
Matt (mst) Trout