From: Stevan Little Date: Fri, 21 Sep 2007 19:21:24 +0000 (+0000) Subject: refactored the Constructor to support inlining better and Accessors some too X-Git-Tag: 0_44~13 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=565f0cbbe40a1aa08b7b85574c408dc7e58b2211;p=gitmo%2FClass-MOP.git refactored the Constructor to support inlining better and Accessors some too --- diff --git a/Changes b/Changes index 9bc4029..ef5204a 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,24 @@ Revision history for Perl extension Class-MOP. +0.43 + * Class::MOP::Method::Accessor + - made this a subclass of Class::MOP::Method::Generated + - removed the relevant attributes + + * Class::MOP::Method::Constructor + - fixed the cached values we had to be more sane + - made this a subclass of Class::MOP::Method::Generated + - fixed generated constructor so it properly handles + subclasses now. + - added tests for this + - added the option to allow for both inlined and + non-inlined constructors. + + * Class::MOP::Method::Generated + - added this class as an abstract base for the + Class::MOP::Method::{Constructor,Accessor} classes + - added tests for this + 0.42 Mon. July 16, 2007 !!! Horray for mst, he fixed it !!! diff --git a/lib/Class/MOP.pm b/lib/Class/MOP.pm index dcd0b8b..c090e43 100644 --- a/lib/Class/MOP.pm +++ b/lib/Class/MOP.pm @@ -13,7 +13,7 @@ use Class::MOP::Method; use Class::MOP::Immutable; -our $VERSION = '0.42'; +our $VERSION = '0.43'; our $AUTHORITY = 'cpan:STEVAN'; { @@ -429,6 +429,16 @@ Class::MOP::Method::Wrapped->meta->add_attribute( ); ## -------------------------------------------------------- +## 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 }, + )) +); + +## -------------------------------------------------------- ## Class::MOP::Method::Accessor Class::MOP::Method::Accessor->meta->add_attribute( @@ -447,12 +457,6 @@ Class::MOP::Method::Accessor->meta->add_attribute( )) ); -Class::MOP::Method::Accessor->meta->add_attribute( - Class::MOP::Attribute->new('$!is_inline' => ( - init_arg => 'is_inline', - reader => { 'is_inline' => \&Class::MOP::Method::Accessor::is_inline }, - )) -); ## -------------------------------------------------------- ## Class::MOP::Method::Constructor @@ -514,6 +518,8 @@ $_->meta->make_immutable( Class::MOP::Object + Class::MOP::Method::Generated + Class::MOP::Method::Accessor Class::MOP::Method::Constructor Class::MOP::Method::Wrapped diff --git a/lib/Class/MOP/Immutable.pm b/lib/Class/MOP/Immutable.pm index 3f4a4b0..5898faa 100644 --- a/lib/Class/MOP/Immutable.pm +++ b/lib/Class/MOP/Immutable.pm @@ -92,6 +92,7 @@ sub make_metaclass_immutable { $constructor_class->new( options => \%options, metaclass => $metaclass, + is_inline => 1, ) ) unless $metaclass->has_method($options{constructor_name}); } diff --git a/lib/Class/MOP/Method/Accessor.pm b/lib/Class/MOP/Method/Accessor.pm index 36b29a4..73d907a 100644 --- a/lib/Class/MOP/Method/Accessor.pm +++ b/lib/Class/MOP/Method/Accessor.pm @@ -7,10 +7,10 @@ use warnings; use Carp 'confess'; use Scalar::Util 'blessed', 'weaken'; -our $VERSION = '0.01'; +our $VERSION = '0.02'; our $AUTHORITY = 'cpan:STEVAN'; -use base 'Class::MOP::Method'; +use base 'Class::MOP::Method::Generated'; sub new { my $class = shift; @@ -39,7 +39,7 @@ sub new { # needed weaken($self->{'$!attribute'}); - $self->intialize_body; + $self->initialize_body; return $self; } @@ -48,11 +48,10 @@ sub new { sub associated_attribute { (shift)->{'$!attribute'} } sub accessor_type { (shift)->{'$!accessor_type'} } -sub is_inline { (shift)->{'$!is_inline'} } ## factory -sub intialize_body { +sub initialize_body { my $self = shift; my $method_name = join "_" => ( @@ -246,7 +245,7 @@ This returns the boolean which was passed into C. This returns the attribute instance which was passed into C. -=item B +=item B This will actually generate the method based on the specified criteria passed to the constructor. diff --git a/lib/Class/MOP/Method/Constructor.pm b/lib/Class/MOP/Method/Constructor.pm index 7f7cb81..9439beb 100644 --- a/lib/Class/MOP/Method/Constructor.pm +++ b/lib/Class/MOP/Method/Constructor.pm @@ -7,10 +7,10 @@ use warnings; use Carp 'confess'; use Scalar::Util 'blessed', 'weaken', 'looks_like_number'; -our $VERSION = '0.01'; +our $VERSION = '0.02'; our $AUTHORITY = 'cpan:STEVAN'; -use base 'Class::MOP::Method'; +use base 'Class::MOP::Method::Generated'; sub new { my $class = shift; @@ -23,11 +23,9 @@ sub new { # from our superclass '&!body' => undef, # specific to this subclass - '%!options' => $options{options}, - '$!meta_instance' => $options{metaclass}->get_meta_instance, - '@!attributes' => [ $options{metaclass}->compute_all_applicable_attributes ], - # ... + '%!options' => $options{options}, '$!associated_metaclass' => $options{metaclass}, + '$!is_inline' => ($options{is_inline} || 0), } => $class; # we don't want this creating @@ -35,40 +33,52 @@ sub new { # needed weaken($self->{'$!associated_metaclass'}); - $self->intialize_body; + $self->initialize_body; return $self; } -## predicates +## accessors -# NOTE: -# if it is blessed into this class, -# then it is always inlined, that is -# pretty much what this class is for. -sub is_inline { 1 } +sub options { (shift)->{'%!options'} } +sub associated_metaclass { (shift)->{'$!associated_metaclass'} } -## accessors +## cached values ... -sub options { (shift)->{'%!options'} } -sub meta_instance { (shift)->{'$!meta_instance'} } -sub attributes { (shift)->{'@!attributes'} } +sub meta_instance { + my $self = shift; + $self->{'$!meta_instance'} ||= $self->associated_metaclass->get_meta_instance; +} -sub associated_metaclass { (shift)->{'$!associated_metaclass'} } +sub attributes { + my $self = shift; + $self->{'@!attributes'} ||= [ $self->associated_metaclass->compute_all_applicable_attributes ] +} ## method -sub intialize_body { +sub initialize_body { + my $self = shift; + my $method_name = 'generate_constructor_method'; + + $method_name .= '_inline' if $self->is_inline; + + $self->{'&!body'} = $self->$method_name; +} + +sub generate_constructor_method { + return sub { (shift)->meta->new_object(@_) } +} + +sub generate_constructor_method_inline { my $self = shift; - # TODO: - # the %options should also include a both - # a call 'initializer' and call 'SUPER::' - # options, which should cover approx 90% - # of the possible use cases (even if it - # requires some adaption on the part of - # the author, after all, nothing is free) + my $source = 'sub {'; $source .= "\n" . 'my ($class, %params) = @_;'; + + $source .= "\n" . 'return $class->meta->new_object(%params)'; + $source .= "\n" . ' if $class ne \'' . $self->associated_metaclass->name . '\';'; + $source .= "\n" . 'my $instance = ' . $self->meta_instance->inline_create_instance('$class'); $source .= ";\n" . (join ";\n" => map { $self->_generate_slot_initializer($_) @@ -87,7 +97,7 @@ sub intialize_body { $code = eval $source; confess "Could not eval the constructor :\n\n$source\n\nbecause :\n\n$@" if $@; } - $self->{'&!body'} = $code; + return $code; } sub _generate_slot_initializer { @@ -182,12 +192,22 @@ metaclass which is passed into C. This returns a boolean, but since constructors are very rarely not inlined, this always returns true for now. -=item B +=item B This creates the code reference for the constructor itself. =back +=head2 Method Generators + +=over 4 + +=item B + +=item B + +=back + =head1 AUTHORS Stevan Little Estevan@iinteractive.comE diff --git a/lib/Class/MOP/Method/Generated.pm b/lib/Class/MOP/Method/Generated.pm new file mode 100644 index 0000000..afbc391 --- /dev/null +++ b/lib/Class/MOP/Method/Generated.pm @@ -0,0 +1,97 @@ + +package Class::MOP::Method::Generated; + +use strict; +use warnings; + +use Carp 'confess'; + +our $VERSION = '0.01'; +our $AUTHORITY = 'cpan:STEVAN'; + +use base 'Class::MOP::Method'; + +sub new { + my $class = shift; + my %options = @_; + + my $self = bless { + # from our superclass + '&!body' => undef, + # specific to this subclass + '$!is_inline' => ($options{is_inline} || 0), + } => $class; + + $self->initialize_body; + + return $self; +} + +## accessors + +sub is_inline { (shift)->{'$!is_inline'} } + +sub initialize_body { + confess "No body to initialize, " . __PACKAGE__ . " is an abstract base class"; +} + + + +1; + +__END__ + +=pod + +=head1 NAME + +Class::MOP::Method::Generated - Abstract base class for generated methods + +=head1 DESCRIPTION + +This is a C subclass which is used interally +by C and C. + +=head1 METHODS + +=over 4 + +=item B + +This creates the method based on the criteria in C<%options>, +these options are: + +=over 4 + +=item I + +This is a boolean to indicate if the method should be generated +as a closure, or as a more optimized inline version. + +=back + +=item B + +This returns the boolean which was passed into C. + +=item B + +This is an abstract method and will throw an exception if called. + +=back + +=head1 AUTHORS + +Stevan Little Estevan@iinteractive.comE + +=head1 COPYRIGHT AND LICENSE + +Copyright 2006, 2007 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. + +=cut + diff --git a/t/000_load.t b/t/000_load.t index dd828c9..b7d104a 100644 --- a/t/000_load.t +++ b/t/000_load.t @@ -3,7 +3,7 @@ use strict; use warnings; -use Test::More tests => 39; +use Test::More tests => 42; BEGIN { use_ok('Class::MOP'); @@ -14,6 +14,7 @@ BEGIN { use_ok('Class::MOP::Attribute'); use_ok('Class::MOP::Method'); use_ok('Class::MOP::Method::Wrapped'); + use_ok('Class::MOP::Method::Generated'); use_ok('Class::MOP::Method::Accessor'); use_ok('Class::MOP::Method::Constructor'); use_ok('Class::MOP::Instance'); @@ -26,6 +27,7 @@ my $CLASS_MOP_CLASS_IMMUTABLE_CLASS = 'Class::MOP::Class::__ANON__::SERIAL::1'; my %METAS = ( 'Class::MOP::Attribute' => Class::MOP::Attribute->meta, + 'Class::MOP::Method::Generated' => Class::MOP::Method::Generated->meta, 'Class::MOP::Method::Accessor' => Class::MOP::Method::Accessor->meta, 'Class::MOP::Method::Constructor' => Class::MOP::Method::Constructor->meta, 'Class::MOP::Package' => Class::MOP::Package->meta, @@ -59,6 +61,7 @@ is_deeply( Class::MOP::Method->meta, Class::MOP::Method::Accessor->meta, Class::MOP::Method::Constructor->meta, + Class::MOP::Method::Generated->meta, Class::MOP::Method::Wrapped->meta, Class::MOP::Module->meta, Class::MOP::Object->meta, @@ -74,7 +77,8 @@ is_deeply( Class::MOP::Instance Class::MOP::Method Class::MOP::Method::Accessor - Class::MOP::Method::Constructor + Class::MOP::Method::Constructor + Class::MOP::Method::Generated Class::MOP::Method::Wrapped Class::MOP::Module Class::MOP::Object @@ -91,7 +95,8 @@ is_deeply( "Class::MOP::Instance-" . $Class::MOP::Instance::VERSION . "-cpan:STEVAN", "Class::MOP::Method-" . $Class::MOP::Method::VERSION . "-cpan:STEVAN", "Class::MOP::Method::Accessor-" . $Class::MOP::Method::Accessor::VERSION . "-cpan:STEVAN", - "Class::MOP::Method::Constructor-" . $Class::MOP::Method::Constructor::VERSION . "-cpan:STEVAN", + "Class::MOP::Method::Constructor-" . $Class::MOP::Method::Constructor::VERSION . "-cpan:STEVAN", + "Class::MOP::Method::Generated-" . $Class::MOP::Method::Generated::VERSION . "-cpan:STEVAN", "Class::MOP::Method::Wrapped-" . $Class::MOP::Method::Wrapped::VERSION . "-cpan:STEVAN", "Class::MOP::Module-" . $Class::MOP::Module::VERSION . "-cpan:STEVAN", "Class::MOP::Object-" . $Class::MOP::Object::VERSION . "-cpan:STEVAN", diff --git a/t/072_immutable_w_constructors.t b/t/072_immutable_w_constructors.t index 70a59d1..cfa7d77 100644 --- a/t/072_immutable_w_constructors.t +++ b/t/072_immutable_w_constructors.t @@ -3,7 +3,7 @@ use strict; use warnings; -use Test::More tests => 73; +use Test::More tests => 77; use Test::Exception; BEGIN { @@ -87,8 +87,18 @@ BEGIN { my $foo = Foo->new(bar => 'BAZ'); isa_ok($foo, 'Foo'); is($foo->bar, 'BAZ', '... got the right parameter value'); + } + + # NOTE: + # check that the constructor correctly handles inheritance + { + my $bar = Bar->new(); + isa_ok($bar, 'Bar'); + isa_ok($bar, 'Foo'); + is($bar->bar, 'BAR', '... got the right inherited parameter value'); + is($bar->baz, 'BAZ', '... got the right inherited parameter value'); } - + # check out accessors too { my $bar_accessor = $meta->get_method('bar');