0.42
[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
9195ddff 10our $VERSION = '0.07';
f0480c45 11our $AUTHORITY = 'cpan:STEVAN';
2243a22b 12
6e57504d 13use base 'Class::MOP::Object';
14
2243a22b 15# introspection
16
17sub meta {
18 require Class::MOP::Class;
19 Class::MOP::Class->initialize(blessed($_[0]) || $_[0]);
20}
21
6d5355c3 22# creation ...
23
24sub initialize {
9d6dce77 25 my $class = shift;
26 my $package_name = shift;
27 # we hand-construct the class
28 # until we can bootstrap it
a5e51f0b 29 no strict 'refs';
30 return bless {
c23184fc 31 '$!package' => $package_name,
56dcfc1a 32 # NOTE:
33 # because of issues with the Perl API
34 # to the typeglob in some versions, we
35 # need to just always grab a new
36 # reference to the hash in the accessor.
37 # Ideally we could just store a ref and
38 # it would Just Work, but oh well :\
c23184fc 39 '%!namespace' => \undef,
a5e51f0b 40 } => $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
c23184fc 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';
59 \%{$_[0]->name . '::'}
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
c0cbf4d9 78 my ($sigil, $name) = ($variable =~ /^(.)(.*)$/);
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
86 return ($name, $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
a5e51f0b 97 my ($name, $sigil, $type) = $self->_deconstruct_variable_name($variable);
6d5355c3 98
a5e51f0b 99 no strict 'refs';
56dcfc1a 100 no warnings 'redefine', 'misc';
101 *{$self->name . '::' . $name} = ref $initial_value ? $initial_value : \$initial_value;
c46b802b 102}
103
104sub remove_package_glob {
105 my ($self, $name) = @_;
106 no strict 'refs';
107 delete ${$self->name . '::'}{$name};
a5e51f0b 108}
6d5355c3 109
c46b802b 110# ... these functions deal with stuff on the namespace level
111
a5e51f0b 112sub has_package_symbol {
113 my ($self, $variable) = @_;
114
115 my ($name, $sigil, $type) = $self->_deconstruct_variable_name($variable);
56dcfc1a 116
c20522bd 117 return 0 unless exists $self->namespace->{$name};
d852f4d2 118
119 # FIXME:
120 # For some really stupid reason
121 # a typeglob will have a default
122 # value of \undef in the SCALAR
123 # slot, so we need to work around
124 # this. Which of course means that
125 # if you put \undef in your scalar
126 # then this is broken.
92af7fdf 127
128 if (ref($self->namespace->{$name}) eq 'SCALAR') {
129 return ($type eq 'CODE' ? 1 : 0);
130 }
131 elsif ($type eq 'SCALAR') {
d852f4d2 132 my $val = *{$self->namespace->{$name}}{$type};
92af7fdf 133 return defined(${$val}) ? 1 : 0;
d852f4d2 134 }
135 else {
56dcfc1a 136 defined(*{$self->namespace->{$name}}{$type}) ? 1 : 0;
d852f4d2 137 }
a5e51f0b 138}
139
140sub get_package_symbol {
141 my ($self, $variable) = @_;
142
143 my ($name, $sigil, $type) = $self->_deconstruct_variable_name($variable);
144
c20522bd 145 $self->add_package_symbol($variable)
146 unless exists $self->namespace->{$name};
92af7fdf 147
148 if (ref($self->namespace->{$name}) eq 'SCALAR') {
149 if ($type eq 'CODE') {
150 no strict 'refs';
151 return \&{$self->name.'::'.$name};
152 }
153 else {
154 return undef;
155 }
156 }
157 else {
158 return *{$self->namespace->{$name}}{$type};
159 }
a5e51f0b 160}
161
162sub remove_package_symbol {
163 my ($self, $variable) = @_;
164
165 my ($name, $sigil, $type) = $self->_deconstruct_variable_name($variable);
166
c46b802b 167 # FIXME:
168 # no doubt this is grossly inefficient and
169 # could be done much easier and faster in XS
170
171 my ($scalar, $array, $hash, $code);
a5e51f0b 172 if ($type eq 'SCALAR') {
c46b802b 173 $array = $self->get_package_symbol('@' . $name) if $self->has_package_symbol('@' . $name);
174 $hash = $self->get_package_symbol('%' . $name) if $self->has_package_symbol('%' . $name);
175 $code = $self->get_package_symbol('&' . $name) if $self->has_package_symbol('&' . $name);
a5e51f0b 176 }
177 elsif ($type eq 'ARRAY') {
c46b802b 178 $scalar = $self->get_package_symbol('$' . $name) if $self->has_package_symbol('$' . $name);
179 $hash = $self->get_package_symbol('%' . $name) if $self->has_package_symbol('%' . $name);
180 $code = $self->get_package_symbol('&' . $name) if $self->has_package_symbol('&' . $name);
a5e51f0b 181 }
182 elsif ($type eq 'HASH') {
c46b802b 183 $scalar = $self->get_package_symbol('$' . $name) if $self->has_package_symbol('$' . $name);
184 $array = $self->get_package_symbol('@' . $name) if $self->has_package_symbol('@' . $name);
185 $code = $self->get_package_symbol('&' . $name) if $self->has_package_symbol('&' . $name);
a5e51f0b 186 }
187 elsif ($type eq 'CODE') {
c46b802b 188 $scalar = $self->get_package_symbol('$' . $name) if $self->has_package_symbol('$' . $name);
189 $array = $self->get_package_symbol('@' . $name) if $self->has_package_symbol('@' . $name);
190 $hash = $self->get_package_symbol('%' . $name) if $self->has_package_symbol('%' . $name);
a5e51f0b 191 }
192 else {
193 confess "This should never ever ever happen";
7f436b8c 194 }
c46b802b 195
196 $self->remove_package_glob($name);
197
198 $self->add_package_symbol(('$' . $name) => $scalar) if defined $scalar;
199 $self->add_package_symbol(('@' . $name) => $array) if defined $array;
200 $self->add_package_symbol(('%' . $name) => $hash) if defined $hash;
201 $self->add_package_symbol(('&' . $name) => $code) if defined $code;
9d6dce77 202}
c0cbf4d9 203
9d6dce77 204sub list_all_package_symbols {
92330ee2 205 my ($self, $type_filter) = @_;
206 return keys %{$self->namespace} unless defined $type_filter;
91e0eb4a 207 # NOTE:
92330ee2 208 # or we can filter based on
209 # type (SCALAR|ARRAY|HASH|CODE)
210 my $namespace = $self->namespace;
91e0eb4a 211 return grep {
92af7fdf 212 (ref($namespace->{$_})
213 ? (ref($namespace->{$_}) eq 'SCALAR' && $type_filter eq 'CODE')
214 : (ref(\$namespace->{$_}) eq 'GLOB'
215 && defined(*{$namespace->{$_}}{$type_filter})));
91e0eb4a 216 } keys %{$namespace};
6d5355c3 217}
218
2243a22b 2191;
220
221__END__
222
223=pod
224
225=head1 NAME
226
227Class::MOP::Package - Package Meta Object
228
229=head1 SYNOPSIS
230
231=head1 DESCRIPTION
232
233=head1 METHODS
234
235=over 4
236
237=item B<meta>
238
b9d9fc0b 239=item B<initialize ($package_name)>
6d5355c3 240
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
2367814a 295Copyright 2006, 2007 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