- added Moose::Util::apply_all_roles
[gitmo/Mouse.git] / lib / Mouse / Meta / Role.pm
1 #!/usr/bin/env perl
2 package Mouse::Meta::Role;
3 use strict;
4 use warnings;
5 use Carp 'confess';
6
7 do {
8     my %METACLASS_CACHE;
9
10     # because Mouse doesn't introspect existing classes, we're forced to
11     # only pay attention to other Mouse classes
12     sub _metaclass_cache {
13         my $class = shift;
14         my $name  = shift;
15         return $METACLASS_CACHE{$name};
16     }
17
18     sub initialize {
19         my $class = shift;
20         my $name  = shift;
21         $METACLASS_CACHE{$name} = $class->new(name => $name)
22             if !exists($METACLASS_CACHE{$name});
23         return $METACLASS_CACHE{$name};
24     }
25 };
26
27 sub new {
28     my $class = shift;
29     my %args  = @_;
30
31     $args{attributes}       ||= {};
32     $args{required_methods} ||= [];
33
34     bless \%args, $class;
35 }
36
37 sub name { $_[0]->{name} }
38
39 sub add_required_methods {
40     my $self = shift;
41     my @methods = @_;
42     push @{$self->{required_methods}}, @methods;
43 }
44
45 sub add_attribute {
46     my $self = shift;
47     my $name = shift;
48     my $spec = shift;
49     $self->{attributes}->{$name} = $spec;
50 }
51
52 sub has_attribute { exists $_[0]->{attributes}->{$_[1]}  }
53 sub get_attribute_list { keys %{ $_[0]->{attributes} } }
54 sub get_attribute { $_[0]->{attributes}->{$_[1]} }
55
56 # copied from Class::Inspector
57 sub get_method_list {
58     my $self = shift;
59     my $name = $self->name;
60
61     no strict 'refs';
62     # Get all the CODE symbol table entries
63     my @functions = grep !/^meta$/,
64       grep { /\A[^\W\d]\w*\z/o }
65       grep { defined &{"${name}::$_"} }
66       keys %{"${name}::"};
67     wantarray ? @functions : \@functions;
68 }
69
70 sub apply {
71     my $self  = shift;
72     my $selfname = $self->name;
73     my $class = shift;
74     my $classname = $class->name;
75
76     for my $name (@{$self->{required_methods}}) {
77         unless ($classname->can($name)) {
78             confess "'$selfname' requires the method '$name' to be implemented by '$classname'";
79         }
80     }
81
82     {
83         no strict 'refs';
84         for my $name ($self->get_method_list) {
85             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';
86             if ($classname->can($name)) {
87                 # XXX what's Moose's behavior?
88                 next;
89             }
90             *{"${classname}::${name}"} = *{"${selfname}::${name}"};
91         }
92     }
93
94     for my $name ($self->get_attribute_list) {
95         next if $class->has_attribute($name);
96         my $spec = $self->get_attribute($name);
97         Mouse::Meta::Attribute->create($class, $name, %$spec);
98     }
99
100     for my $modifier_type (qw/before after around/) {
101         my $add_method = "add_${modifier_type}_method_modifier";
102         my $modified = $self->{"${modifier_type}_method_modifiers"};
103
104         for my $method_name (keys %$modified) {
105             for my $code (@{ $modified->{$method_name} }) {
106                 $class->$add_method($method_name => $code);
107             }
108         }
109     }
110 }
111
112 for my $modifier_type (qw/before after around/) {
113     no strict 'refs';
114     *{ __PACKAGE__ . '::' . "add_${modifier_type}_method_modifier" } = sub {
115         my ($self, $method_name, $method) = @_;
116
117         push @{ $self->{"${modifier_type}_method_modifiers"}->{$method_name} },
118             $method;
119     };
120
121     *{ __PACKAGE__ . '::' . "get_${modifier_type}_method_modifiers" } = sub {
122         my ($self, $method_name, $method) = @_;
123         @{ $self->{"${modifier_type}_method_modifiers"}->{$method_name} || [] }
124     };
125 }
126
127 1;
128