X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FClass%2FMOP.pm;h=f68ed297c3b6452361ad0d0872b9d05f408e4b66;hb=a007159dcf8bc57e8ca504cd49e0b6034962b6b8;hp=2b86f1af407501d78a5245bcb328b3bc71bd2382;hpb=a5eca69502089a63153a77e2c0c0b5f1c9c2504f;p=gitmo%2FClass-MOP.git diff --git a/lib/Class/MOP.pm b/lib/Class/MOP.pm index 2b86f1a..f68ed29 100644 --- a/lib/Class/MOP.pm +++ b/lib/Class/MOP.pm @@ -4,15 +4,756 @@ package Class::MOP; use strict; use warnings; -use Scalar::Util 'blessed'; - -our $VERSION = '0.01'; - -# my %METAS; -# sub UNIVERSAL::meta { -# my $class = blessed($_[0]) || $_[0]; -# $METAS{$class} ||= Class::MOP::Class->initialize($class) -# } +use MRO::Compat; + +use Carp 'confess'; +use Scalar::Util 'weaken'; + +use Class::MOP::Class; +use Class::MOP::Attribute; +use Class::MOP::Method; + +use Class::MOP::Immutable; + +BEGIN { + + our $VERSION = '0.64'; + our $AUTHORITY = 'cpan:STEVAN'; + + *IS_RUNNING_ON_5_10 = ($] < 5.009_005) + ? sub () { 0 } + : sub () { 1 }; + + # NOTE: + # we may not use this yet, but once + # the get_code_info XS gets merged + # upstream to it, we will always use + # it. But for now it is just kinda + # extra overhead. + # - SL + require Sub::Identify; + + # stash these for a sec, and see how things go + my $_PP_subname = sub { $_[1] }; + my $_PP_get_code_info = \&Sub::Identify::get_code_info; + + if ($ENV{CLASS_MOP_NO_XS}) { + # NOTE: + # this is if you really want things + # to be slow, then you can force the + # no-XS rule this way, otherwise we + # make an effort to load as much of + # the XS as possible. + # - SL + no warnings 'prototype', 'redefine'; + + unless (IS_RUNNING_ON_5_10()) { + # get this from MRO::Compat ... + *check_package_cache_flag = \&MRO::Compat::__get_pkg_gen_pp; + } + else { + # NOTE: + # but if we are running 5.10 + # there is no need to use the + # Pure Perl version since we + # can use the built in mro + # version instead. + # - SL + *check_package_cache_flag = \&mro::get_pkg_gen; + } + # our own version of Sub::Name + *subname = $_PP_subname; + # and the Sub::Identify version of the get_code_info + *get_code_info = $_PP_get_code_info; + } + else { + # now try our best to get as much + # of the XS loaded as possible + { + local $@; + eval { + require XSLoader; + XSLoader::load( 'Class::MOP', $VERSION ); + }; + die $@ if $@ && $@ !~ /object version|loadable object/; + + # okay, so the XS failed to load, so + # use the pure perl one instead. + *get_code_info = $_PP_get_code_info if $@; + } + + # get it from MRO::Compat + *check_package_cache_flag = \&mro::get_pkg_gen; + + # now try and load the Sub::Name + # module and use that as a means + # for naming our CVs, if not, we + # use the workaround instead. + if ( eval { require Sub::Name } ) { + *subname = \&Sub::Name::subname; + } + else { + *subname = $_PP_subname; + } + } +} + +{ + # 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; + + # 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 get_metaclass_by_name { $METAS{$_[0]} } + sub store_metaclass_by_name { $METAS{$_[0]} = $_[1] } + sub weaken_metaclass { weaken($METAS{$_[0]}) } + sub does_metaclass_exist { exists $METAS{$_[0]} && defined $METAS{$_[0]} } + sub remove_metaclass_by_name { $METAS{$_[0]} = undef } + + # NOTE: + # We only cache metaclasses, meaning instances of + # Class::MOP::Class. We do not cache instance of + # Class::MOP::Package or Class::MOP::Module. Mostly + # because I don't yet see a good reason to do so. +} + +sub load_class { + my $class = shift; + + if (ref($class) || !defined($class) || !length($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 $file = $class . '.pm'; + $file =~ s{::}{/}g; + eval { CORE::require($file) }; + confess "Could not load class ($class) because : $@" if $@; + } + + # initialize a metaclass if necessary + unless (does_metaclass_exist($class)) { + eval { Class::MOP::Class->initialize($class) }; + confess "Could not initialize class ($class) because : $@" if $@; + } + + return get_metaclass_by_name($class); +} + +sub is_class_loaded { + my $class = shift; + + return 0 if ref($class) || !defined($class) || !length($class); + + # walk the symbol table tree to avoid autovififying + # \*{${main::}{"Foo::"}} == \*main::Foo:: + + my $pack = \*::; + foreach my $part (split('::', $class)) { + return 0 unless exists ${$$pack}{"${part}::"}; + $pack = \*{${$$pack}{"${part}::"}}; + } + + # check for $VERSION or @ISA + return 1 if exists ${$$pack}{VERSION} + && defined *{${$$pack}{VERSION}}{SCALAR}; + return 1 if exists ${$$pack}{ISA} + && defined *{${$$pack}{ISA}}{ARRAY}; + + # check for any method + foreach ( keys %{$$pack} ) { + next if substr($_, -2, 2) eq '::'; + + my $glob = ${$$pack}{$_} || next; + + # constant subs + if ( IS_RUNNING_ON_5_10 ) { + return 1 if ref $glob eq 'SCALAR'; + } + + return 1 if defined *{$glob}{CODE}; + } + + # fail + return 0; +} + + +## ---------------------------------------------------------------------------- +## Setting up our environment ... +## ---------------------------------------------------------------------------- +## Class::MOP needs to have a few things in the global perl environment so +## that it can operate effectively. Those things are done here. +## ---------------------------------------------------------------------------- + +# ... nothing yet actually ;) + +## ---------------------------------------------------------------------------- +## Bootstrapping +## ---------------------------------------------------------------------------- +## The code below here is to bootstrap our MOP with itself. This is also +## sometimes called "tying the knot". By doing this, we make it much easier +## to extend the MOP through subclassing and such since now you can use the +## MOP itself to extend itself. +## +## Yes, I know, thats weird and insane, but it's a good thing, trust me :) +## ---------------------------------------------------------------------------- + +# We need to add in the meta-attributes here so that +# any subclass of Class::MOP::* will be able to +# inherit them using &construct_instance + +## -------------------------------------------------------- +## Class::MOP::Package + +Class::MOP::Package->meta->add_attribute( + Class::MOP::Attribute->new('$!package' => ( + reader => { + # NOTE: we need to do this in order + # for the instance meta-object to + # not fall into meta-circular death + # + # we just alias the original method + # rather than re-produce it here + 'name' => \&Class::MOP::Package::name + }, + init_arg => 'package', + )) +); + +Class::MOP::Package->meta->add_attribute( + Class::MOP::Attribute->new('%!namespace' => ( + reader => { + # NOTE: + # we just alias the original method + # rather than re-produce it here + 'namespace' => \&Class::MOP::Package::namespace + }, + init_arg => undef, + default => sub { \undef } + )) +); + +# NOTE: +# use the metaclass to construct the meta-package +# which is a superclass of the metaclass itself :P +Class::MOP::Package->meta->add_method('initialize' => sub { + my $class = shift; + my $package_name = shift; + $class->meta->new_object('package' => $package_name, @_); +}); + +## -------------------------------------------------------- +## Class::MOP::Module + +# NOTE: +# yeah this is kind of stretching things a bit, +# but truthfully the version should be an attribute +# of the Module, the weirdness comes from having to +# stick to Perl 5 convention and store it in the +# $VERSION package variable. Basically if you just +# squint at it, it will look how you want it to look. +# Either as a package variable, or as a attribute of +# the metaclass, isn't abstraction great :) + +Class::MOP::Module->meta->add_attribute( + Class::MOP::Attribute->new('$!version' => ( + reader => { + # NOTE: + # we just alias the original method + # rather than re-produce it here + 'version' => \&Class::MOP::Module::version + }, + init_arg => undef, + default => sub { \undef } + )) +); + +# NOTE: +# By following the same conventions as version here, +# we are opening up the possibility that people can +# use the $AUTHORITY in non-Class::MOP modules as +# well. + +Class::MOP::Module->meta->add_attribute( + Class::MOP::Attribute->new('$!authority' => ( + reader => { + # NOTE: + # we just alias the original method + # rather than re-produce it here + 'authority' => \&Class::MOP::Module::authority + }, + init_arg => undef, + default => sub { \undef } + )) +); + +## -------------------------------------------------------- +## Class::MOP::Class + +Class::MOP::Class->meta->add_attribute( + Class::MOP::Attribute->new('%!attributes' => ( + reader => { + # NOTE: we need to do this in order + # for the instance meta-object to + # not fall into meta-circular death + # + # we just alias the original method + # rather than re-produce it here + 'get_attribute_map' => \&Class::MOP::Class::get_attribute_map + }, + init_arg => 'attributes', + default => sub { {} } + )) +); + +Class::MOP::Class->meta->add_attribute( + Class::MOP::Attribute->new('%!methods' => ( + init_arg => 'methods', + reader => { + # NOTE: + # we just alias the original method + # rather than re-produce it here + 'get_method_map' => \&Class::MOP::Class::get_method_map + }, + default => sub { {} } + )) +); + +Class::MOP::Class->meta->add_attribute( + Class::MOP::Attribute->new('@!superclasses' => ( + accessor => { + # NOTE: + # we just alias the original method + # rather than re-produce it here + 'superclasses' => \&Class::MOP::Class::superclasses + }, + init_arg => undef, + default => sub { \undef } + )) +); + +Class::MOP::Class->meta->add_attribute( + Class::MOP::Attribute->new('$!attribute_metaclass' => ( + reader => { + # NOTE: + # we just alias the original method + # rather than re-produce it here + 'attribute_metaclass' => \&Class::MOP::Class::attribute_metaclass + }, + init_arg => 'attribute_metaclass', + default => 'Class::MOP::Attribute', + )) +); + +Class::MOP::Class->meta->add_attribute( + Class::MOP::Attribute->new('$!method_metaclass' => ( + reader => { + # NOTE: + # we just alias the original method + # rather than re-produce it here + 'method_metaclass' => \&Class::MOP::Class::method_metaclass + }, + init_arg => 'method_metaclass', + default => 'Class::MOP::Method', + )) +); + +Class::MOP::Class->meta->add_attribute( + Class::MOP::Attribute->new('$!instance_metaclass' => ( + reader => { + # NOTE: we need to do this in order + # for the instance meta-object to + # not fall into meta-circular death + # + # we just alias the original method + # rather than re-produce it here + 'instance_metaclass' => \&Class::MOP::Class::instance_metaclass + }, + init_arg => 'instance_metaclass', + default => 'Class::MOP::Instance', + )) +); + +# NOTE: +# we don't actually need to tie the knot with +# Class::MOP::Class here, it is actually handled +# within Class::MOP::Class itself in the +# construct_class_instance method. + +## -------------------------------------------------------- +## Class::MOP::Attribute + +Class::MOP::Attribute->meta->add_attribute( + Class::MOP::Attribute->new('$!name' => ( + init_arg => 'name', + reader => { + # NOTE: we need to do this in order + # for the instance meta-object to + # not fall into meta-circular death + # + # we just alias the original method + # rather than re-produce it here + 'name' => \&Class::MOP::Attribute::name + } + )) +); + +Class::MOP::Attribute->meta->add_attribute( + Class::MOP::Attribute->new('$!associated_class' => ( + init_arg => 'associated_class', + reader => { + # NOTE: we need to do this in order + # for the instance meta-object to + # not fall into meta-circular death + # + # we just alias the original method + # rather than re-produce it here + 'associated_class' => \&Class::MOP::Attribute::associated_class + } + )) +); + +Class::MOP::Attribute->meta->add_attribute( + Class::MOP::Attribute->new('$!accessor' => ( + init_arg => 'accessor', + reader => { 'accessor' => \&Class::MOP::Attribute::accessor }, + predicate => { 'has_accessor' => \&Class::MOP::Attribute::has_accessor }, + )) +); + +Class::MOP::Attribute->meta->add_attribute( + Class::MOP::Attribute->new('$!reader' => ( + init_arg => 'reader', + reader => { 'reader' => \&Class::MOP::Attribute::reader }, + predicate => { 'has_reader' => \&Class::MOP::Attribute::has_reader }, + )) +); + +Class::MOP::Attribute->meta->add_attribute( + Class::MOP::Attribute->new('$!initializer' => ( + init_arg => 'initializer', + reader => { 'initializer' => \&Class::MOP::Attribute::initializer }, + predicate => { 'has_initializer' => \&Class::MOP::Attribute::has_initializer }, + )) +); + +Class::MOP::Attribute->meta->add_attribute( + Class::MOP::Attribute->new('$!writer' => ( + init_arg => 'writer', + reader => { 'writer' => \&Class::MOP::Attribute::writer }, + predicate => { 'has_writer' => \&Class::MOP::Attribute::has_writer }, + )) +); + +Class::MOP::Attribute->meta->add_attribute( + Class::MOP::Attribute->new('$!predicate' => ( + init_arg => 'predicate', + reader => { 'predicate' => \&Class::MOP::Attribute::predicate }, + predicate => { 'has_predicate' => \&Class::MOP::Attribute::has_predicate }, + )) +); + +Class::MOP::Attribute->meta->add_attribute( + Class::MOP::Attribute->new('$!clearer' => ( + init_arg => 'clearer', + reader => { 'clearer' => \&Class::MOP::Attribute::clearer }, + predicate => { 'has_clearer' => \&Class::MOP::Attribute::has_clearer }, + )) +); + +Class::MOP::Attribute->meta->add_attribute( + Class::MOP::Attribute->new('$!builder' => ( + init_arg => 'builder', + reader => { 'builder' => \&Class::MOP::Attribute::builder }, + predicate => { 'has_builder' => \&Class::MOP::Attribute::has_builder }, + )) +); + +Class::MOP::Attribute->meta->add_attribute( + Class::MOP::Attribute->new('$!init_arg' => ( + init_arg => 'init_arg', + reader => { 'init_arg' => \&Class::MOP::Attribute::init_arg }, + predicate => { 'has_init_arg' => \&Class::MOP::Attribute::has_init_arg }, + )) +); + +Class::MOP::Attribute->meta->add_attribute( + Class::MOP::Attribute->new('$!default' => ( + init_arg => 'default', + # default has a custom 'reader' method ... + predicate => { 'has_default' => \&Class::MOP::Attribute::has_default }, + )) +); + +Class::MOP::Attribute->meta->add_attribute( + Class::MOP::Attribute->new('@!associated_methods' => ( + init_arg => 'associated_methods', + reader => { 'associated_methods' => \&Class::MOP::Attribute::associated_methods }, + default => sub { [] } + )) +); + +# NOTE: (meta-circularity) +# This should be one of the last things done +# it will "tie the knot" with Class::MOP::Attribute +# so that it uses the attributes meta-objects +# to construct itself. +Class::MOP::Attribute->meta->add_method('new' => sub { + my $class = shift; + my $name = shift; + my %options = @_; + + (defined $name && $name) + || confess "You must provide a name for the attribute"; + $options{init_arg} = $name + if not exists $options{init_arg}; + + if(exists $options{builder}){ + confess("builder must be a defined scalar value which is a method name") + if ref $options{builder} || !(defined $options{builder}); + confess("Setting both default and builder is not allowed.") + if exists $options{default}; + } else { + (Class::MOP::Attribute::is_default_a_coderef(\%options)) + || confess("References are not allowed as default values, you must ". + "wrap the default of '$name' in a CODE reference (ex: sub { [] } and not [])") + if exists $options{default} && ref $options{default}; + } + # return the new object + $class->meta->new_object(name => $name, %options); +}); + +Class::MOP::Attribute->meta->add_method('clone' => sub { + my $self = shift; + $self->meta->clone_object($self, @_); +}); + +## -------------------------------------------------------- +## Class::MOP::Method + +Class::MOP::Method->meta->add_attribute( + Class::MOP::Attribute->new('&!body' => ( + init_arg => 'body', + reader => { 'body' => \&Class::MOP::Method::body }, + )) +); + +Class::MOP::Method->meta->add_attribute( + Class::MOP::Attribute->new('$!package_name' => ( + init_arg => 'package_name', + reader => { 'package_name' => \&Class::MOP::Method::package_name }, + )) +); + +Class::MOP::Method->meta->add_attribute( + Class::MOP::Attribute->new('$!name' => ( + init_arg => 'name', + reader => { 'name' => \&Class::MOP::Method::name }, + )) +); + +Class::MOP::Method->meta->add_method('wrap' => sub { + my $class = shift; + my $code = shift; + my %options = @_; + + ('CODE' eq ref($code)) + || confess "You must supply a CODE reference to bless, not (" . ($code || 'undef') . ")"; + + ($options{package_name} && $options{name}) + || confess "You must supply the package_name and name parameters"; + + # return the new object + $class->meta->new_object(body => $code, %options); +}); + +Class::MOP::Method->meta->add_method('clone' => sub { + my $self = shift; + $self->meta->clone_object($self, @_); +}); + +## -------------------------------------------------------- +## Class::MOP::Method::Wrapped + +# NOTE: +# the way this item is initialized, this +# really does not follow the standard +# practices of attributes, but we put +# it here for completeness +Class::MOP::Method::Wrapped->meta->add_attribute( + Class::MOP::Attribute->new('%!modifier_table') +); + +## -------------------------------------------------------- +## Class::MOP::Method::Generated + +Class::MOP::Method::Generated->meta->add_attribute( + Class::MOP::Attribute->new('$!is_inline' => ( + init_arg => 'is_inline', + reader => { 'is_inline' => \&Class::MOP::Method::Generated::is_inline }, + default => 0, + )) +); + +Class::MOP::Method::Generated->meta->add_method('new' => sub { + my ($class, %options) = @_; + ($options{package_name} && $options{name}) + || confess "You must supply the package_name and name parameters"; + my $self = $class->meta->new_object(%options); + $self->initialize_body; + $self; +}); + +## -------------------------------------------------------- +## Class::MOP::Method::Accessor + +Class::MOP::Method::Accessor->meta->add_attribute( + Class::MOP::Attribute->new('$!attribute' => ( + init_arg => 'attribute', + reader => { + 'associated_attribute' => \&Class::MOP::Method::Accessor::associated_attribute + }, + )) +); + +Class::MOP::Method::Accessor->meta->add_attribute( + Class::MOP::Attribute->new('$!accessor_type' => ( + init_arg => 'accessor_type', + reader => { 'accessor_type' => \&Class::MOP::Method::Accessor::accessor_type }, + )) +); + +Class::MOP::Method::Accessor->meta->add_method('new' => sub { + my $class = shift; + my %options = @_; + + (exists $options{attribute}) + || confess "You must supply an attribute to construct with"; + + (exists $options{accessor_type}) + || confess "You must supply an accessor_type to construct with"; + + (Scalar::Util::blessed($options{attribute}) && $options{attribute}->isa('Class::MOP::Attribute')) + || confess "You must supply an attribute which is a 'Class::MOP::Attribute' instance"; + + ($options{package_name} && $options{name}) + || confess "You must supply the package_name and name parameters"; + + # return the new object + my $self = $class->meta->new_object(%options); + + # we don't want this creating + # a cycle in the code, if not + # needed + Scalar::Util::weaken($self->{'$!attribute'}); + + $self->initialize_body; + + $self; +}); + + +## -------------------------------------------------------- +## Class::MOP::Method::Constructor + +Class::MOP::Method::Constructor->meta->add_attribute( + Class::MOP::Attribute->new('%!options' => ( + init_arg => 'options', + reader => { + 'options' => \&Class::MOP::Method::Constructor::options + }, + default => sub { +{} } + )) +); + +Class::MOP::Method::Constructor->meta->add_attribute( + Class::MOP::Attribute->new('$!associated_metaclass' => ( + init_arg => 'metaclass', + reader => { + 'associated_metaclass' => \&Class::MOP::Method::Constructor::associated_metaclass + }, + )) +); + +Class::MOP::Method::Constructor->meta->add_method('new' => sub { + my $class = shift; + my %options = @_; + + (Scalar::Util::blessed $options{metaclass} && $options{metaclass}->isa('Class::MOP::Class')) + || confess "You must pass a metaclass instance if you want to inline" + if $options{is_inline}; + + ($options{package_name} && $options{name}) + || confess "You must supply the package_name and name parameters"; + + # return the new object + my $self = $class->meta->new_object(%options); + + # we don't want this creating + # a cycle in the code, if not + # needed + Scalar::Util::weaken($self->{'$!associated_metaclass'}); + + $self->initialize_body; + + $self; +}); + +## -------------------------------------------------------- +## Class::MOP::Instance + +# NOTE: +# these don't yet do much of anything, but are just +# included for completeness + +Class::MOP::Instance->meta->add_attribute( + Class::MOP::Attribute->new('$!meta') +); + +Class::MOP::Instance->meta->add_attribute( + Class::MOP::Attribute->new('@!slots') +); + +## -------------------------------------------------------- +## 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. + +$_->meta->make_immutable( + inline_constructor => 0, + inline_accessors => 0, +) for qw/ + Class::MOP::Package + Class::MOP::Module + Class::MOP::Class + + Class::MOP::Attribute + Class::MOP::Method + Class::MOP::Instance + + Class::MOP::Object + + Class::MOP::Method::Generated + + Class::MOP::Method::Accessor + Class::MOP::Method::Constructor + Class::MOP::Method::Wrapped +/; 1; @@ -20,450 +761,412 @@ __END__ =pod -=head1 NAME +=head1 NAME Class::MOP - A Meta Object Protocol for Perl 5 -=head1 SYNOPSIS - - # ... coming soon - =head1 DESCRIPTON -This module is an attempt to create a meta object protocol for the -Perl 5 object system. It makes no attempt to change the behavior or -characteristics of the Perl 5 object system, only to create a +This module is a fully functioning meta object protocol for the +Perl 5 object system. It makes no attempt to change the behavior or +characteristics of the Perl 5 object system, only to create a protocol for its manipulation and introspection. -That said, it does attempt to create the tools for building a rich -set of extensions to the Perl 5 object system. Every attempt has been -made for these tools to keep to the spirit of the Perl 5 object +That said, it does attempt to create the tools for building a rich +set of extensions to the Perl 5 object system. Every attempt has been +made for these tools to keep to the spirit of the Perl 5 object system that we all know and love. +This documentation is admittedly sparse on details, as time permits +I will try to improve them. For now, I suggest looking at the items +listed in the L section for more information. In particular +the book "The Art of the Meta Object Protocol" was very influential +in the development of this system. + =head2 What is a Meta Object Protocol? -A meta object protocol is an API to an object system. +A meta object protocol is an API to an object system. -To be more specific, it is a set of abstractions of the components of -an object system (typically things like; classes, object, methods, -object attributes, etc.). These abstractions can then be used to both +To be more specific, it is a set of abstractions of the components of +an object system (typically things like; classes, object, methods, +object attributes, etc.). These abstractions can then be used to both inspect and manipulate the object system which they describe. -It can be said that there are two MOPs for any object system; the -implicit MOP, and the explicit MOP. The implicit MOP handles things -like method dispatch or inheritance, which happen automatically as -part of how the object system works. The explicit MOP typically -handles the introspection/reflection features of the object system. -All object systems have implicit MOPs, without one, they would not -work. Explict MOPs however as less common, and depending on the -language can vary from restrictive (Reflection in Java or C#) to -wide open (CLOS is a perfect example). +It can be said that there are two MOPs for any object system; the +implicit MOP, and the explicit MOP. The implicit MOP handles things +like method dispatch or inheritance, which happen automatically as +part of how the object system works. The explicit MOP typically +handles the introspection/reflection features of the object system. +All object systems have implicit MOPs, without one, they would not +work. Explict MOPs however as less common, and depending on the +language can vary from restrictive (Reflection in Java or C#) to +wide open (CLOS is a perfect example). + +=head2 Yet Another Class Builder!! Why? + +This is B a class builder so much as it is a I>. My intent is that an end user does not use this module +directly, but instead this module is used by module authors to +build extensions and features onto the Perl 5 object system. =head2 Who is this module for? -This module is specifically for anyone who has ever created or -wanted to create a module for the Class:: namespace. The tools which -this module will provide will hopefully make it easier to do more -complex things with Perl 5 classes by removing such barriers as -the need to hack the symbol tables, or understand the fine details -of method dispatch. +This module is specifically for anyone who has ever created or +wanted to create a module for the Class:: namespace. The tools which +this module will provide will hopefully make it easier to do more +complex things with Perl 5 classes by removing such barriers as +the need to hack the symbol tables, or understand the fine details +of method dispatch. =head2 What changes do I have to make to use this module? -This module was designed to be as unintrusive as possible. So many of -it's features are accessible without B change to your existsing -code at all. It is meant to be a compliment to your existing code and -not an intrusion on your code base. +This module was designed to be as unintrusive as possible. Many of +its features are accessible without B change to your existsing +code at all. It is meant to be a compliment to your existing code and +not an intrusion on your code base. Unlike many other B +modules, this module B require you subclass it, or even that +you C it in within your module's package. -The only feature which requires additions to your code are the -attribute handling and instance construction features. The only reason -for this is because Perl 5's object system does not actually have -these features built in. More information about this feature can be -found below. +The only features which requires additions to your code are the +attribute handling and instance construction features, and these are +both completely optional features. The only reason for this is because +Perl 5's object system does not actually have these features built +in. More information about this feature can be found below. =head2 A Note about Performance? -It is a common misconception that explict MOPs are performance drains. -But this is not a universal truth at all, it is an side-effect of -specific implementations. For instance, using Java reflection is much -slower because the JVM cannot take advantage of any compiler -optimizations, and the JVM has to deal with much more runtime type -information as well. Reflection in C# is marginally better as it was -designed into the language and runtime (the CLR). In contrast, CLOS -(the Common Lisp Object System) was built to support an explicit MOP, -and so performance is tuned for it. - -This library in particular does it's absolute best to avoid putting -B drain at all upon your code's performance, while still trying -to make sure it is fast as well (although only as a secondary -concern). +It is a common misconception that explict MOPs are performance drains. +But this is not a universal truth at all, it is an side-effect of +specific implementations. For instance, using Java reflection is much +slower because the JVM cannot take advantage of any compiler +optimizations, and the JVM has to deal with much more runtime type +information as well. Reflection in C# is marginally better as it was +designed into the language and runtime (the CLR). In contrast, CLOS +(the Common Lisp Object System) was built to support an explicit MOP, +and so performance is tuned for it. + +This library in particular does it's absolute best to avoid putting +B drain at all upon your code's performance. In fact, by itself +it does nothing to affect your existing code. So you only pay for +what you actually use. + +=head2 About Metaclass compatibility + +This module makes sure that all metaclasses created are both upwards +and downwards compatible. The topic of metaclass compatibility is +highly esoteric and is something only encountered when doing deep and +involved metaclass hacking. There are two basic kinds of metaclass +incompatibility; upwards and downwards. + +Upwards metaclass compatibility means that the metaclass of a +given class is either the same as (or a subclass of) all of the +class's ancestors. + +Downward metaclass compatibility means that the metaclasses of a +given class's anscestors are all either the same as (or a subclass +of) that metaclass. + +Here is a diagram showing a set of two classes (C and C) and +two metaclasses (C and C) which have correct +metaclass compatibility both upwards and downwards. + + +---------+ +---------+ + | Meta::A |<----| Meta::B | <....... (instance of ) + +---------+ +---------+ <------- (inherits from) + ^ ^ + : : + +---------+ +---------+ + | A |<----| B | + +---------+ +---------+ + +As I said this is a highly esoteric topic and one you will only run +into if you do a lot of subclassing of B. If you +are interested in why this is an issue see the paper +I linked to in the +L section of this document. + +=head2 Using custom metaclasses + +Always use the metaclass pragma when using a custom metaclass, this +will ensure the proper initialization order and not accidentely +create an incorrect type of metaclass for you. This is a very rare +problem, and one which can only occur if you are doing deep metaclass +programming. So in other words, don't worry about it. =head1 PROTOCOLS -The protocol is divided into 3 main sub-protocols: +The protocol is divided into 4 main sub-protocols: =over 4 =item The Class protocol -This provides a means of manipulating and introspecting a Perl 5 -class. It handles all of symbol table hacking for you, and provides +This provides a means of manipulating and introspecting a Perl 5 +class. It handles all of symbol table hacking for you, and provides a rich set of methods that go beyond simple package introspection. +See L for more details. + =item The Attribute protocol -This provides a consistent represenation for an attribute of a -Perl 5 class. Since there are so many ways to create and handle -atttributes in Perl 5 OO, this attempts to provide as much of a -unified approach as possible, while giving the freedom and +This provides a consistent represenation for an attribute of a +Perl 5 class. Since there are so many ways to create and handle +attributes in Perl 5 OO, this attempts to provide as much of a +unified approach as possible, while giving the freedom and flexibility to subclass for specialization. +See L for more details. + =item The Method protocol -This provides a means of manipulating and introspecting methods in -the Perl 5 object system. As with attributes, there are many ways to -approach this topic, so we try to keep it pretty basic, while still +This provides a means of manipulating and introspecting methods in +the Perl 5 object system. As with attributes, there are many ways to +approach this topic, so we try to keep it pretty basic, while still making it possible to extend the system in many ways. -=back - -What follows is a more detailed documentation on each specific sub -protocol. - -=head2 The Class protocol - -=head3 Class construction - -These methods handle creating Class objects, which can be used to -both create new classes, and analyze pre-existing ones. +See L for more details. -Class::MOP will internally store weakened references to all the -instances you create with these methods, so that they do not need -to be created any more than nessecary. +=item The Instance protocol -=over 4 - -=item B ?@superclasses, - methods => ?%methods, - attributes => ?%attributes)> - -This returns the basic Class object, bringing the specified -C<$package_name> into existence and adding any of the -C<$package_version>, C<@superclasses>, C<%methods> and C<%attributes> -to it. - -=item B +This provides a layer of abstraction for creating object instances. +Since the other layers use this protocol, it is relatively easy to +change the type of your instances from the default HASH ref to other +types of references. Several examples are provided in the F +directory included in this distribution. -This initializes a Class object for a given a C<$package_name>. +See L for more details. =back -=head3 Instance construction +=head1 FUNCTIONS + +=head2 Constants =over 4 -=item B +=item I -This will construct and instance using the C<$canidate> as storage -(currently only HASH references are supported). This will collect all -the applicable attribute meta-objects and layout out the fields in the -C<$canidate>, it will then initialize them using either use the -corresponding key in C<%params> or any default value or initializer -found in the attribute meta-object. +We set this constant depending on what version perl we are on, this +allows us to take advantage of new 5.10 features and stay backwards +compat. =back -=head3 Informational +=head2 Utility functions =over 4 -=item B - -This is a read-only attribute which returns the package name that -the Class is stored in. +=item B -=item B +This will load a given C<$class_name> and if it does not have an +already initialized metaclass, then it will intialize one for it. +This function can be used in place of tricks like +C or using C. -This is a read-only attribute which returns the C<$VERSION> of the -package the Class is stored in. +=item B -=back +This will return a boolean depending on if the C<$class_name> has +been loaded. -=head3 Inheritance Relationships +NOTE: This does a basic check of the symbol table to try and +determine as best it can if the C<$class_name> is loaded, it +is probably correct about 99% of the time. -=over 4 +=item B -=item B +This will return an integer that is managed by C +to determine if a module's symbol table has been altered. -This is a read-write attribute which represents the superclass -relationships of this Class. Basically, it can get and set the -C<@ISA> for you. +In Perl 5.10 or greater, this flag is package specific. However in +versions prior to 5.10, this will use the C variable +which is not package specific. -=item B +=item B -This computes the a list of the Class's ancestors in the same order -in which method dispatch will be done. +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. -=back +=item B -=head3 Methods +B -=over 4 +If possible, we will load the L module and this will function +as C does, otherwise it will just return the C<$code> +argument. -=item B +=back -This will take a C<$method_name> and CODE reference to that -C<$method> and install it into the Class. +=head2 Metaclass cache functions -B : This does absolutely nothing special to C<$method> -other than use B to make sure it is tagged with the -correct name, and therefore show up correctly in stack traces and -such. +Class::MOP holds a cache of metaclasses, the following are functions +(B) 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. -=item B +=over 4 -This just provides a simple way to check if the Class implements -a specific C<$method_name>. It will I however, attempt to check -if the class inherits the method. +=item B -This will correctly handle functions defined outside of the package -that use a fully qualified name (C). +This will return an hash of all the metaclass instances that have +been cached by B keyed by the package name. -This will correctly handle functions renamed with B and -installed using the symbol tables. However, if you are naming the -subroutine outside of the package scope, you must use the fully -qualified name, including the package name, for C to -correctly identify it. +=item B -This will attempt to correctly ignore functions imported from other -packages using B. It breaks down if the function imported -is an C<__ANON__> sub (such as with C), which very well -may be a valid method being applied to the class. +This will return an array of all the metaclass instances that have +been cached by B. -In short, this method cannot always be trusted to determine if the -C<$method_name> is actually a method. However, it will DWIM about -90% of the time, so it's a small trade off IMO. +=item B -=item B +This will return an array of all the metaclass names that have +been cached by B. -This will return a CODE reference of the specified C<$method_name>, -or return undef if that method does not exist. +=item B -=item B +This will return a cached B instance of nothing +if no metaclass exist by that C<$name>. -This will attempt to remove a given C<$method_name> from the Class. -It will return the CODE reference that it has removed, and will -attempt to use B to clear the methods associated name. +=item B -=item B +This will store a metaclass in the cache at the supplied C<$key>. -This will return a list of method names for all I defined -methods. It does B provide a list of all applicable methods, -including any inherited ones. If you want a list of all applicable -methods, use the C method. +=item B -=item B +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>. -This will return a list of all the methods names this Class will -support, 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. +=item B -=item B +This will return true of there exists a metaclass stored in the +C<$name> key and return false otherwise. -This will traverse the inheritence hierarchy and locate all methods -with a given C<$method_name>. Similar to -C it returns a list of HASH references -with the following information; method name (which will always be the -same as C<$method_name>), the name of the class in which the method -lives and a CODE reference for the actual method. +=item B -The list of methods produced is a distinct list, meaning there are no -duplicates in it. This is especially useful for things like object -initialization and destruction where you only want the method called -once, and in the correct order. +This will remove a the metaclass stored in the C<$name> key. =back -=head3 Attributes +=head1 SEE ALSO -It should be noted that since there is no one consistent way to define -the attributes of a class in Perl 5. These methods can only work with -the information given, and can not easily discover information on -their own. +=head2 Books -=over 4 +There are very few books out on Meta Object Protocols and Metaclasses +because it is such an esoteric topic. The following books are really +the only ones I have found. If you know of any more, B> +email me and let me know, I would love to hear about them. -=item B - -This stores a C<$attribute_meta_object> in the Class object and -associates it with the C<$attribute_name>. Unlike methods, attributes -within the MOP are stored as meta-information only. They will be used -later to construct instances from (see C above). -More details about the attribute meta-objects can be found in the -L section of this document. +=over 4 -=item B +=item "The Art of the Meta Object Protocol" -Checks to see if this Class has an attribute by the name of -C<$attribute_name> and returns a boolean. +=item "Advances in Object-Oriented Metalevel Architecture and Reflection" -=item B +=item "Putting MetaClasses to Work" -Returns the attribute meta-object associated with C<$attribute_name>, -if none is found, it will return undef. +=item "Smalltalk: The Language" -=item B +=back -This will remove the attribute meta-object stored at -C<$attribute_name>, then return the removed attribute meta-object. +=head2 Papers -B Removing an attribute will only affect future instances of -the class, it will not make any attempt to remove the attribute from -any existing instances of the class. +=over 4 -=item B +=item Uniform and safe metaclass composition -This returns a list of attribute names which are defined in the local -class. If you want a list of all applicable attributes for a class, -use the C method. +An excellent paper by the people who brought us the original Traits paper. +This paper is on how Traits can be used to do safe metaclass composition, +and offers an excellent introduction section which delves into the topic of +metaclass compatibility. -=item B +L -This will traverse the inheritance heirachy and return a list of HASH -references for all the applicable attributes for this class. The HASH -references will contain the following information; the attribute name, -the class which the attribute is associated with and the actual -attribute meta-object +=item Safe Metaclass Programming -=item B +This paper seems to precede the above paper, and propose a mix-in based +approach as opposed to the Traits based approach. Both papers have similar +information on the metaclass compatibility problem space. -This will communicate with all of the classes attributes to create -and install the appropriate accessors. (see L -below for more details). +L =back -=head2 The Attribute Protocol - -This protocol is almost entirely an invention of this module. This is -because Perl 5 does not have consistent notion of what is an attribute -of a class. There are so many ways in which this is done, and very few -(if any) are discoverable by this module. - -So, all that said, this module attempts to inject some order into this -chaos, by introducing a more consistent approach. - -=head3 Creation +=head2 Prior Art =over 4 -=item B - - Class::MOP::Attribute->new('$foo' => ( - accessor => 'foo', # dual purpose get/set accessor - init_arg => '-foo', # class->new will look for a -foo key - default => 'BAR IS BAZ!' # if no -foo key is provided, use this - )); - - Class::MOP::Attribute->new('$.bar' => ( - reader => 'bar', # getter - writer => 'set_bar', # setter - init_arg => '-bar', # class->new will look for a -bar key - # no default value means it is undef - )); - -=back - -=head3 Informational +=item The Perl 6 MetaModel work in the Pugs project =over 4 -=item B - -=item B - -=item B +=item L -=item B +=item L -=item B - -=item B +=back =back -=head3 Informational predicates +=head2 Articles =over 4 -=item B - -Returns true if this attribute uses a get/set accessor, and false -otherwise - -=item B +=item CPAN Module Review of Class::MOP -Returns true if this attribute has a reader, and false otherwise +L -=item B - -Returns true if this attribute has a writer, and false otherwise +=back -=item B +=head1 SIMILAR MODULES -Returns true if this attribute has a class intialization argument, and -false otherwise +As I have said above, this module is a class-builder-builder, so it is +not the same thing as modules like L and +L. That being said there are very few modules on CPAN +with similar goals to this module. The one I have found which is most +like this module is L, although it's philosophy and the MOP it +creates are very different from this modules. -=item B +=head1 BUGS -Returns true if this attribute has a default value, and false -otherwise. +All complex software has bugs lurking in it, and this module is no +exception. If you find a bug please either email me, or add the bug +to cpan-RT. -=back - -=head3 Attribute Accessor generation +=head1 ACKNOWLEDGEMENTS =over 4 -=item B +=item Rob Kinyon -This allows the attribute to generate code for it's own accessor -methods. This is mostly part of an internal protocol between the class -and it's own attributes, see the C method above. +Thanks to Rob for actually getting the development of this module kick-started. =back -=head2 The Method Protocol +=head1 AUTHORS -This protocol is very small, since methods in Perl 5 are just -subroutines within the particular package. Basically all we do is to -bless the subroutine and provide some very simple introspection -methods for it. +Stevan Little Estevan@iinteractive.comE -=head1 SEE ALSO +B -=over 4 +Brandon (blblack) Black -=item "The Art of the Meta Object Protocol" +Guillermo (groditi) Roditi -=item "Advances in Object-Oriented Metalevel Architecture and Reflection" +Matt (mst) Trout -=back +Rob (robkinyon) Kinyon -=head1 AUTHOR +Yuval (nothingmuch) Kogman -Stevan Little Estevan@iinteractive.comE +Scott (konobi) McWhirter =head1 COPYRIGHT AND LICENSE -Copyright 2006 by Infinity Interactive, Inc. +Copyright 2006-2008 by Infinity Interactive, Inc. L This library is free software; you can redistribute it and/or modify -it under the same terms as Perl itself. +it under the same terms as Perl itself. =cut