Allow required versions to be specified when setting superclasses and applying roles.
Florian Ragwitz [Wed, 5 May 2010 20:18:04 +0000 (22:18 +0200)]
Changes
lib/Moose.pm
lib/Moose/Meta/Class.pm
lib/Moose/Util.pm
t/010_basics/002_require_superclasses.t
t/030_roles/003_apply_role.t

diff --git a/Changes b/Changes
index 6bd1f8e..c937ea0 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,6 +1,11 @@
 Also see Moose::Manual::Delta for more details of, and workarounds
 for, noteworthy changes.
 
+  [NEW FEATURES]
+
+  * Allow specifying required versions when setting superclasses or applying
+    roles (Florian Ragwitz).
+
 1.02 Sat, May 01, 2010
 
   [BUG FIXES]
index 2332d27..2b1b567 100644 (file)
@@ -403,10 +403,31 @@ actually C<push>es onto the class's C<@ISA>, whereas C<extends> will
 replace it. This is important to ensure that classes which do not have
 superclasses still properly inherit from L<Moose::Object>.
 
+Each superclass can be followed by a hash reference with options. Currently,
+only L<-version|Class::MOP/Class Loading Options> is recognized:
+
+    extends 'My::Parent'      => { -version => 0.01 },
+            'My::OtherParent' => { -version => 0.03 };
+
+An exception will be thrown if the version requirements are not
+satisfied.
+
 =item B<with (@roles)>
 
 This will apply a given set of C<@roles> to the local class.
 
+Like with C<extends>, each specified role can be followed by a hash
+reference with a L<-version|Class::MOP/Class Loading Options> option:
+
+    with 'My::Role'      => { -version => 0.32 },
+         'My::Otherrole' => { -version => 0.23 };
+
+The specified version requirements must be satisfied, otherwise an
+exception will be thrown.
+
+If your role takes options or arguments, they can be passed along in the
+hash reference as well.
+
 =item B<has $name|@$names =E<gt> %options>
 
 This will install an attribute of a given C<$name> into the current class. If
index a46c270..de7fadb 100644 (file)
@@ -7,6 +7,7 @@ use warnings;
 use Class::MOP;
 
 use Carp ();
+use Data::OptList;
 use List::Util qw( first );
 use List::MoreUtils qw( any all uniq first_index );
 use Scalar::Util 'weaken', 'blessed';
@@ -279,14 +280,15 @@ sub new_object {
 
 sub superclasses {
     my $self = shift;
-    my @supers = @_;
-    foreach my $super (@supers) {
-        Class::MOP::load_class($super);
-        my $meta = Class::MOP::class_of($super);
-        $self->throw_error("You cannot inherit from a Moose Role ($super)")
+    my $supers = Data::OptList::mkopt(\@_);
+    foreach my $super (@{ $supers }) {
+        my ($name, $opts) = @{ $super };
+        Class::MOP::load_class($name, $opts);
+        my $meta = Class::MOP::class_of($name);
+        $self->throw_error("You cannot inherit from a Moose Role ($name)")
             if $meta && $meta->isa('Moose::Meta::Role')
     }
-    return $self->SUPER::superclasses(@supers);
+    return $self->SUPER::superclasses(map { $_->[0] } @{ $supers });
 }
 
 ### ---------------------------------------------
@@ -723,6 +725,14 @@ roles, it will be reused.
       cache        => 1,
   );
 
+Each entry in both the C<superclasses> and the C<roles> option can be
+followed by a hash reference with arguments. The C<supperclasses>
+option can be supplied with a L<-version|Class::MOP/Class Loading
+Options> option that ensures the loaded superclass satisfies the
+required version. The C<role> option also takes the C<-version> as an
+argument, but the option hash reference can also contain any other
+role relevant values like exclusions or parameterized role arguments.
+
 =item B<< $metaclass->make_immutable(%options) >>
 
 This overrides the parent's method to add a few options. Specifically,
@@ -737,6 +747,15 @@ C<inline_accessors> option to false.
 This overrides the parent's method in order to add support for
 attribute triggers.
 
+=item B<< $metaclass->superclasses(@superclasses) >>
+
+This is the accesssor allowing you to read or change the parents of
+the class.
+
+Each superclass can be followed by a hash reference containing a
+L<-version|Class::MOP/Class Loading Options> value. If the version
+requirement is not satisfied an error will be thrown.
+
 =item B<< $metaclass->add_override_method_modifier($name, $sub) >>
 
 This adds an C<override> method modifier to the package.
index 13c52aa..82ee65c 100644 (file)
@@ -105,7 +105,7 @@ sub _apply_all_roles {
             $meta = $role->[0];
         }
         else {
-            Class::MOP::load_class( $role->[0] );
+            Class::MOP::load_class( $role->[0] , $role->[1] );
             $meta = Class::MOP::class_of( $role->[0] );
         }
 
index 9757c35..ad09165 100644 (file)
@@ -44,4 +44,26 @@ use Test::Exception;
     'correct error when superclass could not be found';
 }
 
+{
+    package Affe;
+    our $VERSION = 23;
+}
+
+{
+    package Tiger;
+    use Moose;
+
+    ::lives_ok { extends 'Foo', Affe => { -version => 13 } }
+    'extends with version requirement';
+}
+
+{
+    package Birne;
+    use Moose;
+
+    ::throws_ok { extends 'Foo', Affe => { -version => 42 } }
+    qr/Affe version 42 required--this is only version 23/,
+    'extends with unsatisfied version requirement';
+}
+
 done_testing;
index d474213..e58c0e2 100644 (file)
@@ -10,6 +10,8 @@ use Test::Exception;
     package FooRole;
     use Moose::Role;
 
+    our $VERSION = 23;
+
     has 'bar' => ( is => 'rw', isa => 'FooClass' );
     has 'baz' => ( is => 'ro' );
 
@@ -43,7 +45,13 @@ use Test::Exception;
     use Moose;
 
     extends 'BarClass';
-    with 'FooRole';
+
+    ::throws_ok { with 'FooRole' => { -version => 42 } }
+        qr/FooRole version 42 required--this is only version 23/,
+        'applying role with unsatisfied version requirement';
+
+    ::lives_ok { with 'FooRole' => { -version => 13 } }
+        'applying role with satisfied version requirement';
 
     sub blau {'FooClass::blau'}    # << the role wraps this ...