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