refactoring no-get_method_map with package symmbol APIs
[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
074ec38f 10our $VERSION = '0.89';
d519662a 11$VERSION = eval $VERSION;
f0480c45 12our $AUTHORITY = 'cpan:STEVAN';
2243a22b 13
6e57504d 14use base 'Class::MOP::Object';
15
6d5355c3 16# creation ...
17
18sub initialize {
3be6bc1c 19 my ( $class, @args ) = @_;
20
21 unshift @args, "package" if @args % 2;
22
23 my %options = @args;
24 my $package_name = $options{package};
25
26
9d6dce77 27 # we hand-construct the class
28 # until we can bootstrap it
a19fcb5b 29 if ( my $meta = Class::MOP::get_metaclass_by_name($package_name) ) {
973de492 30 return $meta;
a19fcb5b 31 } else {
973de492 32 my $meta = ( ref $class || $class )->_new({
33 'package' => $package_name,
11ac821d 34 %options,
973de492 35 });
a19fcb5b 36
973de492 37 Class::MOP::store_metaclass_by_name($package_name, $meta);
a19fcb5b 38
973de492 39 return $meta;
a19fcb5b 40 }
41}
42
43sub reinitialize {
3be6bc1c 44 my ( $class, @args ) = @_;
45
46 unshift @args, "package" if @args % 2;
47
48 my %options = @args;
3eda22f8 49 my $package_name = delete $options{package};
3be6bc1c 50
a19fcb5b 51 (defined $package_name && $package_name && !blessed($package_name))
52 || confess "You must pass a package name and it cannot be blessed";
3be6bc1c 53
a19fcb5b 54 Class::MOP::remove_metaclass_by_name($package_name);
3be6bc1c 55
3eda22f8 56 $class->initialize($package_name, %options); # call with first arg form for compat
682655a3 57}
58
59sub _new {
0bfc85b8 60 my $class = shift;
61 my $options = @_ == 1 ? $_[0] : {@_};
682655a3 62
0bfc85b8 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;
6d5355c3 73}
74
75# Attributes
76
77# NOTE:
78# all these attribute readers will be bootstrapped
79# away in the Class::MOP bootstrap section
80
56dcfc1a 81sub namespace {
82 # NOTE:
83 # because of issues with the Perl API
84 # to the typeglob in some versions, we
85 # need to just always grab a new
86 # reference to the hash here. Ideally
87 # we could just store a ref and it would
88 # Just Work, but oh well :\
89 no strict 'refs';
8683db0e 90 \%{$_[0]->{'package'} . '::'}
56dcfc1a 91}
6d5355c3 92
a5e51f0b 93# utility methods
6d5355c3 94
c0cbf4d9 95{
96 my %SIGIL_MAP = (
97 '$' => 'SCALAR',
98 '@' => 'ARRAY',
99 '%' => 'HASH',
100 '&' => 'CODE',
101 );
6d5355c3 102
a5e51f0b 103 sub _deconstruct_variable_name {
104 my ($self, $variable) = @_;
105
c0cbf4d9 106 (defined $variable)
107 || confess "You must pass a variable name";
a5e51f0b 108
f430cfa4 109 my $sigil = substr($variable, 0, 1, '');
a5e51f0b 110
c0cbf4d9 111 (defined $sigil)
112 || confess "The variable name must include a sigil";
a5e51f0b 113
c0cbf4d9 114 (exists $SIGIL_MAP{$sigil})
a5e51f0b 115 || confess "I do not recognize that sigil '$sigil'";
116
f430cfa4 117 return ($variable, $sigil, $SIGIL_MAP{$sigil});
c0cbf4d9 118 }
a5e51f0b 119}
6d5355c3 120
a5e51f0b 121# Class attributes
6d5355c3 122
c46b802b 123# ... these functions have to touch the symbol table itself,.. yuk
124
a5e51f0b 125sub add_package_symbol {
126 my ($self, $variable, $initial_value) = @_;
6d5355c3 127
8b49a472 128 my ($name, $sigil, $type) = ref $variable eq 'HASH'
129 ? @{$variable}{qw[name sigil type]}
26159d55 130 : $self->_deconstruct_variable_name($variable);
6d5355c3 131
8683db0e 132 my $pkg = $self->{'package'};
9a8bbfc9 133
a5e51f0b 134 no strict 'refs';
26159d55 135 no warnings 'redefine', 'misc', 'prototype';
136 *{$pkg . '::' . $name} = ref $initial_value ? $initial_value : \$initial_value;
c46b802b 137}
138
139sub remove_package_glob {
140 my ($self, $name) = @_;
141 no strict 'refs';
142 delete ${$self->name . '::'}{$name};
a5e51f0b 143}
6d5355c3 144
c46b802b 145# ... these functions deal with stuff on the namespace level
146
a5e51f0b 147sub has_package_symbol {
148 my ($self, $variable) = @_;
149
8b49a472 150 my ($name, $sigil, $type) = ref $variable eq 'HASH'
151 ? @{$variable}{qw[name sigil type]}
152 : $self->_deconstruct_variable_name($variable);
56dcfc1a 153
ae234dc6 154 my $namespace = $self->namespace;
155
156 return 0 unless exists $namespace->{$name};
d852f4d2 157
55039f82 158 my $entry_ref = \$namespace->{$name};
159 if (ref($entry_ref) eq 'GLOB') {
160 if ($type eq 'SCALAR') {
161 return defined(${ *{$entry_ref}{SCALAR} });
162 }
163 else {
164 return defined(*{$entry_ref}{$type});
165 }
166 }
167 else {
168 # a symbol table entry can be -1 (stub), string (stub with prototype),
169 # or reference (constant)
170 return $type eq 'CODE';
d852f4d2 171 }
a5e51f0b 172}
173
174sub get_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
ae234dc6 181 my $namespace = $self->namespace;
182
55039f82 183 # FIXME
c20522bd 184 $self->add_package_symbol($variable)
ae234dc6 185 unless exists $namespace->{$name};
92af7fdf 186
55039f82 187 my $entry_ref = \$namespace->{$name};
188
189 if (ref($entry_ref) eq 'GLOB') {
190 return *{$entry_ref}{$type};
191 }
192 else{
193 if($type eq 'CODE'){
92af7fdf 194 no strict 'refs';
55039f82 195 return \&{$self->name . '::' . $name};
92af7fdf 196 }
55039f82 197 else{
92af7fdf 198 return undef;
199 }
200 }
a5e51f0b 201}
202
203sub remove_package_symbol {
204 my ($self, $variable) = @_;
205
8b49a472 206 my ($name, $sigil, $type) = ref $variable eq 'HASH'
207 ? @{$variable}{qw[name sigil type]}
208 : $self->_deconstruct_variable_name($variable);
a5e51f0b 209
c46b802b 210 # FIXME:
211 # no doubt this is grossly inefficient and
212 # could be done much easier and faster in XS
213
8b49a472 214 my ($scalar_desc, $array_desc, $hash_desc, $code_desc) = (
215 { sigil => '$', type => 'SCALAR', name => $name },
216 { sigil => '@', type => 'ARRAY', name => $name },
217 { sigil => '%', type => 'HASH', name => $name },
218 { sigil => '&', type => 'CODE', name => $name },
219 );
220
c46b802b 221 my ($scalar, $array, $hash, $code);
a5e51f0b 222 if ($type eq 'SCALAR') {
8b49a472 223 $array = $self->get_package_symbol($array_desc) if $self->has_package_symbol($array_desc);
224 $hash = $self->get_package_symbol($hash_desc) if $self->has_package_symbol($hash_desc);
225 $code = $self->get_package_symbol($code_desc) if $self->has_package_symbol($code_desc);
a5e51f0b 226 }
227 elsif ($type eq 'ARRAY') {
8b49a472 228 $scalar = $self->get_package_symbol($scalar_desc) if $self->has_package_symbol($scalar_desc);
229 $hash = $self->get_package_symbol($hash_desc) if $self->has_package_symbol($hash_desc);
230 $code = $self->get_package_symbol($code_desc) if $self->has_package_symbol($code_desc);
a5e51f0b 231 }
232 elsif ($type eq 'HASH') {
8b49a472 233 $scalar = $self->get_package_symbol($scalar_desc) if $self->has_package_symbol($scalar_desc);
234 $array = $self->get_package_symbol($array_desc) if $self->has_package_symbol($array_desc);
235 $code = $self->get_package_symbol($code_desc) if $self->has_package_symbol($code_desc);
a5e51f0b 236 }
237 elsif ($type eq 'CODE') {
8b49a472 238 $scalar = $self->get_package_symbol($scalar_desc) if $self->has_package_symbol($scalar_desc);
239 $array = $self->get_package_symbol($array_desc) if $self->has_package_symbol($array_desc);
240 $hash = $self->get_package_symbol($hash_desc) if $self->has_package_symbol($hash_desc);
a5e51f0b 241 }
242 else {
243 confess "This should never ever ever happen";
7f436b8c 244 }
c46b802b 245
246 $self->remove_package_glob($name);
247
8b49a472 248 $self->add_package_symbol($scalar_desc => $scalar) if defined $scalar;
249 $self->add_package_symbol($array_desc => $array) if defined $array;
250 $self->add_package_symbol($hash_desc => $hash) if defined $hash;
251 $self->add_package_symbol($code_desc => $code) if defined $code;
9d6dce77 252}
c0cbf4d9 253
9d6dce77 254sub list_all_package_symbols {
92330ee2 255 my ($self, $type_filter) = @_;
a38e4d1a 256
257 my $namespace = $self->namespace;
258 return keys %{$namespace} unless defined $type_filter;
259
91e0eb4a 260 # NOTE:
92330ee2 261 # or we can filter based on
262 # type (SCALAR|ARRAY|HASH|CODE)
3609af79 263 if ( $type_filter eq 'CODE' ) {
264 return grep {
92af7fdf 265 (ref($namespace->{$_})
3609af79 266 ? (ref($namespace->{$_}) eq 'SCALAR')
267 : (ref(\$namespace->{$_}) eq 'GLOB'
268 && defined(*{$namespace->{$_}}{CODE})));
269 } keys %{$namespace};
270 } else {
271 return grep { *{$namespace->{$_}}{$type_filter} } keys %{$namespace};
272 }
6d5355c3 273}
274
2243a22b 2751;
276
277__END__
278
279=pod
280
281=head1 NAME
282
283Class::MOP::Package - Package Meta Object
284
2243a22b 285=head1 DESCRIPTION
286
116a9f45 287The Package Protocol provides an abstraction of a Perl 5 package. A
288package is basically namespace, and this module provides methods for
289looking at and changing that namespace's symbol table.
121991f6 290
2243a22b 291=head1 METHODS
292
293=over 4
294
116a9f45 295=item B<< Class::MOP::Package->initialize($package_name) >>
296
297This method creates a new C<Class::MOP::Package> instance which
298represents specified package. If an existing metaclass object exists
299for the package, that will be returned instead.
300
301=item B<< Class::MOP::Package->reinitialize($package_name) >>
2243a22b 302
116a9f45 303This method forcibly removes any existing metaclass for the package
304before calling C<initialize>
127d39a7 305
116a9f45 306Do not call this unless you know what you are doing.
6d5355c3 307
116a9f45 308=item B<< $metapackage->name >>
127d39a7 309
116a9f45 310This is returns the package's name, as passed to the constructor.
a19fcb5b 311
116a9f45 312=item B<< $metapackage->namespace >>
a19fcb5b 313
116a9f45 314This returns a hash reference to the package's symbol table. The keys
315are symbol names and the values are typeglob references.
6d5355c3 316
116a9f45 317=item B<< $metapackage->add_package_symbol($variable_name, $initial_value) >>
b9d9fc0b 318
116a9f45 319This method accepts a variable name and an optional initial value. The
320C<$variable_name> must contain a leading sigil.
a5e51f0b 321
116a9f45 322This method creates the variable in the package's symbol table, and
323sets it to the initial value if one was provided.
b9d9fc0b 324
116a9f45 325=item B<< $metapackage->get_package_symbol($variable_name) >>
b9d9fc0b 326
116a9f45 327Given a variable name, this method returns the variable as a reference
328or undef if it does not exist. The C<$variable_name> must contain a
329leading sigil.
b9d9fc0b 330
116a9f45 331=item B<< $metapackage->has_package_symbol($variable_name) >>
6d5355c3 332
116a9f45 333Returns true if there is a package variable defined for
334C<$variable_name>. The C<$variable_name> must contain a leading sigil.
6d5355c3 335
116a9f45 336=item B<< $metapackage->remove_package_symbol($variable_name) >>
6d5355c3 337
116a9f45 338This will remove the package variable specified C<$variable_name>. The
339C<$variable_name> must contain a leading sigil.
6d5355c3 340
116a9f45 341=item B<< $metapackage->remove_package_glob($glob_name) >>
b9d9fc0b 342
116a9f45 343Given the name of a glob, this will remove that glob from the
344package's symbol table. Glob names do not include a sigil. Removing
345the glob removes all variables and subroutines with the specified
346name.
b9d9fc0b 347
116a9f45 348=item B<< $metapackage->list_all_package_symbols($type_filter) >>
b9d9fc0b 349
116a9f45 350This will list all the glob names associated with the current
351package. These names do not have leading sigils.
c46b802b 352
116a9f45 353You can provide an optional type filter, which should be one of
354'SCALAR', 'ARRAY', 'HASH', or 'CODE'.
9d6dce77 355
116a9f45 356=item B<< $metapackage->get_all_package_symbols($type_filter) >>
b9d9fc0b 357
116a9f45 358This works much like C<list_all_package_symbols>, but it returns a
359hash reference. The keys are glob names and the values are references
360to the value for that name.
92330ee2 361
116a9f45 362=item B<< Class::MOP::Package->meta >>
ae234dc6 363
116a9f45 364This will return a L<Class::MOP::Class> instance for this class.
ae234dc6 365
2243a22b 366=back
367
1a09d9cc 368=head1 AUTHORS
2243a22b 369
370Stevan Little E<lt>stevan@iinteractive.comE<gt>
371
372=head1 COPYRIGHT AND LICENSE
373
070bb6c9 374Copyright 2006-2009 by Infinity Interactive, Inc.
2243a22b 375
376L<http://www.iinteractive.com>
377
378This library is free software; you can redistribute it and/or modify
379it under the same terms as Perl itself.
380
92af7fdf 381=cut