Make the perl version of get_all_package_symbols handle stub functions.
[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';
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                 : ( (*{$namespace->{$_}}{CODE}) # the extra parents prevent breakage on 5.8.2
296                     ? ( $_ => *{$namespace->{$_}}{CODE} )
297                     : (do {
298                         my $sym = B::svref_2object(\$namespace->{$_});
299                         my $svt = ref $sym if $sym;
300                         ($sym && ($svt eq 'B::PV' || $svt eq 'B::PVIV'))
301                             ? ($_ => ($pkg ||= $self->name)->can($_))
302                             : () }) ) )
303         } keys %$namespace;
304     } else {
305         return map {
306             $_ => *{$namespace->{$_}}{$type_filter}
307         } grep {
308             !ref($namespace->{$_}) && *{$namespace->{$_}}{$type_filter}
309         } keys %$namespace;
310     }
311 }
312
313 1;
314
315 __END__
316
317 =pod
318
319 =head1 NAME 
320
321 Class::MOP::Package - Package Meta Object
322
323 =head1 DESCRIPTION
324
325 This is an abstraction of a Perl 5 package, it is a superclass of
326 L<Class::MOP::Class> and provides all of the symbol table 
327 introspection methods.
328
329 =head1 METHODS
330
331 =over 4
332
333 =item B<meta>
334
335 Returns a metaclass for this package.
336
337 =item B<initialize ($package_name)>
338
339 This will initialize a Class::MOP::Package instance which represents 
340 the package of C<$package_name>.
341
342 =item B<reinitialize ($package_name, %options)>
343
344 This removes the old metaclass, and creates a new one in it's place.
345 Do B<not> use this unless you really know what you are doing, it could
346 very easily make a very large mess of your program.
347
348 =item B<name>
349
350 This is a read-only attribute which returns the package name for the 
351 given instance.
352
353 =item B<namespace>
354
355 This returns a HASH reference to the symbol table. The keys of the 
356 HASH are the symbol names, and the values are typeglob references.
357
358 =item B<add_package_symbol ($variable_name, ?$initial_value)>
359
360 Given a C<$variable_name>, which must contain a leading sigil, this 
361 method will create that variable within the package which houses the 
362 class. It also takes an optional C<$initial_value>, which must be a 
363 reference of the same type as the sigil of the C<$variable_name> 
364 implies.
365
366 =item B<get_package_symbol ($variable_name)>
367
368 This will return a reference to the package variable in 
369 C<$variable_name>. 
370
371 =item B<has_package_symbol ($variable_name)>
372
373 Returns true (C<1>) if there is a package variable defined for 
374 C<$variable_name>, and false (C<0>) otherwise.
375
376 =item B<remove_package_symbol ($variable_name)>
377
378 This will attempt to remove the package variable at C<$variable_name>.
379
380 =item B<remove_package_glob ($glob_name)>
381
382 This will attempt to remove the entire typeglob associated with 
383 C<$glob_name> from the package. 
384
385 =item B<list_all_package_symbols (?$type_filter)>
386
387 This will list all the glob names associated with the current package. 
388 By inspecting the globs returned you can discern all the variables in 
389 the package.
390
391 By passing a C<$type_filter>, you can limit the list to only those 
392 which match the filter (either SCALAR, ARRAY, HASH or CODE).
393
394 =item B<get_all_package_symbols (?$type_filter)>
395
396 Works exactly like C<list_all_package_symbols> but returns a HASH of 
397 name => thing mapping instead of just an ARRAY of names.
398
399 =back
400
401 =head1 AUTHORS
402
403 Stevan Little E<lt>stevan@iinteractive.comE<gt>
404
405 =head1 COPYRIGHT AND LICENSE
406
407 Copyright 2006-2008 by Infinity Interactive, Inc.
408
409 L<http://www.iinteractive.com>
410
411 This library is free software; you can redistribute it and/or modify
412 it under the same terms as Perl itself.
413
414 =cut