use Scalar::Util 'blessed', 'reftype';
use Sub::Name 'subname';
use B 'svref_2object';
+use Clone ();
our $VERSION = '0.03';
# 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;
+
sub initialize {
my $class = shift;
my $package_name = shift;
|| confess "You must pass a package name";
# make sure the package name is not blessed
$package_name = blessed($package_name) || $package_name;
- return $METAS{$package_name} if exists $METAS{$package_name};
- $METAS{$package_name} = $class->construct_class_instance($package_name, @_);
+ $class->construct_class_instance(':package' => $package_name, @_);
}
# NOTE: (meta-circularity)
# normal &construct_instance.
sub construct_class_instance {
my $class = shift;
- my $package_name = shift;
+ my %options = @_;
+ my $package_name = $options{':package'};
(defined $package_name && $package_name)
- || confess "You must pass a package name";
+ || confess "You must pass a package name";
+ return $METAS{$package_name} if exists $METAS{$package_name};
$class = blessed($class) || $class;
+ # now create the metaclass
+ my $meta;
if ($class =~ /^Class::MOP::/) {
- bless {
+ $meta = bless {
'$:package' => $package_name,
'%:attributes' => {},
- '$:attribute_metaclass' => 'Class::MOP::Attribute',
- '$:method_metaclass' => 'Class::MOP::Method',
+ '$:attribute_metaclass' => $options{':attribute_metaclass'} || 'Class::MOP::Attribute',
+ '$:method_metaclass' => $options{':method_metaclass'} || 'Class::MOP::Method',
} => $class;
}
else {
# it is safe to use meta here because
# class will always be a subclass of
# Class::MOP::Class, which defines meta
- bless $class->meta->construct_instance(':package' => $package_name, @_) => $class
+ $meta = bless $class->meta->construct_instance(%options) => $class
}
+ # and check the metaclass compatibility
+ $meta->check_metaclass_compatability();
+ $METAS{$package_name} = $meta;
+ }
+
+ sub check_metaclass_compatability {
+ my $self = shift;
+
+ # this is always okay ...
+ return if blessed($self) eq 'Class::MOP::Class';
+
+ my @class_list = $self->class_precedence_list;
+ shift @class_list; # shift off $self->name
+
+ foreach my $class_name (@class_list) {
+ next unless $METAS{$class_name};
+ my $meta = $METAS{$class_name};
+ ($self->isa(blessed($meta)))
+ || confess $self->name . "->meta => (" . (blessed($self)) . ")" .
+ " is not compatible with the " .
+ $class_name . "->meta => (" . (blessed($meta)) . ")";
+ }
}
}
sub new_object {
my $class = shift;
+ # NOTE:
+ # we need to protect the integrity of the
+ # Class::MOP::Class singletons here, so we
+ # delegate this to &construct_class_instance
+ # which will deal with the singletons
+ return $class->construct_class_instance(@_)
+ if $class->name->isa('Class::MOP::Class');
bless $class->construct_instance(@_) => $class->name;
}
my ($class, %params) = @_;
my $instance = {};
foreach my $attr ($class->compute_all_applicable_attributes()) {
- my $init_arg = $attr->has_init_arg() ? $attr->init_arg() : $attr->name;
+ 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};
sub clone_object {
my $class = shift;
my $instance = shift;
- bless $class->clone_instance($instance, @_) => $class->name;
+ (blessed($instance) && $instance->isa($class->name))
+ || confess "You must pass an instance ($instance) of the metaclass (" . $class->name . ")";
+ # NOTE:
+ # we need to protect the integrity of the
+ # Class::MOP::Class singletons here, they
+ # should not be cloned.
+ return $instance if $instance->isa('Class::MOP::Class');
+ bless $class->clone_instance($instance, @_) => blessed($instance);
}
sub clone_instance {
- my ($class, $self, %params) = @_;
- (blessed($self))
+ my ($class, $instance, %params) = @_;
+ (blessed($instance))
|| confess "You can only clone instances, \$self is not a blessed instance";
# NOTE:
- # this should actually do a deep clone
- # instead of this cheap hack. I will
- # add that in later.
- # (use the Class::Cloneable::Util code)
- my $clone = { %{$self} };
+ # This will deep clone, which might
+ # not be what you always want. So
+ # the best thing is to write a more
+ # controled &clone method locally
+ # in the class (see Class::MOP)
+ my $clone = Clone::clone($instance);
foreach my $attr ($class->compute_all_applicable_attributes()) {
- my $init_arg = $attr->has_init_arg() ? $attr->init_arg() : $attr->name;
+ my $init_arg = $attr->init_arg();
# try to fetch the init arg from the %params ...
$clone->{$attr->name} = $params{$init_arg}
if exists $params{$init_arg};
This initializes and returns returns a B<Class::MOP::Class> object
for a given a C<$package_name>.
-=item B<construct_class_instance ($package_name)>
+=item B<construct_class_instance (%options)>
This will construct an instance of B<Class::MOP::Class>, it is
here so that we can actually "tie the knot" for B<Class::MOP::Class>
method is used internally by C<initialize> and should never be called
from outside of that method really.
+=item B<check_metaclass_compatability>
+
+This method is called as the very last thing in the
+C<construct_class_instance> method. This will check that the
+metaclass you are creating is compatible with the metaclasses of all
+your ancestors. For more inforamtion about metaclass compatibility
+see the C<About Metaclass compatibility> section in L<Class::MOP>.
+
=back
=head2 Object instance construction and cloning