File Coverage

blib/lib/Stash/Manip.pm
Criterion Covered Total %
statement 110 133 82.7
branch 62 92 67.3
condition 15 21 71.4
subroutine 20 21 95.2
pod 9 9 100.0
total 216 276 78.2


line stmt bran cond sub pod time code
1             package Stash::Manip;
2             BEGIN {
3 7     7   204665 $Stash::Manip::VERSION = '0.02';
4             }
5 7     7   65 use strict;
  7         14  
  7         232  
6 7     7   38 use warnings;
  7         11  
  7         223  
7              
8 7     7   45 use Carp qw(confess);
  7         18  
  7         557  
9 7     7   47 use Scalar::Util qw(reftype);
  7         10  
  7         1549  
10              
11             =head1 NAME
12              
13             Stash::Manip - routines for manipulating stashes
14              
15             =head1 VERSION
16              
17             version 0.02
18              
19             =head1 SYNOPSIS
20              
21             my $stash = Stash::Manip->new('Foo');
22             $stash->add_package_symbol('%foo', {bar => 1});
23             # $Foo::foo{bar} == 1
24             $stash->has_package_symbol('$foo') # false
25             my $namespace = $stash->namespace;
26             *{ $namespace->{foo} }{HASH} # {bar => 1}
27              
28             =head1 DESCRIPTION
29              
30             Manipulating stashes (Perl's symbol tables) is occasionally necessary, but
31             incredibly messy, and easy to get wrong. This module hides all of that behind a
32             simple API.
33              
34             NOTE: Most methods in this class require a variable specification that includes
35             a sigil. If this sigil is absent, it is assumed to represent the IO slot.
36              
37             =head1 METHODS
38              
39             =cut
40              
41             =head2 new $package_name
42              
43             Creates a new C object, for the package given as the only
44             argument.
45              
46             =cut
47              
48             sub new {
49 11     11 1 6779 my $class = shift;
50 11         64 my ($namespace) = @_;
51 11         68 return bless { 'package' => $namespace }, $class;
52             }
53              
54             =head2 name
55              
56             Returns the name of the package that this object represents.
57              
58             =cut
59              
60             sub name {
61 129     129 1 568 return $_[0]->{package};
62             }
63              
64             =head2 namespace
65              
66             Returns the raw stash itself.
67              
68             =cut
69              
70             sub namespace {
71             # NOTE:
72             # because of issues with the Perl API
73             # to the typeglob in some versions, we
74             # need to just always grab a new
75             # reference to the hash here. Ideally
76             # we could just store a ref and it would
77             # Just Work, but oh well :\
78 7     7   46 no strict 'refs';
  7         24  
  7         3276  
79 96     96 1 105 return \%{$_[0]->name . '::'};
  96         204  
80             }
81              
82             {
83             my %SIGIL_MAP = (
84             '$' => 'SCALAR',
85             '@' => 'ARRAY',
86             '%' => 'HASH',
87             '&' => 'CODE',
88             '' => 'IO',
89             );
90              
91             sub _deconstruct_variable_name {
92 92     92   221 my ($self, $variable) = @_;
93              
94 92 50 33     508 (defined $variable && length $variable)
95             || confess "You must pass a variable name";
96              
97 92         187 my $sigil = substr($variable, 0, 1, '');
98              
99 92 100       395 if (exists $SIGIL_MAP{$sigil}) {
100 82         338 return ($variable, $sigil, $SIGIL_MAP{$sigil});
101             }
102             else {
103 10         42 return ("${sigil}${variable}", '', $SIGIL_MAP{''});
104             }
105             }
106             }
107              
108             =head2 add_package_symbol $variable $value
109              
110             Adds a new package symbol, for the symbol given as C<$variable>, and optionally
111             gives it an initial value of C<$value>. C<$variable> should be the name of
112             variable including the sigil, so
113              
114             Stash::Manip->new('Foo')->add_package_symbol('%foo')
115              
116             will create C<%Foo::foo>.
117              
118             =cut
119              
120             sub _valid_for_type {
121 29     29   41 my $self = shift;
122 29         42 my ($value, $type) = @_;
123 29 100 100     395 if ($type eq 'HASH' || $type eq 'ARRAY'
      100        
      100        
124             || $type eq 'IO' || $type eq 'CODE') {
125 19         144 return reftype($value) eq $type;
126             }
127             else {
128 10         31 my $ref = reftype($value);
129 10   66     133 return !defined($ref) || $ref eq 'SCALAR' || $ref eq 'REF' || $ref eq 'LVALUE';
130             }
131             }
132              
133             sub add_package_symbol {
134 30     30 1 8154 my ($self, $variable, $initial_value) = @_;
135              
136 14         38 my ($name, $sigil, $type) = ref $variable eq 'HASH'
137 30 100       119 ? @{$variable}{qw[name sigil type]}
138             : $self->_deconstruct_variable_name($variable);
139              
140 30 100       97 if (@_ > 2) {
141 29 100       72 $self->_valid_for_type($initial_value, $type)
142             || confess "$initial_value is not of type $type";
143             }
144              
145 26         65 my $pkg = $self->name;
146              
147 7     7   39 no strict 'refs';
  7         14  
  7         263  
148 7     7   35 no warnings 'redefine', 'misc', 'prototype';
  7         10  
  7         765  
149 26 100       63 *{$pkg . '::' . $name} = ref $initial_value ? $initial_value : \$initial_value;
  26         735  
150             }
151              
152             =head2 remove_package_glob $name
153              
154             Removes all package variables with the given name, regardless of sigil.
155              
156             =cut
157              
158             sub remove_package_glob {
159 6     6 1 11 my ($self, $name) = @_;
160 7     7   35 no strict 'refs';
  7         11  
  7         2877  
161 6         7 delete ${$self->name . '::'}{$name};
  6         14  
162             }
163              
164             # ... these functions deal with stuff on the namespace level
165              
166             =head2 has_package_symbol $variable
167              
168             Returns whether or not the given package variable (including sigil) exists.
169              
170             =cut
171              
172             sub has_package_symbol {
173 61     61 1 8935 my ($self, $variable) = @_;
174              
175 19         47 my ($name, $sigil, $type) = ref $variable eq 'HASH'
176 61 100       210 ? @{$variable}{qw[name sigil type]}
177             : $self->_deconstruct_variable_name($variable);
178              
179 61         141 my $namespace = $self->namespace;
180              
181 61 100       185 return unless exists $namespace->{$name};
182              
183 59         90 my $entry_ref = \$namespace->{$name};
184 59 50       320 if (reftype($entry_ref) eq 'GLOB') {
185 59 100       105 if ( $type eq 'SCALAR' ) {
186 9         15 return defined ${ *{$entry_ref}{SCALAR} };
  9         18  
  9         58  
187             }
188             else {
189 50         96 return defined *{$entry_ref}{$type};
  50         948  
190             }
191             }
192             else {
193             # a symbol table entry can be -1 (stub), string (stub with prototype),
194             # or reference (constant)
195 0         0 return $type eq 'CODE';
196             }
197             }
198              
199             =head2 get_package_symbol $variable
200              
201             Returns the value of the given package variable (including sigil).
202              
203             =cut
204              
205             sub get_package_symbol {
206 38     38 1 5231 my ($self, $variable) = @_;
207              
208 13         35 my ($name, $sigil, $type) = ref $variable eq 'HASH'
209 38 100       136 ? @{$variable}{qw[name sigil type]}
210             : $self->_deconstruct_variable_name($variable);
211              
212 38         92 my $namespace = $self->namespace;
213              
214 38 100       206 if (!exists $namespace->{$name}) {
215             # assigning to the result of this function like
216             # @{$stash->get_package_symbol('@ISA')} = @new_ISA
217             # makes the result not visible until the variable is explicitly
218             # accessed... in the case of @ISA, this might never happen
219             # for instance, assigning like that and then calling $obj->isa
220             # will fail. see t/005-isa.t
221 3 100 100     28 if ($type eq 'ARRAY' && $name ne 'ISA') {
    100          
222 1         7 $self->add_package_symbol($variable, []);
223             }
224             elsif ($type eq 'HASH') {
225 1         6 $self->add_package_symbol($variable, {});
226             }
227             else {
228             # FIXME
229 1         5 $self->add_package_symbol($variable)
230             }
231             }
232              
233 38         73 my $entry_ref = \$namespace->{$name};
234              
235 38 50       97 if (ref($entry_ref) eq 'GLOB') {
236 38         54 return *{$entry_ref}{$type};
  38         198  
237             }
238             else {
239 0 0       0 if ($type eq 'CODE') {
240 7     7   40 no strict 'refs';
  7         14  
  7         6113  
241 0         0 return \&{ $self->name . '::' . $name };
  0         0  
242             }
243             else {
244 0         0 return undef;
245             }
246             }
247             }
248              
249             =head2 remove_package_symbol $variable
250              
251             Removes the package variable described by C<$variable> (which includes the
252             sigil); other variables with the same name but different sigils will be
253             untouched.
254              
255             =cut
256              
257             sub remove_package_symbol {
258 6     6 1 2080 my ($self, $variable) = @_;
259              
260 0         0 my ($name, $sigil, $type) = ref $variable eq 'HASH'
261 6 50       31 ? @{$variable}{qw[name sigil type]}
262             : $self->_deconstruct_variable_name($variable);
263              
264             # FIXME:
265             # no doubt this is grossly inefficient and
266             # could be done much easier and faster in XS
267              
268 6         82 my ($scalar_desc, $array_desc, $hash_desc, $code_desc, $io_desc) = (
269             { sigil => '$', type => 'SCALAR', name => $name },
270             { sigil => '@', type => 'ARRAY', name => $name },
271             { sigil => '%', type => 'HASH', name => $name },
272             { sigil => '&', type => 'CODE', name => $name },
273             { sigil => '', type => 'IO', name => $name },
274             );
275              
276 6         13 my ($scalar, $array, $hash, $code, $io);
277 6 100       657 if ($type eq 'SCALAR') {
    50          
    100          
    100          
    50          
278 1 50       5 $array = $self->get_package_symbol($array_desc) if $self->has_package_symbol($array_desc);
279 1 50       5 $hash = $self->get_package_symbol($hash_desc) if $self->has_package_symbol($hash_desc);
280 1 50       5 $code = $self->get_package_symbol($code_desc) if $self->has_package_symbol($code_desc);
281 1 50       4 $io = $self->get_package_symbol($io_desc) if $self->has_package_symbol($io_desc);
282             }
283             elsif ($type eq 'ARRAY') {
284 0         0 $scalar = $self->get_package_symbol($scalar_desc);
285 0 0       0 $hash = $self->get_package_symbol($hash_desc) if $self->has_package_symbol($hash_desc);
286 0 0       0 $code = $self->get_package_symbol($code_desc) if $self->has_package_symbol($code_desc);
287 0 0       0 $io = $self->get_package_symbol($io_desc) if $self->has_package_symbol($io_desc);
288             }
289             elsif ($type eq 'HASH') {
290 1         3 $scalar = $self->get_package_symbol($scalar_desc);
291 1 50       4 $array = $self->get_package_symbol($array_desc) if $self->has_package_symbol($array_desc);
292 1 50       4 $code = $self->get_package_symbol($code_desc) if $self->has_package_symbol($code_desc);
293 1 50       4 $io = $self->get_package_symbol($io_desc) if $self->has_package_symbol($io_desc);
294             }
295             elsif ($type eq 'CODE') {
296 3         14 $scalar = $self->get_package_symbol($scalar_desc);
297 3 100       11 $array = $self->get_package_symbol($array_desc) if $self->has_package_symbol($array_desc);
298 3 100       8 $hash = $self->get_package_symbol($hash_desc) if $self->has_package_symbol($hash_desc);
299 3 100       8 $io = $self->get_package_symbol($io_desc) if $self->has_package_symbol($io_desc);
300             }
301             elsif ($type eq 'IO') {
302 1         4 $scalar = $self->get_package_symbol($scalar_desc);
303 1 50       3 $array = $self->get_package_symbol($array_desc) if $self->has_package_symbol($array_desc);
304 1 50       3 $hash = $self->get_package_symbol($hash_desc) if $self->has_package_symbol($hash_desc);
305 1 50       3 $code = $self->get_package_symbol($code_desc) if $self->has_package_symbol($code_desc);
306             }
307             else {
308 0         0 confess "This should never ever ever happen";
309             }
310              
311 6         21 $self->remove_package_glob($name);
312              
313 6         22 $self->add_package_symbol($scalar_desc => $scalar);
314 6 100       20 $self->add_package_symbol($array_desc => $array) if defined $array;
315 6 100       15 $self->add_package_symbol($hash_desc => $hash) if defined $hash;
316 6 100       38 $self->add_package_symbol($code_desc => $code) if defined $code;
317 6 100       2665 $self->add_package_symbol($io_desc => $io) if defined $io;
318             }
319              
320             =head2 list_all_package_symbols $type_filter
321              
322             Returns a list of package variable names in the package, without sigils. If a
323             C is passed, it is used to select package variables of a given
324             type, where valid types are the slots of a typeglob ('SCALAR', 'CODE', 'HASH',
325             etc).
326              
327             =cut
328              
329             sub list_all_package_symbols {
330 0     0 1   my ($self, $type_filter) = @_;
331              
332 0           my $namespace = $self->namespace;
333 0 0         return keys %{$namespace} unless defined $type_filter;
  0            
334              
335             # NOTE:
336             # or we can filter based on
337             # type (SCALAR|ARRAY|HASH|CODE)
338 0 0         if ($type_filter eq 'CODE') {
339             return grep {
340 0           (ref($namespace->{$_})
341             ? (ref($namespace->{$_}) eq 'SCALAR')
342             : (ref(\$namespace->{$_}) eq 'GLOB'
343 0 0 0       && defined(*{$namespace->{$_}}{CODE})));
344 0           } keys %{$namespace};
345             } else {
346 0           return grep { *{$namespace->{$_}}{$type_filter} } keys %{$namespace};
  0            
  0            
  0            
347             }
348             }
349              
350             =head1 BUGS
351              
352             No known bugs.
353              
354             Please report any bugs through RT: email
355             C, or browse to
356             L.
357              
358             =head1 SEE ALSO
359              
360             L - this module is a factoring out of code that used to
361             live here
362              
363             =head1 SUPPORT
364              
365             You can find this documentation for this module with the perldoc command.
366              
367             perldoc Stash::Manip
368              
369             You can also look for information at:
370              
371             =over 4
372              
373             =item * AnnoCPAN: Annotated CPAN documentation
374              
375             L
376              
377             =item * CPAN Ratings
378              
379             L
380              
381             =item * RT: CPAN's request tracker
382              
383             L
384              
385             =item * Search CPAN
386              
387             L
388              
389             =back
390              
391             =head1 AUTHOR
392              
393             Jesse Luehrs
394              
395             Mostly copied from code from L, by Stevan Little and the
396             Moose Cabal.
397              
398             =head1 COPYRIGHT AND LICENSE
399              
400             This software is copyright (c) 2010 by Jesse Luehrs.
401              
402             This is free software; you can redistribute it and/or modify it under
403             the same terms as perl itself.
404              
405             =cut
406              
407             1;