Optimize Method::Delegation
gfx [Mon, 22 Feb 2010 07:04:42 +0000 (16:04 +0900)]
lib/Mouse/Meta/Method/Delegation.pm
t/001_mouse/019-handles.t

index 51e4999..60bdad8 100644 (file)
@@ -3,26 +3,47 @@ use Mouse::Util qw(:meta); # enables strict and warnings
 use Scalar::Util;
 
 sub _generate_delegation{
-    my (undef, $attribute, $handle_name, $method_to_call, @curried_args) = @_;
-
-    my $reader = $attribute->get_read_method_ref();
-    return sub {
-        my $instance = shift;
-        my $proxy    = $instance->$reader();
-
-        my $error = !defined($proxy)                              ? ' is not defined'
-                  : ref($proxy) && !Scalar::Util::blessed($proxy) ? qq{ is not an object (got '$proxy')}
-                                                                  : undef;
-        if ($error) {
-            $instance->meta->throw_error(
-                "Cannot delegate $handle_name to $method_to_call because "
-                    . "the value of "
-                    . $attribute->name
-                    . $error
-             );
-        }
-        $proxy->$method_to_call(@curried_args, @_);
-    };
+    my (undef, $attr, $handle_name, $method_to_call, @curried_args) = @_;
+
+    my $reader = $attr->get_read_method_ref();
+
+    my $can_be_optimized = $attr->{_method_delegation_can_be_optimized};
+
+    if(!defined $can_be_optimized){
+        my $tc     = $attr->type_constraint;
+
+        $attr->{_method_delegation_can_be_optimized} =
+            (defined($tc) && $tc->is_a_type_of('Object'))
+            && ($attr->is_required || $attr->has_default || $attr->has_builder)
+            && ($attr->is_lazy || !$attr->has_clearer);
+    }
+
+    if($can_be_optimized){
+        # need not check the attribute value
+        return sub {
+            return shift()->$reader()->$method_to_call(@curried_args, @_);
+        };
+    }
+    else {
+        # need to check the attribute value
+        return sub {
+            my $instance = shift;
+            my $proxy    = $instance->$reader();
+
+            my $error = !defined($proxy)                              ? ' is not defined'
+                      : ref($proxy) && !Scalar::Util::blessed($proxy) ? qq{ is not an object (got '$proxy')}
+                                                                      : undef;
+            if ($error) {
+                $instance->meta->throw_error(
+                    "Cannot delegate $handle_name to $method_to_call because "
+                        . "the value of "
+                        . $attr->name
+                        . $error
+                 );
+            }
+            $proxy->$method_to_call(@curried_args, @_);
+        };
+    }
 }
 
 
index 5bae5a9..3dbf314 100644 (file)
@@ -1,21 +1,15 @@
 #!/usr/bin/env perl
 use strict;
 use warnings;
-use Test::More tests => 26;
+use Test::More;
 use Test::Exception;
 
 do {
     package Person;
+    use Mouse;
 
-    sub new {
-        my $class = shift;
-        my %args  = @_;
-
-        bless \%args, $class;
-    }
-
-    sub name { $_[0]->{name} = $_[1] if @_ > 1; $_[0]->{name} }
-    sub age { $_[0]->{age} = $_[1] if @_ > 1; $_[0]->{age} }
+    has name => (is => 'rw');
+    has age  => (is => 'rw');
 
     sub make_string {
         my($self, $template) = @_;
@@ -38,11 +32,13 @@ do {
     );
 
     has me => (
-        is => 'rw',
+        is  => 'rw',
+        isa => 'Person',
         default => sub { Person->new(age => 21, name => "Shawn") },
         predicate => 'quid',
         handles => [qw/name age/],
     );
+
 };
 
 can_ok(Class => qw(person has_person person_name person_age name age quid));
@@ -85,29 +81,26 @@ is_deeply(
     "correct handles layout for 'person'",
 );
 
+throws_ok{
+    $object->person(undef);
+    $object->person_name();
+} qr/Cannot delegate person_name to name because the value of person is not defined/;
 
-{
-    throws_ok{
-        $object->person(undef);
-        $object->person_name();
-    } qr/Cannot delegate person_name to name because the value of person is not defined/;
-
-    throws_ok{
-        $object->person([]);
-        $object->person_age();
-    } qr/Cannot delegate person_age to age because the value of person is not an object/;
-}
+throws_ok{
+    $object->person([]);
+    $object->person_age();
+} qr/Cannot delegate person_age to age because the value of person is not an object/;
 
-eval{
+throws_ok{
     $object->person(undef);
     $object->person_name();
-};
-like $@, qr/Cannot delegate person_name to name because the value of person is not defined/;
+} qr/Cannot delegate person_name to name because the value of person is not defined/;
 
-eval{
+throws_ok{
     $object->person([]);
     $object->person_age();
-};
-like $@, qr/Cannot delegate person_age to age because the value of person is not an object/;
+} qr/Cannot delegate person_age to age because the value of person is not an object/;
+
 
+done_testing;