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