initial import of code from Class::MOP::Package
[gitmo/Package-Stash-PP.git] / lib / Stash / Manip.pm
CommitLineData
f4979588 1package Stash::Manip;
f10f6217 2use strict;
3use warnings;
4
5use Carp qw(confess);
6use Scalar::Util qw(reftype);
f4979588 7
8=head1 NAME
9
10Stash::Manip -
11
12=head1 SYNOPSIS
13
14
15=head1 DESCRIPTION
16
17
18=cut
19
f10f6217 20sub new {
21 my $class = shift;
22 my ($namespace) = @_;
23 return bless { package => $namespace }, $class;
24}
25
26sub name {
27 return $_[0]->{package};
28}
29
30sub namespace {
31 # NOTE:
32 # because of issues with the Perl API
33 # to the typeglob in some versions, we
34 # need to just always grab a new
35 # reference to the hash here. Ideally
36 # we could just store a ref and it would
37 # Just Work, but oh well :\
38 no strict 'refs';
39 return \%{$_[0]->name . '::'};
40}
41
42{
43 my %SIGIL_MAP = (
44 '$' => 'SCALAR',
45 '@' => 'ARRAY',
46 '%' => 'HASH',
47 '&' => 'CODE',
48 );
49
50 sub _deconstruct_variable_name {
51 my ($self, $variable) = @_;
52
53 (defined $variable)
54 || confess "You must pass a variable name";
55
56 my $sigil = substr($variable, 0, 1, '');
57
58 (defined $sigil)
59 || confess "The variable name must include a sigil";
60
61 (exists $SIGIL_MAP{$sigil})
62 || confess "I do not recognize that sigil '$sigil'";
63
64 return ($variable, $sigil, $SIGIL_MAP{$sigil});
65 }
66}
67
68sub add_package_symbol {
69 my ($self, $variable, $initial_value) = @_;
70
71 my ($name, $sigil, $type) = ref $variable eq 'HASH'
72 ? @{$variable}{qw[name sigil type]}
73 : $self->_deconstruct_variable_name($variable);
74
75 my $pkg = $self->name;
76
77 no strict 'refs';
78 no warnings 'redefine', 'misc', 'prototype';
79 *{$pkg . '::' . $name} = ref $initial_value ? $initial_value : \$initial_value;
80}
81
82sub remove_package_glob {
83 my ($self, $name) = @_;
84 no strict 'refs';
85 delete ${$self->name . '::'}{$name};
86}
87
88# ... these functions deal with stuff on the namespace level
89
90sub has_package_symbol {
91 my ($self, $variable) = @_;
92
93 my ($name, $sigil, $type) = ref $variable eq 'HASH'
94 ? @{$variable}{qw[name sigil type]}
95 : $self->_deconstruct_variable_name($variable);
96
97 my $namespace = $self->namespace;
98
99 return unless exists $namespace->{$name};
100
101 my $entry_ref = \$namespace->{$name};
102 if (reftype($entry_ref) eq 'GLOB') {
103 if ( $type eq 'SCALAR' ) {
104 return defined ${ *{$entry_ref}{SCALAR} };
105 }
106 else {
107 return defined *{$entry_ref}{$type};
108 }
109 }
110 else {
111 # a symbol table entry can be -1 (stub), string (stub with prototype),
112 # or reference (constant)
113 return $type eq 'CODE';
114 }
115}
116
117sub get_package_symbol {
118 my ($self, $variable) = @_;
119
120 my ($name, $sigil, $type) = ref $variable eq 'HASH'
121 ? @{$variable}{qw[name sigil type]}
122 : $self->_deconstruct_variable_name($variable);
123
124 my $namespace = $self->namespace;
125
126 # FIXME
127 $self->add_package_symbol($variable)
128 unless exists $namespace->{$name};
129
130 my $entry_ref = \$namespace->{$name};
131
132 if (ref($entry_ref) eq 'GLOB') {
133 return *{$entry_ref}{$type};
134 }
135 else {
136 if ($type eq 'CODE') {
137 no strict 'refs';
138 return \&{ $self->name . '::' . $name };
139 }
140 else {
141 return undef;
142 }
143 }
144}
145
146sub remove_package_symbol {
147 my ($self, $variable) = @_;
148
149 my ($name, $sigil, $type) = ref $variable eq 'HASH'
150 ? @{$variable}{qw[name sigil type]}
151 : $self->_deconstruct_variable_name($variable);
152
153 # FIXME:
154 # no doubt this is grossly inefficient and
155 # could be done much easier and faster in XS
156
157 my ($scalar_desc, $array_desc, $hash_desc, $code_desc) = (
158 { sigil => '$', type => 'SCALAR', name => $name },
159 { sigil => '@', type => 'ARRAY', name => $name },
160 { sigil => '%', type => 'HASH', name => $name },
161 { sigil => '&', type => 'CODE', name => $name },
162 );
163
164 my ($scalar, $array, $hash, $code);
165 if ($type eq 'SCALAR') {
166 $array = $self->get_package_symbol($array_desc) if $self->has_package_symbol($array_desc);
167 $hash = $self->get_package_symbol($hash_desc) if $self->has_package_symbol($hash_desc);
168 $code = $self->get_package_symbol($code_desc) if $self->has_package_symbol($code_desc);
169 }
170 elsif ($type eq 'ARRAY') {
171 $scalar = $self->get_package_symbol($scalar_desc) if $self->has_package_symbol($scalar_desc);
172 $hash = $self->get_package_symbol($hash_desc) if $self->has_package_symbol($hash_desc);
173 $code = $self->get_package_symbol($code_desc) if $self->has_package_symbol($code_desc);
174 }
175 elsif ($type eq 'HASH') {
176 $scalar = $self->get_package_symbol($scalar_desc) if $self->has_package_symbol($scalar_desc);
177 $array = $self->get_package_symbol($array_desc) if $self->has_package_symbol($array_desc);
178 $code = $self->get_package_symbol($code_desc) if $self->has_package_symbol($code_desc);
179 }
180 elsif ($type eq 'CODE') {
181 $scalar = $self->get_package_symbol($scalar_desc) if $self->has_package_symbol($scalar_desc);
182 $array = $self->get_package_symbol($array_desc) if $self->has_package_symbol($array_desc);
183 $hash = $self->get_package_symbol($hash_desc) if $self->has_package_symbol($hash_desc);
184 }
185 else {
186 confess "This should never ever ever happen";
187 }
188
189 $self->remove_package_glob($name);
190
191 $self->add_package_symbol($scalar_desc => $scalar) if defined $scalar;
192 $self->add_package_symbol($array_desc => $array) if defined $array;
193 $self->add_package_symbol($hash_desc => $hash) if defined $hash;
194 $self->add_package_symbol($code_desc => $code) if defined $code;
195}
196
197sub list_all_package_symbols {
198 my ($self, $type_filter) = @_;
199
200 my $namespace = $self->namespace;
201 return keys %{$namespace} unless defined $type_filter;
202
203 # NOTE:
204 # or we can filter based on
205 # type (SCALAR|ARRAY|HASH|CODE)
206 if ($type_filter eq 'CODE') {
207 return grep {
208 (ref($namespace->{$_})
209 ? (ref($namespace->{$_}) eq 'SCALAR')
210 : (ref(\$namespace->{$_}) eq 'GLOB'
211 && defined(*{$namespace->{$_}}{CODE})));
212 } keys %{$namespace};
213 } else {
214 return grep { *{$namespace->{$_}}{$type_filter} } keys %{$namespace};
215 }
216}
f4979588 217
218=head1 BUGS
219
220No known bugs.
221
222Please report any bugs through RT: email
223C<bug-stash-manip at rt.cpan.org>, or browse to
224L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Stash-Manip>.
225
226=head1 SEE ALSO
227
228
229=head1 SUPPORT
230
231You can find this documentation for this module with the perldoc command.
232
233 perldoc Stash::Manip
234
235You can also look for information at:
236
237=over 4
238
239=item * AnnoCPAN: Annotated CPAN documentation
240
241L<http://annocpan.org/dist/Stash-Manip>
242
243=item * CPAN Ratings
244
245L<http://cpanratings.perl.org/d/Stash-Manip>
246
247=item * RT: CPAN's request tracker
248
249L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Stash-Manip>
250
251=item * Search CPAN
252
253L<http://search.cpan.org/dist/Stash-Manip>
254
255=back
256
257=head1 AUTHOR
258
259 Jesse Luehrs <doy at tozt dot net>
260
261=head1 COPYRIGHT AND LICENSE
262
263This software is copyright (c) 2010 by Jesse Luehrs.
264
265This is free software; you can redistribute it and/or modify it under
266the same terms as perl itself.
267
268=cut
269
2701;