From: Dave Rolsky Date: Sat, 26 Dec 2009 01:52:52 +0000 (-0600) Subject: move core meta-attribute attributes to a mixin class for benefit of role attributes X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=2611f98e2b56d77be2974b624519ceb3260c4e20;p=gitmo%2FClass-MOP.git move core meta-attribute attributes to a mixin class for benefit of role attributes --- diff --git a/lib/Class/MOP.pm b/lib/Class/MOP.pm index 5749ce7..0ae3a0c 100644 --- a/lib/Class/MOP.pm +++ b/lib/Class/MOP.pm @@ -12,6 +12,7 @@ use Carp 'confess'; use Scalar::Util 'weaken', 'reftype', 'blessed'; use Try::Tiny; +use Class::MOP::Mixin::AttributeBase; use Class::MOP::Mixin::HasAttributes; use Class::MOP::Mixin::HasMethods; use Class::MOP::Class; @@ -379,9 +380,8 @@ Class::MOP::Class->meta->add_attribute( # _construct_class_instance method. ## -------------------------------------------------------- -## Class::MOP::Attribute - -Class::MOP::Attribute->meta->add_attribute( +## Class::MOP::Mixin::AttributeBase +Class::MOP::Mixin::AttributeBase->meta->add_attribute( Class::MOP::Attribute->new('name' => ( reader => { # NOTE: we need to do this in order @@ -390,106 +390,108 @@ Class::MOP::Attribute->meta->add_attribute( # # we just alias the original method # rather than re-produce it here - 'name' => \&Class::MOP::Attribute::name + 'name' => \&Class::MOP::Mixin::AttributeBase::name } )) ); -Class::MOP::Attribute->meta->add_attribute( - Class::MOP::Attribute->new('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::Mixin::AttributeBase->meta->add_attribute( Class::MOP::Attribute->new('accessor' => ( - reader => { 'accessor' => \&Class::MOP::Attribute::accessor }, - predicate => { 'has_accessor' => \&Class::MOP::Attribute::has_accessor }, + reader => { 'accessor' => \&Class::MOP::Mixin::AttributeBase::accessor }, + predicate => { 'has_accessor' => \&Class::MOP::Mixin::AttributeBase::has_accessor }, )) ); -Class::MOP::Attribute->meta->add_attribute( +Class::MOP::Mixin::AttributeBase->meta->add_attribute( Class::MOP::Attribute->new('reader' => ( - reader => { 'reader' => \&Class::MOP::Attribute::reader }, - predicate => { 'has_reader' => \&Class::MOP::Attribute::has_reader }, + reader => { 'reader' => \&Class::MOP::Mixin::AttributeBase::reader }, + predicate => { 'has_reader' => \&Class::MOP::Mixin::AttributeBase::has_reader }, )) ); -Class::MOP::Attribute->meta->add_attribute( +Class::MOP::Mixin::AttributeBase->meta->add_attribute( Class::MOP::Attribute->new('initializer' => ( - reader => { 'initializer' => \&Class::MOP::Attribute::initializer }, - predicate => { 'has_initializer' => \&Class::MOP::Attribute::has_initializer }, + reader => { 'initializer' => \&Class::MOP::Mixin::AttributeBase::initializer }, + predicate => { 'has_initializer' => \&Class::MOP::Mixin::AttributeBase::has_initializer }, )) ); -Class::MOP::Attribute->meta->add_attribute( +Class::MOP::Mixin::AttributeBase->meta->add_attribute( Class::MOP::Attribute->new('definition_context' => ( - reader => { 'definition_context' => \&Class::MOP::Attribute::definition_context }, + reader => { 'definition_context' => \&Class::MOP::Mixin::AttributeBase::definition_context }, )) ); -Class::MOP::Attribute->meta->add_attribute( +Class::MOP::Mixin::AttributeBase->meta->add_attribute( Class::MOP::Attribute->new('writer' => ( - reader => { 'writer' => \&Class::MOP::Attribute::writer }, - predicate => { 'has_writer' => \&Class::MOP::Attribute::has_writer }, + reader => { 'writer' => \&Class::MOP::Mixin::AttributeBase::writer }, + predicate => { 'has_writer' => \&Class::MOP::Mixin::AttributeBase::has_writer }, )) ); -Class::MOP::Attribute->meta->add_attribute( +Class::MOP::Mixin::AttributeBase->meta->add_attribute( Class::MOP::Attribute->new('predicate' => ( - reader => { 'predicate' => \&Class::MOP::Attribute::predicate }, - predicate => { 'has_predicate' => \&Class::MOP::Attribute::has_predicate }, + reader => { 'predicate' => \&Class::MOP::Mixin::AttributeBase::predicate }, + predicate => { 'has_predicate' => \&Class::MOP::Mixin::AttributeBase::has_predicate }, )) ); -Class::MOP::Attribute->meta->add_attribute( +Class::MOP::Mixin::AttributeBase->meta->add_attribute( Class::MOP::Attribute->new('clearer' => ( - reader => { 'clearer' => \&Class::MOP::Attribute::clearer }, - predicate => { 'has_clearer' => \&Class::MOP::Attribute::has_clearer }, + reader => { 'clearer' => \&Class::MOP::Mixin::AttributeBase::clearer }, + predicate => { 'has_clearer' => \&Class::MOP::Mixin::AttributeBase::has_clearer }, )) ); -Class::MOP::Attribute->meta->add_attribute( +Class::MOP::Mixin::AttributeBase->meta->add_attribute( Class::MOP::Attribute->new('builder' => ( - reader => { 'builder' => \&Class::MOP::Attribute::builder }, - predicate => { 'has_builder' => \&Class::MOP::Attribute::has_builder }, + reader => { 'builder' => \&Class::MOP::Mixin::AttributeBase::builder }, + predicate => { 'has_builder' => \&Class::MOP::Mixin::AttributeBase::has_builder }, )) ); -Class::MOP::Attribute->meta->add_attribute( +Class::MOP::Mixin::AttributeBase->meta->add_attribute( Class::MOP::Attribute->new('init_arg' => ( - reader => { 'init_arg' => \&Class::MOP::Attribute::init_arg }, - predicate => { 'has_init_arg' => \&Class::MOP::Attribute::has_init_arg }, + reader => { 'init_arg' => \&Class::MOP::Mixin::AttributeBase::init_arg }, + predicate => { 'has_init_arg' => \&Class::MOP::Mixin::AttributeBase::has_init_arg }, )) ); -Class::MOP::Attribute->meta->add_attribute( +Class::MOP::Mixin::AttributeBase->meta->add_attribute( Class::MOP::Attribute->new('default' => ( # default has a custom 'reader' method ... - predicate => { 'has_default' => \&Class::MOP::Attribute::has_default }, + predicate => { 'has_default' => \&Class::MOP::Mixin::AttributeBase::has_default }, )) ); +Class::MOP::Mixin::AttributeBase->meta->add_attribute( + Class::MOP::Attribute->new('insertion_order' => ( + reader => { 'insertion_order' => \&Class::MOP::Mixin::AttributeBase::insertion_order }, + writer => { '_set_insertion_order' => \&Class::MOP::Mixin::AttributeBase::_set_insertion_order }, + predicate => { 'has_insertion_order' => \&Class::MOP::Mixin::AttributeBase::has_insertion_order }, + )) +); + +## -------------------------------------------------------- +## Class::MOP::Attribute Class::MOP::Attribute->meta->add_attribute( - Class::MOP::Attribute->new('associated_methods' => ( - reader => { 'associated_methods' => \&Class::MOP::Attribute::associated_methods }, - default => sub { [] } + Class::MOP::Attribute->new('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('insertion_order' => ( - reader => { 'insertion_order' => \&Class::MOP::Attribute::insertion_order }, - writer => { '_set_insertion_order' => \&Class::MOP::Attribute::_set_insertion_order }, - predicate => { 'has_insertion_order' => \&Class::MOP::Attribute::has_insertion_order }, + Class::MOP::Attribute->new('associated_methods' => ( + reader => { 'associated_methods' => \&Class::MOP::Attribute::associated_methods }, + default => sub { [] } )) ); @@ -697,6 +699,7 @@ $_->meta->make_immutable( constructor_name => undef, inline_accessors => 0, ) for qw/ + Class::MOP::Mixin::AttributeBase Class::MOP::Mixin::HasAttributes Class::MOP::Mixin::HasMethods /; diff --git a/lib/Class/MOP/Attribute.pm b/lib/Class/MOP/Attribute.pm index b9ca6d2..7bd6839 100644 --- a/lib/Class/MOP/Attribute.pm +++ b/lib/Class/MOP/Attribute.pm @@ -14,7 +14,7 @@ our $VERSION = '0.97'; $VERSION = eval $VERSION; our $AUTHORITY = 'cpan:STEVAN'; -use base 'Class::MOP::Object'; +use base 'Class::MOP::Object', 'Class::MOP::Mixin::AttributeBase'; # NOTE: (meta-circularity) # This method will be replaced in the @@ -44,7 +44,7 @@ sub new { confess("Setting both default and builder is not allowed.") if exists $options{default}; } else { - (is_default_a_coderef(\%options)) + ($class->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}; @@ -156,42 +156,9 @@ sub _set_initial_slot_value { $instance->$initializer($value, $callback, $self); } -# NOTE: -# the next bunch of methods will get bootstrapped -# away in the Class::MOP bootstrapping section - sub associated_class { $_[0]->{'associated_class'} } sub associated_methods { $_[0]->{'associated_methods'} } -sub has_accessor { defined($_[0]->{'accessor'}) } -sub has_reader { defined($_[0]->{'reader'}) } -sub has_writer { defined($_[0]->{'writer'}) } -sub has_predicate { defined($_[0]->{'predicate'}) } -sub has_clearer { defined($_[0]->{'clearer'}) } -sub has_builder { defined($_[0]->{'builder'}) } -sub has_init_arg { defined($_[0]->{'init_arg'}) } -sub has_default { defined($_[0]->{'default'}) } -sub has_initializer { defined($_[0]->{'initializer'}) } -sub has_insertion_order { defined($_[0]->{'insertion_order'}) } - -sub accessor { $_[0]->{'accessor'} } -sub reader { $_[0]->{'reader'} } -sub writer { $_[0]->{'writer'} } -sub predicate { $_[0]->{'predicate'} } -sub clearer { $_[0]->{'clearer'} } -sub builder { $_[0]->{'builder'} } -sub init_arg { $_[0]->{'init_arg'} } -sub initializer { $_[0]->{'initializer'} } -sub definition_context { $_[0]->{'definition_context'} } -sub insertion_order { $_[0]->{'insertion_order'} } -sub _set_insertion_order { $_[0]->{'insertion_order'} = $_[1] } - -# end bootstrapped away method section. -# (all methods below here are kept intact) - -sub has_read_method { $_[0]->has_reader || $_[0]->has_accessor } -sub has_write_method { $_[0]->has_writer || $_[0]->has_accessor } - sub get_read_method { my $self = shift; my $reader = $self->reader || $self->accessor; @@ -252,24 +219,6 @@ sub get_write_method_ref { } } -sub is_default_a_coderef { - my ($value) = $_[0]->{'default'}; - return unless ref($value); - return ref($value) eq 'CODE' || (blessed($value) && $value->isa('Class::MOP::Method')); -} - -sub default { - my ($self, $instance) = @_; - if (defined $instance && $self->is_default_a_coderef) { - # if the default is a CODE ref, then - # we pass in the instance and default - # can return a value based on that - # instance. Somewhat crude, but works. - return $self->{'default'}->($instance); - } - $self->{'default'}; -} - # slots sub slots { (shift)->name } diff --git a/lib/Class/MOP/Mixin/AttributeBase.pm b/lib/Class/MOP/Mixin/AttributeBase.pm new file mode 100644 index 0000000..62d84a4 --- /dev/null +++ b/lib/Class/MOP/Mixin/AttributeBase.pm @@ -0,0 +1,62 @@ +package Class::MOP::Mixin::AttributeBase; + +use strict; +use warnings; + +use Scalar::Util 'blessed'; + +our $VERSION = '0.97'; +$VERSION = eval $VERSION; +our $AUTHORITY = 'cpan:STEVAN'; + +use base 'Class::MOP::Object'; + +sub has_accessor { defined $_[0]->{'accessor'} } +sub has_reader { defined $_[0]->{'reader'} } +sub has_writer { defined $_[0]->{'writer'} } +sub has_predicate { defined $_[0]->{'predicate'} } +sub has_clearer { defined $_[0]->{'clearer'} } +sub has_builder { defined $_[0]->{'builder'} } +sub has_init_arg { defined $_[0]->{'init_arg'} } +sub has_default { defined $_[0]->{'default'} } +sub has_initializer { defined $_[0]->{'initializer'} } +sub has_insertion_order { defined $_[0]->{'insertion_order'} } + +sub accessor { $_[0]->{'accessor'} } +sub reader { $_[0]->{'reader'} } +sub writer { $_[0]->{'writer'} } +sub predicate { $_[0]->{'predicate'} } +sub clearer { $_[0]->{'clearer'} } +sub builder { $_[0]->{'builder'} } +sub init_arg { $_[0]->{'init_arg'} } +sub initializer { $_[0]->{'initializer'} } +sub definition_context { $_[0]->{'definition_context'} } +sub insertion_order { $_[0]->{'insertion_order'} } +sub _set_insertion_order { $_[0]->{'insertion_order'} = $_[1] } + +sub has_read_method { $_[0]->has_reader || $_[0]->has_accessor } +sub has_write_method { $_[0]->has_writer || $_[0]->has_accessor } + +sub is_default_a_coderef { + # Uber hack because it is called from CMOP::Attribute constructor as + # $class->is_default_a_coderef(\%options) + my ($value) = ref $_[0] ? $_[0]->{'default'} : $_[1]->{'default'}; + + return unless ref($value); + + return ref($value) eq 'CODE' + || ( blessed($value) && $value->isa('Class::MOP::Method') ); +} + +sub default { + my ( $self, $instance ) = @_; + if ( defined $instance && $self->is_default_a_coderef ) { + # if the default is a CODE ref, then we pass in the instance and + # default can return a value based on that instance. Somewhat crude, + # but works. + return $self->{'default'}->($instance); + } + $self->{'default'}; +} + +1; diff --git a/t/000_load.t b/t/000_load.t index 47ce599..6060948 100644 --- a/t/000_load.t +++ b/t/000_load.t @@ -5,6 +5,7 @@ use Test::More; BEGIN { use_ok('Class::MOP'); + use_ok('Class::MOP::Mixin::AttributeBase'); use_ok('Class::MOP::Mixin::HasAttributes'); use_ok('Class::MOP::Mixin::HasMethods'); use_ok('Class::MOP::Package'); @@ -31,6 +32,7 @@ my %METAS = ( 'Class::MOP::Method::Accessor' => Class::MOP::Method::Accessor->meta, 'Class::MOP::Method::Constructor' => Class::MOP::Method::Constructor->meta, + 'Class::MOP::Mixin::AttributeBase' => Class::MOP::Mixin::AttributeBase->meta, 'Class::MOP::Mixin::HasAttributes' => Class::MOP::Mixin::HasAttributes->meta, 'Class::MOP::Mixin::HasMethods' => Class::MOP::Mixin::HasMethods->meta, 'Class::MOP::Package' => Class::MOP::Package->meta, @@ -81,6 +83,7 @@ is_deeply( Class::MOP::Method::Generated->meta, Class::MOP::Method::Inlined->meta, Class::MOP::Method::Wrapped->meta, + Class::MOP::Mixin::AttributeBase->meta, Class::MOP::Mixin::HasAttributes->meta, Class::MOP::Mixin::HasMethods->meta, Class::MOP::Module->meta, @@ -98,6 +101,7 @@ is_deeply( Class::MOP::Class Class::MOP::Class::Immutable::Class::MOP::Class Class::MOP::Class::Immutable::Trait + Class::MOP::Mixin::AttributeBase Class::MOP::Mixin::HasAttributes Class::MOP::Mixin::HasMethods Class::MOP::Instance diff --git a/t/014_attribute_introspection.t b/t/014_attribute_introspection.t index d221389..e38b616 100644 --- a/t/014_attribute_introspection.t +++ b/t/014_attribute_introspection.t @@ -8,12 +8,13 @@ use Class::MOP; { my $attr = Class::MOP::Attribute->new('$test'); - is($attr->meta, Class::MOP::Attribute->meta, '... instance and class both lead to the same meta'); + is( $attr->meta, Class::MOP::Attribute->meta, + '... instance and class both lead to the same meta' ); } { my $meta = Class::MOP::Attribute->meta(); - isa_ok($meta, 'Class::MOP::Class'); + isa_ok( $meta, 'Class::MOP::Class' ); my @methods = qw( new @@ -62,15 +63,20 @@ use Class::MOP; remove_accessors _new - ); + ); is_deeply( - [ sort $meta->get_method_list ], + [ + sort Class::MOP::Mixin::AttributeBase->meta->get_method_list, + $meta->get_method_list + ], [ sort @methods ], - '... our method list matches'); + '... our method list matches' + ); foreach my $method_name (@methods) { - ok($meta->has_method($method_name), '... Class::MOP::Attribute->has_method(' . $method_name . ')'); + ok( $meta->find_method_by_name($method_name), + '... Class::MOP::Attribute->find_method_by_name(' . $method_name . ')' ); } my @attributes = ( @@ -91,12 +97,19 @@ use Class::MOP; ); is_deeply( - [ sort $meta->get_attribute_list ], + [ + sort Class::MOP::Mixin::AttributeBase->meta->get_attribute_list, + $meta->get_attribute_list + ], [ sort @attributes ], - '... our attribute list matches'); + '... our attribute list matches' + ); foreach my $attribute_name (@attributes) { - ok($meta->has_attribute($attribute_name), '... Class::MOP::Attribute->has_attribute(' . $attribute_name . ')'); + ok( $meta->find_attribute_by_name($attribute_name), + '... Class::MOP::Attribute->find_attribute_by_name(' + . $attribute_name + . ')' ); } # We could add some tests here to make sure that diff --git a/xs/Attribute.xs b/xs/Attribute.xs deleted file mode 100644 index 0375cb4..0000000 --- a/xs/Attribute.xs +++ /dev/null @@ -1,8 +0,0 @@ -#include "mop.h" - -MODULE = Class::MOP::Attribute PACKAGE = Class::MOP::Attribute - -PROTOTYPES: DISABLE - -BOOT: - INSTALL_SIMPLE_READER(Attribute, name); diff --git a/xs/AttributeBase.xs b/xs/AttributeBase.xs new file mode 100644 index 0000000..b29a14c --- /dev/null +++ b/xs/AttributeBase.xs @@ -0,0 +1,8 @@ +#include "mop.h" + +MODULE = Class::MOP::Mixin::AttributeBase PACKAGE = Class::MOP::Mixin::AttributeBase + +PROTOTYPES: DISABLE + +BOOT: + INSTALL_SIMPLE_READER(Mixin::AttributeBase, name); diff --git a/xs/MOP.xs b/xs/MOP.xs index a0a29fd..faa7704 100644 --- a/xs/MOP.xs +++ b/xs/MOP.xs @@ -13,7 +13,7 @@ find_method (const char *key, STRLEN keylen, SV *val, void *ud) EXTERN_C XS(boot_Class__MOP__Mixin__HasMethods); EXTERN_C XS(boot_Class__MOP__Package); -EXTERN_C XS(boot_Class__MOP__Attribute); +EXTERN_C XS(boot_Class__MOP__Mixin__AttributeBase); EXTERN_C XS(boot_Class__MOP__Method); MODULE = Class::MOP PACKAGE = Class::MOP @@ -25,7 +25,7 @@ BOOT: MOP_CALL_BOOT (boot_Class__MOP__Mixin__HasMethods); MOP_CALL_BOOT (boot_Class__MOP__Package); - MOP_CALL_BOOT (boot_Class__MOP__Attribute); + MOP_CALL_BOOT (boot_Class__MOP__Mixin__AttributeBase); MOP_CALL_BOOT (boot_Class__MOP__Method); # use prototype here to be compatible with get_code_info from Sub::Identify