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