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