Make handles accept role TCs.
Florian Ragwitz [Fri, 26 Mar 2010 15:01:55 +0000 (16:01 +0100)]
Changes
lib/Moose.pm
lib/Moose/Meta/Attribute.pm
t/020_attributes/010_attribute_delegation.t

diff --git a/Changes b/Changes
index 165cd13..4f33010 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,22 +1,31 @@
 Also see Moose::Manual::Delta for more details of, and workarounds
 for, noteworthy changes.
 
-1.01 
+1.01
+
+  [NEW FEATURES]
+
+  * The handles option now also accepts a role type constraint in addition to a
+    plain role name. (Florian Ragwitz)
 
   [OTHER]
+
   * Record the Sartak/doy debt properly in Changes (perigrin)
 
 1.00 Tue, Mar 25, 2010
 
   [BUG FIXES]
+
   * Moose::Meta::Attribute::Native::Trait::Code no longer creates reader
     methods by default. (Florian Ragwitz)
 
   [DOCUMENTATION]
+
   * Improve various parts of the documentation and fix many typos.
     (Dave Rolsky, Mateu Hunter, Graham Knop, Robin V, Jay Hannah, Jesse Luehrs)
 
   [OTHER]
+
   * Paid the $10 debt to doy from 0.80 Sat, Jun 6, 2009 (Sartak)
 
 0.99 Mon, Mar 8, 2010
index 484364f..b6d32ae 100644 (file)
@@ -492,7 +492,7 @@ B<NOTE:> Triggers will only fire when you B<assign> to the attribute,
 either in the constructor, or using the writer. Default and built values will
 B<not> cause the trigger to be fired.
 
-=item I<handles =E<gt> ARRAY | HASH | REGEXP | ROLE | DUCKTYPE | CODE>
+=item I<handles =E<gt> ARRAY | HASH | REGEXP | ROLE | ROLETYPE | DUCKTYPE | CODE>
 
 The I<handles> option provides Moose classes with automated delegation features.
 This is a pretty complex and powerful option. It accepts many different option
@@ -588,13 +588,14 @@ B<NOTE:> An I<isa> option is required when using the regexp option format. This
 is so that we can determine (at compile time) the method list from the class.
 Without an I<isa> this is just not possible.
 
-=item C<ROLE>
+=item C<ROLE> or C<ROLETYPE>
 
-With the role option, you specify the name of a role whose "interface" then
-becomes the list of methods to handle. The "interface" can be defined as; the
-methods of the role and any required methods of the role. It should be noted
-that this does B<not> include any method modifiers or generated attribute
-methods (which is consistent with role composition).
+With the role option, you specify the name of a role or a
+L<role type|Moose::Meta::TypeConstraint::Role> whose "interface" then becomes
+the list of methods to handle. The "interface" can be defined as; the methods
+of the role and any required methods of the role. It should be noted that this
+does B<not> include any method modifiers or generated attribute methods (which
+is consistent with role composition).
 
 =item C<DUCKTYPE>
 
index b6abcdb..6da44fd 100644 (file)
@@ -659,23 +659,25 @@ sub _canonicalize_handles {
         elsif (blessed($handles) && $handles->isa('Moose::Meta::TypeConstraint::DuckType')) {
             return map { $_ => $_ } @{ $handles->methods };
         }
+        elsif (blessed($handles) && $handles->isa('Moose::Meta::TypeConstraint::Role')) {
+            $handles = $handles->role;
+        }
         else {
             $self->throw_error("Unable to canonicalize the 'handles' option with $handles", data => $handles);
         }
     }
-    else {
-        Class::MOP::load_class($handles);
-        my $role_meta = Class::MOP::class_of($handles);
 
-        (blessed $role_meta && $role_meta->isa('Moose::Meta::Role'))
-            || $self->throw_error("Unable to canonicalize the 'handles' option with $handles because its metaclass is not a Moose::Meta::Role", data => $handles);
+    Class::MOP::load_class($handles);
+    my $role_meta = Class::MOP::class_of($handles);
 
-        return map { $_ => $_ }
-            grep { $_ ne 'meta' } (
-            $role_meta->get_method_list,
-            map { $_->name } $role_meta->get_required_method_list,
-            );
-    }
+    (blessed $role_meta && $role_meta->isa('Moose::Meta::Role'))
+        || $self->throw_error("Unable to canonicalize the 'handles' option with $handles because its metaclass is not a Moose::Meta::Role", data => $handles);
+
+    return map { $_ => $_ }
+        grep { $_ ne 'meta' } (
+        $role_meta->get_method_list,
+        map { $_->name } $role_meta->get_required_method_list,
+        );
 }
 
 sub _find_delegate_metaclass {
index 16ec30e..bcd4cc7 100644 (file)
@@ -243,6 +243,15 @@ is($car->stop, 'Engine::stop', '... got the right value from ->stop');
         handles => 'Foo::Bar',
     );
 
+    package Foo::OtherThing;
+    use Moose;
+    use Moose::Util::TypeConstraints;
+
+    has 'other_thing' => (
+        is      => 'rw',
+        isa     => 'Foo::Baz',
+        handles => Moose::Util::TypeConstraints::find_type_constraint('Foo::Bar'),
+    );
 }
 
 {
@@ -259,6 +268,19 @@ is($car->stop, 'Engine::stop', '... got the right value from ->stop');
     is($foo->thing->baz, 'Foo::Baz::BAZ', '... got the right value');
 }
 
+{
+    my $foo = Foo::OtherThing->new(other_thing => Foo::Baz->new);
+    isa_ok($foo, 'Foo::OtherThing');
+    isa_ok($foo->other_thing, 'Foo::Baz');
+
+    ok($foo->meta->has_method('foo'), '... we have the method we expect');
+    ok($foo->meta->has_method('bar'), '... we have the method we expect');
+    ok(!$foo->meta->has_method('baz'), '... we dont have the method we expect');
+
+    is($foo->foo, 'Foo::Baz::FOO', '... got the right value');
+    is($foo->bar, 'Foo::Baz::BAR', '... got the right value');
+    is($foo->other_thing->baz, 'Foo::Baz::BAZ', '... got the right value');
+}
 # -------------------------------------------------------------------
 # AUTOLOAD & handles
 # -------------------------------------------------------------------