Tidy
[gitmo/Mouse.git] / lib / Mouse / Meta / Module.pm
1 package Mouse::Meta::Module;
2 use Mouse::Util qw/:meta get_code_package get_code_ref not_supported/; # enables strict and warnings
3
4 use Carp         ();
5 use Scalar::Util ();
6
7 my %METAS;
8
9 if(Mouse::Util::MOUSE_XS){
10     # register meta storage for performance
11     Mouse::Util::__register_metaclass_storage(\%METAS, 0);
12
13     # ensure thread safety
14     *CLONE = sub { Mouse::Util::__register_metaclass_storage(\%METAS, 1) };
15 }
16
17 sub _metaclass_cache { # DEPRECATED
18     my($self, $name) = @_;
19     Carp::cluck('_metaclass_cache() has been deprecated. Use Mouse::Util::get_metaclass_by_name() instead');
20     return $METAS{$name};
21 }
22
23 sub initialize {
24     my($class, $package_name, @args) = @_;
25
26     ($package_name && !ref($package_name))
27         || $class->throw_error("You must pass a package name and it cannot be blessed");
28
29     return $METAS{$package_name}
30         ||= $class->_construct_meta(package => $package_name, @args);
31 }
32
33 sub reinitialize {
34     my($class, $package_name, @args) = @_;
35
36     $package_name = $package_name->name if ref $package_name;
37
38     ($package_name && !ref($package_name))
39         || $class->throw_error("You must pass a package name and it cannot be blessed");
40
41     delete $METAS{$package_name};
42     return $class->initialize($package_name, @args);
43 }
44
45 sub _class_of{
46     my($class_or_instance) = @_;
47     return undef unless defined $class_or_instance;
48     return $METAS{ ref($class_or_instance) || $class_or_instance };
49 }
50
51 # Means of accessing all the metaclasses that have
52 # been initialized thus far
53 #sub _get_all_metaclasses         {        %METAS         }
54 sub _get_all_metaclass_instances { values %METAS         }
55 sub _get_all_metaclass_names     { keys   %METAS         }
56 sub _get_metaclass_by_name       { $METAS{$_[0]}         }
57 #sub _store_metaclass_by_name     { $METAS{$_[0]} = $_[1] }
58 #sub _weaken_metaclass            { weaken($METAS{$_[0]}) }
59 #sub _does_metaclass_exist        { defined $METAS{$_[0]} }
60 #sub _remove_metaclass_by_name    { delete $METAS{$_[0]}  }
61
62 sub name;
63
64 sub namespace;
65
66 # add_attribute is an abstract method
67
68 sub get_attribute_map { # DEPRECATED
69     Carp::cluck('get_attribute_map() has been deprecated. Use get_attribute_list() and get_attribute() instead');
70     return $_[0]->{attributes};
71 }
72
73 sub has_attribute     { exists $_[0]->{attributes}->{$_[1]} }
74 sub get_attribute     {        $_[0]->{attributes}->{$_[1]} }
75 sub remove_attribute  { delete $_[0]->{attributes}->{$_[1]} }
76
77 sub get_attribute_list{ keys   %{$_[0]->{attributes}} }
78
79 # XXX: for backward compatibility
80 my %foreign = map{ $_ => undef } qw(
81     Mouse Mouse::Role Mouse::Util Mouse::Util::TypeConstraints
82     Carp Scalar::Util List::Util
83 );
84 sub _code_is_mine{
85 #    my($self, $code) = @_;
86
87     return !exists $foreign{ get_code_package($_[1]) };
88 }
89
90 sub add_method;
91
92 sub has_method {
93     my($self, $method_name) = @_;
94
95     defined($method_name)
96         or $self->throw_error('You must define a method name');
97
98     return defined($self->{methods}{$method_name}) || do{
99         my $code = get_code_ref($self->{package}, $method_name);
100         $code && $self->_code_is_mine($code);
101     };
102 }
103
104 sub get_method_body {
105     my($self, $method_name) = @_;
106
107     defined($method_name)
108         or $self->throw_error('You must define a method name');
109
110     return $self->{methods}{$method_name} ||= do{
111         my $code = get_code_ref($self->{package}, $method_name);
112         $code && $self->_code_is_mine($code) ? $code : undef;
113     };
114 }
115
116 sub get_method{
117     my($self, $method_name) = @_;
118
119     if(my $code = $self->get_method_body($method_name)){
120         return Mouse::Util::load_class($self->method_metaclass)->wrap(
121             body                 => $code,
122             name                 => $method_name,
123             package              => $self->name,
124             associated_metaclass => $self,
125         );
126     }
127
128     return undef;
129 }
130
131 sub get_method_list {
132     my($self) = @_;
133
134     return grep { $self->has_method($_) } keys %{ $self->namespace };
135 }
136
137 sub _collect_methods { # Mouse specific
138     my($meta, @args) = @_;
139
140     my @methods;
141     foreach my $arg(@args){
142         if(my $type = ref $arg){
143             if($type eq 'Regexp'){
144                 push @methods, grep { $_ =~ $arg } $meta->get_all_method_names;
145             }
146             elsif($type eq 'ARRAY'){
147                 push @methods, @{$arg};
148             }
149             else{
150                 my $subname = ( caller(1) )[3];
151                 $meta->throw_error(
152                     sprintf(
153                         'Methods passed to %s must be provided as a list,'
154                         . ' ArrayRef or regular expression, not %s',
155                         $subname,
156                         $type,
157                     )
158                 );
159             }
160          }
161          else{
162             push @methods, $arg;
163          }
164      }
165      return @methods;
166 }
167
168 my $ANON_SERIAL = 0;  # anonymous class/role id
169 my %IMMORTALS;        # immortal anonymous classes
170
171 sub create {
172     my($self, $package_name, %options) = @_;
173
174     my $class = ref($self) || $self;
175     $self->throw_error('You must pass a package name') if @_ < 2;
176
177     my $superclasses;
178     if(exists $options{superclasses}){
179         if(Mouse::Util::is_a_metarole($self)){
180             delete $options{superclasses};
181         }
182         else{
183             $superclasses = delete $options{superclasses};
184             (ref $superclasses eq 'ARRAY')
185                 || $self->throw_error("You must pass an ARRAY ref of superclasses");
186         }
187     }
188
189     my $attributes = delete $options{attributes};
190     if(defined $attributes){
191         (ref $attributes eq 'ARRAY' || ref $attributes eq 'HASH')
192             || $self->throw_error("You must pass an ARRAY ref of attributes");
193     }
194     my $methods = delete $options{methods};
195     if(defined $methods){
196         (ref $methods eq 'HASH')
197             || $self->throw_error("You must pass a HASH ref of methods");
198     }
199     my $roles = delete $options{roles};
200     if(defined $roles){
201         (ref $roles eq 'ARRAY')
202             || $self->throw_error("You must pass an ARRAY ref of roles");
203     }
204     my $mortal;
205     my $cache_key;
206
207     if(!defined $package_name){ # anonymous
208         $mortal = !$options{cache};
209
210         # anonymous but immortal
211         if(!$mortal){
212                 # something like Super::Class|Super::Class::2=Role|Role::1
213                 $cache_key = join '=' => (
214                     join('|',      @{$superclasses || []}),
215                     join('|', sort @{$roles        || []}),
216                 );
217                 return $IMMORTALS{$cache_key} if exists $IMMORTALS{$cache_key};
218         }
219         $options{anon_serial_id} = ++$ANON_SERIAL;
220         $package_name = $class . '::__ANON__::' . $ANON_SERIAL;
221     }
222
223     # instantiate a module
224     {
225         no strict 'refs';
226         ${ $package_name . '::VERSION'   } = delete $options{version}   if exists $options{version};
227         ${ $package_name . '::AUTHORITY' } = delete $options{authority} if exists $options{authority};
228     }
229
230     my $meta = $self->initialize( $package_name, %options);
231
232     Scalar::Util::weaken $METAS{$package_name}
233         if $mortal;
234
235     $meta->add_method(meta => sub {
236         $self->initialize(ref($_[0]) || $_[0]);
237     });
238
239     $meta->superclasses(@{$superclasses})
240         if defined $superclasses;
241
242     # NOTE:
243     # process attributes first, so that they can
244     # install accessors, but locally defined methods
245     # can then overwrite them. It is maybe a little odd, but
246     # I think this should be the order of things.
247     if (defined $attributes) {
248         if(ref($attributes) eq 'ARRAY'){
249             # array of Mouse::Meta::Attribute
250             foreach my $attr (@{$attributes}) {
251                 $meta->add_attribute($attr);
252             }
253         }
254         else{
255             # hash map of name and attribute spec pairs
256             while(my($name, $attr) = each %{$attributes}){
257                 $meta->add_attribute($name => $attr);
258             }
259         }
260     }
261     if (defined $methods) {
262         while(my($method_name, $method_body) = each %{$methods}){
263             $meta->add_method($method_name, $method_body);
264         }
265     }
266     if (defined $roles){
267         Mouse::Util::apply_all_roles($package_name, @{$roles});
268     }
269
270     if($cache_key){
271         $IMMORTALS{$cache_key} = $meta;
272     }
273
274     return $meta;
275 }
276
277 sub DESTROY{
278     my($self) = @_;
279
280     return if $Mouse::Util::in_global_destruction;
281
282     my $serial_id = $self->{anon_serial_id};
283     return if !$serial_id;
284
285     # XXX: cleaning stash with threads causes panic/SEGV on legacy perls.
286     if(exists $INC{'threads.pm'}) {
287         # (caller)[2] indicates the caller's line number,
288         # which is zero when the current thread is joining (destroying).
289         return if( (caller)[2] == 0);
290     }
291
292     # clean up mortal anonymous class stuff
293
294     # @ISA is a magical variable, so we must clear it manually.
295     @{$self->{superclasses}} = () if exists $self->{superclasses};
296
297     # Then, clear the symbol table hash
298     %{$self->namespace} = ();
299
300     my $name = $self->name;
301     delete $METAS{$name};
302
303     $name =~ s/ $serial_id \z//xms;
304     no strict 'refs';
305     delete ${$name}{ $serial_id . '::' };
306     return;
307 }
308
309 sub throw_error{
310     my($self, $message, %args) = @_;
311
312     local $Carp::CarpLevel  = $Carp::CarpLevel + 1 + ($args{depth} || 0);
313     local $Carp::MaxArgNums = 20; # default is 8, usually we use named args which gets messier though
314
315     if(exists $args{longmess} && !$args{longmess}){ # intentionaly longmess => 0
316         Carp::croak($message);
317     }
318     else{
319         Carp::confess($message);
320     }
321 }
322
323 1;
324 __END__
325
326 =head1 NAME
327
328 Mouse::Meta::Module - The common base class of Mouse::Meta::Class and Mouse::Meta::Role
329
330 =head1 VERSION
331
332 This document describes Mouse version 0.70
333
334 =head1 DESCRIPTION
335
336 This class is an abstract base class of meta classes and meta roles.
337
338 =head1 SEE ALSO
339
340 L<Class::MOP::Class>
341
342 L<Class::MOP::Module>
343
344 L<Class::MOP::Package>
345
346 =cut
347