X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMouse%2FMeta%2FRole.pm;h=95bdca80506de5774678f1d939cd7415b817a515;hb=b4160dbe7bdfeacc3f559cf3eb9b792a5794a62b;hp=90370443e3716c13df4cd008281c69778ede0a9f;hpb=2e92bb89f22acc49ce81b6ec6593d6190559ac45;p=gitmo%2FMouse.git diff --git a/lib/Mouse/Meta/Role.pm b/lib/Mouse/Meta/Role.pm index 9037044..95bdca8 100644 --- a/lib/Mouse/Meta/Role.pm +++ b/lib/Mouse/Meta/Role.pm @@ -1,4 +1,3 @@ -#!/usr/bin/env perl package Mouse::Meta::Role; use strict; use warnings; @@ -30,6 +29,7 @@ sub new { $args{attributes} ||= {}; $args{required_methods} ||= []; + $args{roles} ||= []; bless \%args, $class; } @@ -60,8 +60,8 @@ sub get_method_list { no strict 'refs'; # Get all the CODE symbol table entries - my @functions = grep !/^meta$/, - grep { /\A[^\W\d]\w*\z/o } + my @functions = + grep !/^(?:has|with|around|before|after|blessed|extends|confess|excludes|meta|requires)$/, grep { defined &{"${name}::$_"} } keys %{"${name}::"}; wantarray ? @functions : \@functions; @@ -72,31 +72,54 @@ sub apply { my $selfname = $self->name; my $class = shift; my $classname = $class->name; + my %args = @_; - for my $name (@{$self->{required_methods}}) { - unless ($classname->can($name)) { - confess "'$selfname' requires the method '$name' to be implemented by '$classname'"; + if ($class->isa('Mouse::Meta::Class')) { + for my $name (@{$self->{required_methods}}) { + unless ($classname->can($name)) { + confess "'$selfname' requires the method '$name' to be implemented by '$classname'"; + } } } { no strict 'refs'; for my $name ($self->get_method_list) { - next if $name eq 'has' || $name eq 'requires' || $name eq 'meta' || $name eq 'with' || $name eq 'around' || $name eq 'before' || $name eq 'after' || $name eq 'blessed' || $name eq 'extends' || $name eq 'confess' || $name eq 'excludes'; + next if $name eq 'meta'; + if ($classname->can($name)) { # XXX what's Moose's behavior? - next; + #next; + } else { + *{"${classname}::${name}"} = *{"${selfname}::${name}"}; + } + if ($args{alias} && $args{alias}->{$name}) { + my $dstname = $args{alias}->{$name}; + unless ($classname->can($dstname)) { + *{"${classname}::${dstname}"} = *{"${selfname}::${name}"}; + } } - *{"${classname}::${name}"} = *{"${selfname}::${name}"}; } } - for my $name ($self->get_attribute_list) { - next if $class->has_attribute($name); - my $spec = $self->get_attribute($name); - Mouse::Meta::Attribute->create($class, $name, %$spec); + if ($class->isa('Mouse::Meta::Class')) { + # apply role to class + for my $name ($self->get_attribute_list) { + next if $class->has_attribute($name); + my $spec = $self->get_attribute($name); + Mouse::Meta::Attribute->create($class, $name, %$spec); + } + } else { + # apply role to role + # XXX Room for speed improvement + for my $name ($self->get_attribute_list) { + next if $class->has_attribute($name); + my $spec = $self->get_attribute($name); + $class->add_attribute($name, $spec); + } } + # XXX Room for speed improvement in role to role for my $modifier_type (qw/before after around/) { my $add_method = "add_${modifier_type}_method_modifier"; my $modified = $self->{"${modifier_type}_method_modifiers"}; @@ -107,6 +130,105 @@ sub apply { } } } + + # append roles + push @{ $class->roles }, $self, @{ $self->roles }; +} + +sub combine_apply { + my(undef, $class, @roles) = @_; + my $classname = $class->name; + + if ($class->isa('Mouse::Meta::Class')) { + for my $role_spec (@roles) { + my $self = $role_spec->[0]->meta; + for my $name (@{$self->{required_methods}}) { + unless ($classname->can($name)) { + my $method_required = 0; + for my $role (@roles) { + $method_required = 1 if $self->name ne $role->[0] && $role->[0]->can($name); + } + confess "'".$self->name."' requires the method '$name' to be implemented by '$classname'" + unless $method_required; + } + } + } + } + + { + no strict 'refs'; + for my $role_spec (@roles) { + my $self = $role_spec->[0]->meta; + my $selfname = $self->name; + my %args = %{ $role_spec->[1] }; + for my $name ($self->get_method_list) { + next if $name eq 'meta'; + + if ($classname->can($name)) { + # XXX what's Moose's behavior? + #next; + } else { + *{"${classname}::${name}"} = *{"${selfname}::${name}"}; + } + if ($args{alias} && $args{alias}->{$name}) { + my $dstname = $args{alias}->{$name}; + unless ($classname->can($dstname)) { + *{"${classname}::${dstname}"} = *{"${selfname}::${name}"}; + } + } + } + } + } + + + if ($class->isa('Mouse::Meta::Class')) { + # apply role to class + for my $role_spec (@roles) { + my $self = $role_spec->[0]->meta; + for my $name ($self->get_attribute_list) { + next if $class->has_attribute($name); + my $spec = $self->get_attribute($name); + Mouse::Meta::Attribute->create($class, $name, %$spec); + } + } + } else { + # apply role to role + # XXX Room for speed improvement + for my $role_spec (@roles) { + my $self = $role_spec->[0]->meta; + for my $name ($self->get_attribute_list) { + next if $class->has_attribute($name); + my $spec = $self->get_attribute($name); + $class->add_attribute($name, $spec); + } + } + } + + # XXX Room for speed improvement in role to role + for my $modifier_type (qw/before after around/) { + my $add_method = "add_${modifier_type}_method_modifier"; + for my $role_spec (@roles) { + my $self = $role_spec->[0]->meta; + my $modified = $self->{"${modifier_type}_method_modifiers"}; + + for my $method_name (keys %$modified) { + for my $code (@{ $modified->{$method_name} }) { + $class->$add_method($method_name => $code); + } + } + } + } + + # append roles + my %role_apply_cache; + my @apply_roles; + for my $role_spec (@roles) { + my $self = $role_spec->[0]->meta; + push @apply_roles, $self unless $role_apply_cache{$self}++; + for my $role ($self->roles) { + push @apply_roles, $role unless $role_apply_cache{$role}++; + } + } } for my $modifier_type (qw/before after around/) { @@ -124,5 +246,7 @@ for my $modifier_type (qw/before after around/) { }; } +sub roles { $_[0]->{roles} } + 1;