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