14990eef7b39f4f87962dd588d8b598051469f36
[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($class, $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, ArrayRef or regular expression, not %s',
154                         $subname,
155                         $type,
156                     )
157                 );
158             }
159          }
160          else{
161             push @methods, $arg;
162          }
163      }
164      return @methods;
165 }
166
167 my $ANON_SERIAL = 0;  # anonymous class/role id
168 my %IMMORTALS;        # immortal anonymous classes
169
170 sub create {
171     my($self, $package_name, %options) = @_;
172
173     my $class = ref($self) || $self;
174     $self->throw_error('You must pass a package name') if @_ < 2;
175
176     my $superclasses;
177     if(exists $options{superclasses}){
178         if(Mouse::Util::is_a_metarole($self)){
179             delete $options{superclasses};
180         }
181         else{
182             $superclasses = delete $options{superclasses};
183             (ref $superclasses eq 'ARRAY')
184                 || $self->throw_error("You must pass an ARRAY ref of superclasses");
185         }
186     }
187
188     my $attributes = delete $options{attributes};
189     if(defined $attributes){
190         (ref $attributes eq 'ARRAY' || ref $attributes eq 'HASH')
191             || $self->throw_error("You must pass an ARRAY ref of attributes");
192     }
193     my $methods = delete $options{methods};
194     if(defined $methods){
195         (ref $methods eq 'HASH')
196             || $self->throw_error("You must pass a HASH ref of methods");
197     }
198     my $roles = delete $options{roles};
199     if(defined $roles){
200         (ref $roles eq 'ARRAY')
201             || $self->throw_error("You must pass an ARRAY ref of roles");
202     }
203     my $mortal;
204     my $cache_key;
205
206     if(!defined $package_name){ # anonymous
207         $mortal = !$options{cache};
208
209         # anonymous but immortal
210         if(!$mortal){
211                 # something like Super::Class|Super::Class::2=Role|Role::1
212                 $cache_key = join '=' => (
213                     join('|',      @{$superclasses || []}),
214                     join('|', sort @{$roles        || []}),
215                 );
216                 return $IMMORTALS{$cache_key} if exists $IMMORTALS{$cache_key};
217         }
218         $options{anon_serial_id} = ++$ANON_SERIAL;
219         $package_name = $class . '::__ANON__::' . $ANON_SERIAL;
220     }
221
222     # instantiate a module
223     {
224         no strict 'refs';
225         ${ $package_name . '::VERSION'   } = delete $options{version}   if exists $options{version};
226         ${ $package_name . '::AUTHORITY' } = delete $options{authority} if exists $options{authority};
227     }
228
229     my $meta = $self->initialize( $package_name, %options);
230
231     Scalar::Util::weaken $METAS{$package_name}
232         if $mortal;
233
234     $meta->add_method(meta => sub{
235         $self->initialize(ref($_[0]) || $_[0]);
236     });
237
238     $meta->superclasses(@{$superclasses})
239         if defined $superclasses;
240
241     # NOTE:
242     # process attributes first, so that they can
243     # install accessors, but locally defined methods
244     # can then overwrite them. It is maybe a little odd, but
245     # I think this should be the order of things.
246     if (defined $attributes) {
247         if(ref($attributes) eq 'ARRAY'){
248             # array of Mouse::Meta::Attribute
249             foreach my $attr (@{$attributes}) {
250                 $meta->add_attribute($attr);
251             }
252         }
253         else{
254             # hash map of name and attribute spec pairs
255             while(my($name, $attr) = each %{$attributes}){
256                 $meta->add_attribute($name => $attr);
257             }
258         }
259     }
260     if (defined $methods) {
261         while(my($method_name, $method_body) = each %{$methods}){
262             $meta->add_method($method_name, $method_body);
263         }
264     }
265     if (defined $roles){
266         Mouse::Util::apply_all_roles($package_name, @{$roles});
267     }
268
269     if($cache_key){
270         $IMMORTALS{$cache_key} = $meta;
271     }
272
273     return $meta;
274 }
275
276 sub DESTROY{
277     my($self) = @_;
278
279     return if $Mouse::Util::in_global_destruction;
280
281     my $serial_id = $self->{anon_serial_id};
282
283     return if !$serial_id;
284     # mortal anonymous class
285
286     # @ISA is a magical variable, so we clear it manually.
287     @{$self->{superclasses}} = () if exists $self->{superclasses};
288
289     # Then, clear the symbol table hash
290     %{$self->namespace} = ();
291
292     my $name = $self->name;
293     delete $METAS{$name};
294
295     $name =~ s/ $serial_id \z//xms;
296
297     no strict 'refs';
298     delete ${$name}{ $serial_id . '::' };
299
300     return;
301 }
302
303 sub throw_error{
304     my($class, $message, %args) = @_;
305
306     local $Carp::CarpLevel  = $Carp::CarpLevel + 1 + ($args{depth} || 0);
307     local $Carp::MaxArgNums = 20; # default is 8, usually we use named args which gets messier though
308
309     if(exists $args{longmess} && !$args{longmess}){ # intentionaly longmess => 0
310         Carp::croak($message);
311     }
312     else{
313         Carp::confess($message);
314     }
315 }
316
317 1;
318 __END__
319
320 =head1 NAME
321
322 Mouse::Meta::Module - The base class for Mouse::Meta::Class and Mouse::Meta::Role
323
324 =head1 VERSION
325
326 This document describes Mouse version 0.50_03
327
328 =head1 SEE ALSO
329
330 L<Class::MOP::Class>
331
332 L<Class::MOP::Module>
333
334 L<Class::MOP::Package>
335
336 =cut
337