96ad798f2b5f0d2ea97c4af4d767c5728cccd473
[gitmo/Class-MOP.git] / lib / Class / MOP / Package.pm
1
2 package Class::MOP::Package;
3
4 use strict;
5 use warnings;
6
7 use B;
8 use Scalar::Util 'blessed';
9 use Carp         'confess';
10
11 our $VERSION   = '0.71_01';
12 $VERSION = eval $VERSION;
13 our $AUTHORITY = 'cpan:STEVAN';
14
15 use base 'Class::MOP::Object';
16
17 # creation ...
18
19 sub initialize {
20     my ( $class, @args ) = @_;
21
22     unshift @args, "package" if @args % 2;
23
24     my %options = @args;
25     my $package_name = $options{package};
26
27
28     # we hand-construct the class 
29     # until we can bootstrap it
30     if ( my $meta = Class::MOP::get_metaclass_by_name($package_name) ) {
31         return $meta;
32     } else {
33         my $meta = ( ref $class || $class )->_new({
34             'package'   => $package_name,
35         });
36
37         Class::MOP::store_metaclass_by_name($package_name, $meta);
38
39         return $meta;
40     }
41 }
42
43 sub reinitialize {
44     my ( $class, @args ) = @_;
45
46     unshift @args, "package" if @args % 2;
47
48     my %options = @args;
49     my $package_name = delete $options{package};
50
51     (defined $package_name && $package_name && !blessed($package_name))
52         || confess "You must pass a package name and it cannot be blessed";
53
54     Class::MOP::remove_metaclass_by_name($package_name);
55
56     $class->initialize($package_name, %options); # call with first arg form for compat
57 }
58
59 sub _new {
60     my $class = shift;
61     my $options = @_ == 1 ? $_[0] : {@_};
62
63     # NOTE:
64     # because of issues with the Perl API 
65     # to the typeglob in some versions, we 
66     # need to just always grab a new 
67     # reference to the hash in the accessor. 
68     # Ideally we could just store a ref and 
69     # it would Just Work, but oh well :\
70     $options->{namespace} ||= \undef;
71
72     bless $options, $class;
73 }
74
75 # Attributes
76
77 # NOTE:
78 # all these attribute readers will be bootstrapped 
79 # away in the Class::MOP bootstrap section
80
81 sub name      { $_[0]->{'package'} }
82 sub namespace { 
83     # NOTE:
84     # because of issues with the Perl API 
85     # to the typeglob in some versions, we 
86     # need to just always grab a new 
87     # reference to the hash here. Ideally 
88     # we could just store a ref and it would
89     # Just Work, but oh well :\    
90     no strict 'refs';    
91     \%{$_[0]->{'package'} . '::'} 
92 }
93
94 # utility methods
95
96 {
97     my %SIGIL_MAP = (
98         '$' => 'SCALAR',
99         '@' => 'ARRAY',
100         '%' => 'HASH',
101         '&' => 'CODE',
102     );
103     
104     sub _deconstruct_variable_name {
105         my ($self, $variable) = @_;
106
107         (defined $variable)
108             || confess "You must pass a variable name";    
109
110         my $sigil = substr($variable, 0, 1, '');
111
112         (defined $sigil)
113             || confess "The variable name must include a sigil";    
114
115         (exists $SIGIL_MAP{$sigil})
116             || confess "I do not recognize that sigil '$sigil'";    
117         
118         return ($variable, $sigil, $SIGIL_MAP{$sigil});
119     }
120 }
121
122 # Class attributes
123
124 # ... these functions have to touch the symbol table itself,.. yuk
125
126 sub add_package_symbol {
127     my ($self, $variable, $initial_value) = @_;
128
129     my ($name, $sigil, $type) = ref $variable eq 'HASH'
130         ? @{$variable}{qw[name sigil type]}
131         : $self->_deconstruct_variable_name($variable); 
132
133     my $pkg = $self->{'package'};
134
135     no strict 'refs';
136     no warnings 'redefine', 'misc';    
137     *{$pkg . '::' . $name} = ref $initial_value ? $initial_value : \$initial_value;      
138 }
139
140 sub remove_package_glob {
141     my ($self, $name) = @_;
142     no strict 'refs';        
143     delete ${$self->name . '::'}{$name};     
144 }
145
146 # ... these functions deal with stuff on the namespace level
147
148 sub has_package_symbol {
149     my ($self, $variable) = @_;
150
151     my ($name, $sigil, $type) = ref $variable eq 'HASH'
152         ? @{$variable}{qw[name sigil type]}
153         : $self->_deconstruct_variable_name($variable);
154     
155     my $namespace = $self->namespace;
156     
157     return 0 unless exists $namespace->{$name};   
158     
159     # FIXME:
160     # For some really stupid reason 
161     # a typeglob will have a default
162     # value of \undef in the SCALAR 
163     # slot, so we need to work around
164     # this. Which of course means that 
165     # if you put \undef in your scalar
166     # then this is broken.
167
168     if (ref($namespace->{$name}) eq 'SCALAR') {
169         return ($type eq 'CODE');
170     }
171     elsif ($type eq 'SCALAR') {    
172         my $val = *{$namespace->{$name}}{$type};
173         return defined(${$val});
174     }
175     else {
176         defined(*{$namespace->{$name}}{$type});
177     }
178 }
179
180 sub get_package_symbol {
181     my ($self, $variable) = @_;    
182
183     my ($name, $sigil, $type) = ref $variable eq 'HASH'
184         ? @{$variable}{qw[name sigil type]}
185         : $self->_deconstruct_variable_name($variable);
186
187     my $namespace = $self->namespace;
188
189     $self->add_package_symbol($variable)
190         unless exists $namespace->{$name};
191
192     if (ref($namespace->{$name}) eq 'SCALAR') {
193         if ($type eq 'CODE') {
194             no strict 'refs';
195             return \&{$self->name.'::'.$name};
196         }
197         else {
198             return undef;
199         }
200     }
201     else {
202         return *{$namespace->{$name}}{$type};
203     }
204 }
205
206 sub remove_package_symbol {
207     my ($self, $variable) = @_;
208
209     my ($name, $sigil, $type) = ref $variable eq 'HASH'
210         ? @{$variable}{qw[name sigil type]}
211         : $self->_deconstruct_variable_name($variable);
212
213     # FIXME:
214     # no doubt this is grossly inefficient and 
215     # could be done much easier and faster in XS
216
217     my ($scalar_desc, $array_desc, $hash_desc, $code_desc) = (
218         { sigil => '$', type => 'SCALAR', name => $name },
219         { sigil => '@', type => 'ARRAY',  name => $name },
220         { sigil => '%', type => 'HASH',   name => $name },
221         { sigil => '&', type => 'CODE',   name => $name },
222     );
223
224     my ($scalar, $array, $hash, $code);
225     if ($type eq 'SCALAR') {
226         $array  = $self->get_package_symbol($array_desc)  if $self->has_package_symbol($array_desc);
227         $hash   = $self->get_package_symbol($hash_desc)   if $self->has_package_symbol($hash_desc);     
228         $code   = $self->get_package_symbol($code_desc)   if $self->has_package_symbol($code_desc);     
229     }
230     elsif ($type eq 'ARRAY') {
231         $scalar = $self->get_package_symbol($scalar_desc) if $self->has_package_symbol($scalar_desc);
232         $hash   = $self->get_package_symbol($hash_desc)   if $self->has_package_symbol($hash_desc);     
233         $code   = $self->get_package_symbol($code_desc)   if $self->has_package_symbol($code_desc);
234     }
235     elsif ($type eq 'HASH') {
236         $scalar = $self->get_package_symbol($scalar_desc) if $self->has_package_symbol($scalar_desc);
237         $array  = $self->get_package_symbol($array_desc)  if $self->has_package_symbol($array_desc);        
238         $code   = $self->get_package_symbol($code_desc)   if $self->has_package_symbol($code_desc);      
239     }
240     elsif ($type eq 'CODE') {
241         $scalar = $self->get_package_symbol($scalar_desc) if $self->has_package_symbol($scalar_desc);
242         $array  = $self->get_package_symbol($array_desc)  if $self->has_package_symbol($array_desc);        
243         $hash   = $self->get_package_symbol($hash_desc)   if $self->has_package_symbol($hash_desc);        
244     }    
245     else {
246         confess "This should never ever ever happen";
247     }
248         
249     $self->remove_package_glob($name);
250     
251     $self->add_package_symbol($scalar_desc => $scalar) if defined $scalar;      
252     $self->add_package_symbol($array_desc  => $array)  if defined $array;    
253     $self->add_package_symbol($hash_desc   => $hash)   if defined $hash;
254     $self->add_package_symbol($code_desc   => $code)   if defined $code;            
255 }
256
257 sub list_all_package_symbols {
258     my ($self, $type_filter) = @_;
259
260     my $namespace = $self->namespace;
261     return keys %{$namespace} unless defined $type_filter;
262     
263     # NOTE:
264     # or we can filter based on 
265     # type (SCALAR|ARRAY|HASH|CODE)
266     if ( $type_filter eq 'CODE' ) {
267         return grep { 
268         (ref($namespace->{$_})
269                 ? (ref($namespace->{$_}) eq 'SCALAR')
270                 : (ref(\$namespace->{$_}) eq 'GLOB'
271                    && defined(*{$namespace->{$_}}{CODE})));
272         } keys %{$namespace};
273     } else {
274         return grep { *{$namespace->{$_}}{$type_filter} } keys %{$namespace};
275     }
276 }
277
278 sub get_all_package_symbols {
279     my ($self, $type_filter) = @_;
280
281     die "Cannot call get_all_package_symbols as a class method"
282         unless ref $self;
283
284     my $namespace = $self->namespace;
285
286     return %$namespace unless defined $type_filter;
287
288     # for some reason this nasty impl is orders of magnitude faster than a clean version
289     if ( $type_filter eq 'CODE' ) {
290         my $pkg;
291         no strict 'refs';
292         return map {
293             (ref($namespace->{$_})
294                 ? ( $_ => \&{$pkg ||= $self->name . "::$_"} )
295                 : ( ref \$namespace->{$_} eq 'GLOB' # don't use {CODE} unless it's really a glob to prevent stringification of stubs
296                     && (*{$namespace->{$_}}{CODE})  # the extra parents prevent breakage on 5.8.2
297                     ? ( $_ => *{$namespace->{$_}}{CODE} )
298                     : (do {
299                         my $sym = B::svref_2object(\$namespace->{$_});
300                         my $svt = ref $sym if $sym;
301                         ($sym && ($svt eq 'B::PV' || $svt eq 'B::IV'))
302                             ? ($_ => ($pkg ||= $self->name)->can($_))
303                             : () }) ) )
304         } keys %$namespace;
305     } else {
306         return map {
307             $_ => *{$namespace->{$_}}{$type_filter}
308         } grep {
309             !ref($namespace->{$_}) && *{$namespace->{$_}}{$type_filter}
310         } keys %$namespace;
311     }
312 }
313
314 1;
315
316 __END__
317
318 =pod
319
320 =head1 NAME 
321
322 Class::MOP::Package - Package Meta Object
323
324 =head1 DESCRIPTION
325
326 This is an abstraction of a Perl 5 package, it is a superclass of
327 L<Class::MOP::Class> and provides all of the symbol table 
328 introspection methods.
329
330 =head1 METHODS
331
332 =over 4
333
334 =item B<meta>
335
336 Returns a metaclass for this package.
337
338 =item B<initialize ($package_name)>
339
340 This will initialize a Class::MOP::Package instance which represents 
341 the package of C<$package_name>.
342
343 =item B<reinitialize ($package_name, %options)>
344
345 This removes the old metaclass, and creates a new one in it's place.
346 Do B<not> use this unless you really know what you are doing, it could
347 very easily make a very large mess of your program.
348
349 =item B<name>
350
351 This is a read-only attribute which returns the package name for the 
352 given instance.
353
354 =item B<namespace>
355
356 This returns a HASH reference to the symbol table. The keys of the 
357 HASH are the symbol names, and the values are typeglob references.
358
359 =item B<add_package_symbol ($variable_name, ?$initial_value)>
360
361 Given a C<$variable_name>, which must contain a leading sigil, this 
362 method will create that variable within the package which houses the 
363 class. It also takes an optional C<$initial_value>, which must be a 
364 reference of the same type as the sigil of the C<$variable_name> 
365 implies.
366
367 =item B<get_package_symbol ($variable_name)>
368
369 This will return a reference to the package variable in 
370 C<$variable_name>. 
371
372 =item B<has_package_symbol ($variable_name)>
373
374 Returns true (C<1>) if there is a package variable defined for 
375 C<$variable_name>, and false (C<0>) otherwise.
376
377 =item B<remove_package_symbol ($variable_name)>
378
379 This will attempt to remove the package variable at C<$variable_name>.
380
381 =item B<remove_package_glob ($glob_name)>
382
383 This will attempt to remove the entire typeglob associated with 
384 C<$glob_name> from the package. 
385
386 =item B<list_all_package_symbols (?$type_filter)>
387
388 This will list all the glob names associated with the current package. 
389 By inspecting the globs returned you can discern all the variables in 
390 the package.
391
392 By passing a C<$type_filter>, you can limit the list to only those 
393 which match the filter (either SCALAR, ARRAY, HASH or CODE).
394
395 =item B<get_all_package_symbols (?$type_filter)>
396
397 Works exactly like C<list_all_package_symbols> but returns a HASH of 
398 name => thing mapping instead of just an ARRAY of names.
399
400 =back
401
402 =head1 AUTHORS
403
404 Stevan Little E<lt>stevan@iinteractive.comE<gt>
405
406 =head1 COPYRIGHT AND LICENSE
407
408 Copyright 2006-2008 by Infinity Interactive, Inc.
409
410 L<http://www.iinteractive.com>
411
412 This library is free software; you can redistribute it and/or modify
413 it under the same terms as Perl itself.
414
415 =cut