bump version and update Changes for release
[gitmo/Moose.git] / lib / Moose / Meta / Role.pm
index 4f19eb8..f1fd0c7 100644 (file)
@@ -5,10 +5,11 @@ use strict;
 use warnings;
 use metaclass;
 
-use Carp         'confess';
 use Scalar::Util 'blessed';
+use Carp         'confess';
 
-our $VERSION   = '0.56';
+our $VERSION   = '0.72';
+$VERSION = eval $VERSION;
 our $AUTHORITY = 'cpan:STEVAN';
 
 use Moose::Meta::Class;
@@ -121,6 +122,10 @@ foreach my $action (
 sub add_attribute {
     my $self = shift;
     my $name = shift;
+    unless ( defined $name && $name ) {
+        require Moose;
+        Moose->throw_error("You must provide a name for the attribute");
+    }
     my $attr_desc;
     if (scalar @_ == 1 && ref($_[0]) eq 'HASH') {
         $attr_desc = $_[0];
@@ -214,8 +219,8 @@ $META->add_attribute('override_method_modifiers' => (
 sub add_override_method_modifier {
     my ($self, $method_name, $method) = @_;
     (!$self->has_method($method_name))
-        || confess "Cannot add an override of method '$method_name' " .
-                   "because there is a local version of '$method_name'";
+        || Moose->throw_error("Cannot add an override of method '$method_name' " .
+                   "because there is a local version of '$method_name'");
     $self->get_override_method_modifiers_map->{$method_name} = $method;
 }
 
@@ -259,8 +264,9 @@ __PACKAGE__->meta->add_attribute('roles' => (
 sub add_role {
     my ($self, $role) = @_;
     (blessed($role) && $role->isa('Moose::Meta::Role'))
-        || confess "Roles must be instances of Moose::Meta::Role";
+        || Moose->throw_error("Roles must be instances of Moose::Meta::Role");
     push @{$self->get_roles} => $role;
+    $self->reset_package_cache_flag;
 }
 
 sub calculate_all_roles {
@@ -276,7 +282,7 @@ sub calculate_all_roles {
 sub does_role {
     my ($self, $role_name) = @_;
     (defined $role_name)
-        || confess "You must supply a role name to look for";
+        || Moose->throw_error("You must supply a role name to look for");
     # if we are it,.. then return true
     return 1 if $role_name eq $self->name;
     # otherwise.. check our children
@@ -307,10 +313,10 @@ sub get_method_map {
     my $role_name        = $self->name;
     my $method_metaclass = $self->method_metaclass;
 
-    my %all_code = $self->get_all_package_symbols('CODE');
+    my $all_code = $self->get_all_package_symbols('CODE');
 
-    foreach my $symbol (keys %all_code) {
-        my $code = $all_code{$symbol};
+    foreach my $symbol (keys %{ $all_code }) {
+        my $code = $all_code->{$symbol};
 
         next if exists  $map->{$symbol} &&
                 defined $map->{$symbol} &&
@@ -365,38 +371,32 @@ sub has_method {
     exists $self->get_method_map->{$name} ? 1 : 0
 }
 
-# FIXME this is copypasated from Class::MOP::Class
+# FIXME this is copy-pasted from Class::MOP::Class
 # refactor to inherit from some common base
 sub wrap_method_body {
     my ( $self, %args ) = @_;
 
-    my $body = delete $args{body}; # delete is for compat
-
-    ('CODE' eq ref($body))
-        || confess "Your code block must be a CODE reference";
+    ('CODE' eq ref $args{body})
+        || Moose->throw_error("Your code block must be a CODE reference");
 
-    $self->method_metaclass->wrap( $body => (
+    $self->method_metaclass->wrap(
         package_name => $self->name,
         %args,
-    ));
+    );
 }
 
 sub add_method {
     my ($self, $method_name, $method) = @_;
     (defined $method_name && $method_name)
-    || confess "You must define a method name";
+    || Moose->throw_error("You must define a method name");
 
     my $body;
     if (blessed($method)) {
         $body = $method->body;
-        if ($method->package_name ne $self->name &&
-            $method->name         ne $method_name) {
-            warn "Hello there, got something for you."
-            . " Method says " . $method->package_name . " " . $method->name
-            . " Class says " . $self->name . " " . $method_name;
+        if ($method->package_name ne $self->name) {
             $method = $method->clone(
                 package_name => $self->name,
-                name         => $method_name
+                name         => $method_name            
             ) if $method->can('clone');
         }
     }
@@ -426,18 +426,9 @@ sub get_method_list {
 }
 
 sub alias_method {
-    my ($self, $method_name, $method) = @_;
-    (defined $method_name && $method_name)
-        || confess "You must define a method name";
-
-    my $body = (blessed($method) ? $method->body : $method);
-    ('CODE' eq ref($body))
-        || confess "Your code block must be a CODE reference";
+    my $self = shift;
 
-    $self->add_package_symbol(
-        { sigil => '&', type => 'CODE', name => $method_name },
-        $body
-    );
+    $self->add_method(@_);
 }
 
 ## ------------------------------------------------------------------
@@ -448,7 +439,7 @@ sub apply {
     my ($self, $other, @args) = @_;
 
     (blessed($other))
-        || confess "You must pass in an blessed instance";
+        || Moose->throw_error("You must pass in an blessed instance");
         
     if ($other->isa('Moose::Meta::Role')) {
         require Moose::Meta::Role::Application::ToRole;
@@ -464,16 +455,6 @@ sub apply {
     }  
 }
 
-sub apply_to_metaclass_instance {
-    my ($self, $meta, @args) = @_;
-
-    $meta->isa('Moose::Meta::Class') || $meta->isa('Moose::Meta::Role')
-        || confess "You must pass in a Moose::Meta::Class or Moose::Meta::Role instance";
-
-    require Moose::Meta::Role::Application::ToMetaclassInstance;
-    return Moose::Meta::Role::Application::ToMetaclassInstance->new(@args)->apply($self, $meta);
-}
-
 sub combine {
     my ($class, @role_specs) = @_;
     
@@ -496,6 +477,119 @@ sub combine {
     return $c;
 }
 
+sub create {
+    my ( $role, $package_name, %options ) = @_;
+
+    $options{package} = $package_name;
+
+    (ref $options{attributes} eq 'HASH')
+        || confess "You must pass a HASH ref of attributes"
+            if exists $options{attributes};
+
+    (ref $options{methods} eq 'HASH')
+        || confess "You must pass a HASH ref of methods"
+            if exists $options{methods};
+
+    $role->SUPER::create(%options);
+
+    my (%initialize_options) = %options;
+    delete @initialize_options{qw(
+        package
+        attributes
+        methods
+        version
+        authority
+    )};
+
+    my $meta = $role->initialize( $package_name => %initialize_options );
+
+    # FIXME totally lame
+    $meta->add_method('meta' => sub {
+        $role->initialize(ref($_[0]) || $_[0]);
+    });
+
+    if (exists $options{attributes}) {
+        foreach my $attribute_name (keys %{$options{attributes}}) {
+            my $attr = $options{attributes}->{$attribute_name};
+            $meta->add_attribute($attribute_name => $attr);
+        }
+    }
+
+    if (exists $options{methods}) {
+        foreach my $method_name (keys %{$options{methods}}) {
+            $meta->add_method($method_name, $options{methods}->{$method_name});
+        }
+    }
+
+    Class::MOP::weaken_metaclass($meta->name)
+        if $meta->is_anon_role;
+
+    return $meta;
+}
+
+# anonymous roles. most of it is copied straight out of Class::MOP::Class.
+# an intrepid hacker might find great riches if he unifies this code with that
+# code in Class::MOP::Module or Class::MOP::Package
+{
+    # NOTE:
+    # this should be sufficient, if you have a
+    # use case where it is not, write a test and
+    # I will change it.
+    my $ANON_ROLE_SERIAL = 0;
+
+    # NOTE:
+    # we need a sufficiently annoying prefix
+    # this should suffice for now, this is
+    # used in a couple of places below, so
+    # need to put it up here for now.
+    my $ANON_ROLE_PREFIX = 'Moose::Meta::Role::__ANON__::SERIAL::';
+
+    sub is_anon_role {
+        my $self = shift;
+        no warnings 'uninitialized';
+        $self->name =~ /^$ANON_ROLE_PREFIX/;
+    }
+
+    sub create_anon_role {
+        my ($role, %options) = @_;
+        my $package_name = $ANON_ROLE_PREFIX . ++$ANON_ROLE_SERIAL;
+        return $role->create($package_name, %options);
+    }
+
+    # NOTE:
+    # this will only get called for
+    # anon-roles, all other calls
+    # are assumed to occur during
+    # global destruction and so don't
+    # really need to be handled explicitly
+    sub DESTROY {
+        my $self = shift;
+
+        return if Class::MOP::in_global_destruction(); # it'll happen soon anyway and this just makes things more complicated
+
+        no warnings 'uninitialized';
+        return unless $self->name =~ /^$ANON_ROLE_PREFIX/;
+
+        # XXX: is this necessary for us? I don't understand what it's doing
+        # -sartak
+
+        # Moose does a weird thing where it replaces the metaclass for
+        # class when fixing metaclass incompatibility. In that case,
+        # we don't want to clean out the namespace now. We can detect
+        # that because Moose will explicitly update the singleton
+        # cache in Class::MOP.
+        #my $current_meta = Class::MOP::get_metaclass_by_name($self->name);
+        #return if $current_meta ne $self;
+
+        my ($serial_id) = ($self->name =~ /^$ANON_ROLE_PREFIX(\d+)/);
+        no strict 'refs';
+        foreach my $key (keys %{$ANON_ROLE_PREFIX . $serial_id}) {
+            delete ${$ANON_ROLE_PREFIX . $serial_id}{$key};
+        }
+        delete ${'main::' . $ANON_ROLE_PREFIX}{$serial_id . '::'};
+    }
+}
+
 #####################################################################
 ## NOTE:
 ## This is Moose::Meta::Role as defined by Moose (plus the use of 
@@ -790,6 +884,16 @@ probably not that much really).
 
 =back
 
+=over 4
+
+=item B<create>
+
+=item B<create_anon_role>
+
+=item B<is_anon_role>
+
+=back
+
 =head1 BUGS
 
 All complex software has bugs lurking in it, and this module is no
@@ -802,7 +906,7 @@ Stevan Little E<lt>stevan@iinteractive.comE<gt>
 
 =head1 COPYRIGHT AND LICENSE
 
-Copyright 2006-2008 by Infinity Interactive, Inc.
+Copyright 2006-2009 by Infinity Interactive, Inc.
 
 L<http://www.iinteractive.com>