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