File Coverage

blib/lib/Class/Simple.pm
Criterion Covered Total %
statement 189 195 96.9
branch 61 74 82.4
condition 8 11 72.7
subroutine 34 35 97.1
pod 5 5 100.0
total 297 320 92.8


line stmt bran cond sub pod time code
1             #$Id: Simple.pm,v 1.31 2008/02/01 00:38:06 sullivan Exp $
2             #
3             # See the POD documentation starting towards the __END__ of this file.
4              
5             package Class::Simple;
6              
7 11     11   238135 use 5.008;
  11         42  
  11         450  
8 11     11   62 use strict;
  11         27  
  11         400  
9 11     11   55 use warnings;
  11         24  
  11         556  
10              
11             our $VERSION = '0.19';
12              
13 11     11   63 use Scalar::Util qw(refaddr);
  11         26  
  11         3020  
14 11     11   70 use Carp;
  11         24  
  11         875  
15 11     11   11203 use Class::ISA;
  11         45291  
  11         343  
16 11     11   89 use List::Util qw( first );
  11         21  
  11         1956  
17              
18             my %STORAGE;
19             my %PRIVATE;
20             my %READONLY;
21             my @internal_attributes = qw(CLASS);
22              
23             our $AUTOLOAD;
24             our $No_serialized_code;
25              
26             sub AUTOLOAD
27             {
28 97     97   4425 my $self = $_[0]; # DO NOT use shift(). It causes problems with goto.
29              
30 11     11   61 no strict 'refs';
  11         22  
  11         17758  
31              
32 97         511 $AUTOLOAD =~ /(.*)::((get|set|clear|raise|readonly|_)_)?(\w+)/;
33 97         208 my $pkg = $1;
34 97   100     972 my $prefix = $3 || '';
35 97         314 my $attrib = $4;
36              
37             #
38             # $obj->set__foo and $obj->get__foo
39             #
40 97         110 my $store_as;
41 97 100 100     463 if ($prefix eq '_')
    100          
42             {
43 1         3 $attrib = "_$attrib";
44 1         3 $store_as = $attrib;
45             }
46             #
47             # $obj->_foo
48             #
49             elsif (($attrib =~ /^_/) && !$prefix)
50             {
51 5         19 $store_as = $attrib;
52 5         23 $store_as =~ s/^_//;
53             }
54             else
55             {
56 91         150 $store_as = $attrib;
57             }
58              
59 97         182 my $setter = "set_$attrib";
60 97         145 my $getter = "get_$attrib";
61              
62 97 100       502 if (my $get_attributes = $self->can('ATTRIBUTES'))
63             {
64 8         21 my @attributes = &$get_attributes();
65 8         35 push(@attributes, @internal_attributes);
66 26     26   370 croak("$attrib is not a defined attribute in $pkg")
67 8 100       47 unless first(sub {$_ eq $attrib}, @attributes);
68             }
69              
70 96 50       569 if (my $get_private = $pkg->can('PRIVATIZE'))
71             {
72 0         0 my @privates = &$get_private();
73 0 0 0 0   0 if ( first(sub {$_ eq $attrib}, @privates)
  0         0  
74             && (_my_caller() ne $pkg) )
75             {
76 0         0 croak("$attrib is private to $pkg.");
77             }
78             }
79              
80             #
81             # Make sure that if you add more special prefixes here,
82             # you add them to the $AUTOLOAD regex above, too.
83             #
84 96 100 100     550 if ($prefix eq 'set')
    100          
    100          
    100          
    100          
    100          
85             {
86 28         94 *{$AUTOLOAD} = sub
87             {
88 54     54   631 my $self = shift;
89              
90 54         155 my $ref = refaddr($self);
91 54         237 my $store_as = $self->_caller_class($store_as);
92 54 100       432 croak("$attrib is readonly: cannot set.")
93             if ($READONLY{$ref}->{$store_as});
94 52         206 return ($STORAGE{$ref}->{$store_as} = shift(@_));
95 28         150 };
96             }
97             elsif ($prefix eq 'get')
98             {
99 25         87 *{$AUTOLOAD} = sub
100             {
101 74     74   132 my $self = shift;
102              
103 74         166 my $ref = refaddr($self);
104 74         217 my $store_as = $self->_caller_class($store_as,
105             $STORAGE{$ref});
106 74         465 return ($STORAGE{$ref}->{$store_as});
107 25         149 };
108             }
109             #
110             # Bug #7528 in Perl keeps this from working.
111             # http://rt.perl.org/rt3/Public/Bug/Display.html?id=7528
112             # I could make people declare methods they want to use lv_ with
113             # but that goes against the philosophy of being ::Simple.
114             #
115             # elsif ($prefix eq 'lv')
116             # {
117             # *{$AUTOLOAD} = sub : lvalue
118             # {
119             # my $self = shift;
120             #
121             # my $ref = refaddr($self);
122             # my $store_as = $self->_caller_class($store_as);
123             # croak("$attrib is readonly: cannot set.")
124             # if ($READONLY{$ref}->{$store_as});
125             # return ($STORAGE{$ref}->{$store_as});
126             # };
127             # }
128             elsif ($prefix eq 'clear')
129             {
130 1         6 *{$AUTOLOAD} = sub
131             {
132 2     2   5 my $self = shift;
133              
134 2         6 return ($self->$setter(undef));
135 1         4 };
136             }
137             elsif ($prefix eq 'raise')
138             {
139 1         4 *{$AUTOLOAD} = sub
140             {
141 2     2   5 my $self = shift;
142              
143 2         16 return ($self->$setter(1));
144 1         6 };
145             }
146             elsif ($prefix eq 'readonly')
147             {
148 13         68 *{$AUTOLOAD} = sub
149             {
150 22     22   62 my $self = shift;
151              
152 22         163 my $ret = $self->$setter(@_);
153 22         84 my $ref = refaddr($self);
154 22         59 my $store_as = $self->_caller_class($store_as);
155 22         66 $READONLY{$ref}->{$store_as} = 1;
156 22         47 return ($ret);
157 13         90 };
158             }
159             #
160             # All methods starting with '_' can only be called from
161             # within their package. Not inheritable, which makes
162             # the test easier than something privatized..
163             #
164             # Note that we cannot just call get_ and set_ here
165             # because if someone writes their own get_foo and then
166             # _foo is called, _foo will call set_foo, which will
167             # probably store something with _foo, which will call
168             # set_foo, etc. Sure wish we could somehow share
169             # code with get_ and set_, though.
170             #
171             elsif (!$prefix && ($attrib =~ /^_/))
172             {
173 5 50       33 if (my $method = $pkg->can($attrib))
174             {
175 0         0 goto &$method;
176             }
177              
178 5         33 *{$AUTOLOAD} = sub
179             {
180 8     8   3542 my $self = shift;
181              
182 8 100       28 croak("Cannot call $attrib: Private method to $pkg.")
183             unless ($pkg->isa(Class::Simple::_my_caller()));
184 6         20 my $ref = refaddr($self);
185 6 100       18 if (scalar(@_))
186             {
187 3         11 my $store_as = $self->_caller_class($store_as);
188 3 50       23 croak("$attrib is readonly: cannot set.")
189             if ($READONLY{$ref}->{$store_as});
190 3         16 return ($STORAGE{$ref}->{$store_as} =shift(@_));
191             }
192             else
193             {
194 3         127 my $store_as = $self->_caller_class($store_as,
195             $STORAGE{$ref});
196 3         18 return ($STORAGE{$ref}->{$store_as});
197             }
198 5         36 };
199             }
200             else
201             {
202 23         94 *{$AUTOLOAD} = sub
203             {
204 73     73   19865 my $self = shift;
205              
206 73 100       468 return (scalar(@_)
207             ? $self->$setter(@_)
208             : $self->$getter());
209 23         118 };
210             }
211 96         377 goto &$AUTOLOAD;
212             }
213              
214              
215              
216             #
217             # Call all the DEMOLISH()es and then delete from %STORAGE.
218             #
219             sub DESTROY
220             {
221 20     20   3634 my $self = shift;
222              
223 20         68 $self->_travel_isa('DESTROY', 'DEMOLISH');
224 20         80 my $ref = refaddr($self);
225 20 50       153 delete($STORAGE{$ref}) if exists($STORAGE{$ref});
226 20 100       1223 delete($READONLY{$ref}) if exists($READONLY{$ref});
227             }
228              
229              
230              
231             #
232             # Travel up the class's @ISA and run $func, if we can.
233             # To keep from running a sub more than once we flag
234             # $storage in %STORAGE.
235             #
236             sub _travel_isa
237             {
238 39     39   64 my $self = shift;
239 39         61 my $storage = shift;
240 39         55 my $func = shift;
241              
242 39         99 my $ref = refaddr($self);
243 39 50       223 $STORAGE{$ref}->{$storage}= {} unless exists($STORAGE{$ref}->{$storage});
244 39         177 my @path = reverse(Class::ISA::super_path($self->CLASS));
245 39         2001 foreach my $c (@path)
246             {
247 57 100       188 next if ($c eq __PACKAGE__);
248 20 50       91 next if $STORAGE{$ref}->{$storage}->{$c}++;
249              
250 20         42 my $cn = "${c}::can";
251 20 100       179 if (my $in = $c->can($func))
252             {
253 7         23 $self->$in(@_);
254             }
255             }
256 39 100       425 $self->$func(@_) if $self->can($func);
257             }
258              
259              
260              
261             #
262             # Figures out the class of the caller, going up the class hierarchy
263             # starting at the current class and going up until we find something
264             # stored. Confusing, eh? We're trying to properly handle the following:
265             #
266             # package Foo;
267             # use base qw(Bar);
268             # ...
269             # $self->set_a(1);
270             # ...
271             # package Bar;
272             # use base qw(Class::Simple);
273             # ...
274             # $self->set_a(2);
275             #
276             # The set_a in Bar should not affect the set_a in Foo and neither should
277             # affect an Foo object that has done its own set_a.
278             #
279             sub _caller_class
280             {
281 162     162   205 my $self = shift;
282 162         225 my $store_as = shift;
283 162         192 my $storage = shift;
284              
285             #
286             # There is a problem with multiple-inheritance. Until I
287             # can figure out a solution, just return $store_as to
288             # disable this feature.
289             #
290 162         422 return $store_as;
291             # for (my $i = 0; my $c = scalar(caller($i)); ++$i)
292             # {
293             # next if ($c eq __PACKAGE__);
294             # my $sa = "${c}::${store_as}";
295             # if ($storage)
296             # {
297             # next unless $self->isa($c);
298             # my @path = reverse(Class::ISA::super_path($c));
299             # foreach my $p ($c, @path)
300             # {
301             # my $sa = "${p}::${store_as}";
302             # return ($sa) if exists($storage->{$sa});
303             # }
304             # }
305             # else
306             # {
307             # return ($sa) if $self->isa($c);
308             # }
309             # }
310             # my $sa = ref($self) . "::${store_as}";
311             # return ($sa); # Shouldn't get here but just in case
312             }
313              
314              
315              
316             #
317             # Make a scalar. Bless it. Call init.
318             #
319             sub new
320             {
321 20     20 1 21346 my $class = shift;
322              
323             #
324             # Support for NONEW.
325             #
326             {
327 11     11   81 no strict 'refs';
  11         25  
  11         2422  
  20         38  
328 20         56 my $classy = "${class}::";
329 20         292 croak("Cannot call new() in $class.")
330 20 100       33 if exists(${$classy}{'NONEW'});
331             }
332              
333             #
334             # This is how you get an anonymous scalar.
335             #
336 19         39 my $self = \do{my $anon_scalar};
  19         44  
337 19         48 bless($self, $class);
338 19         220 $self->readonly_CLASS($class);
339              
340 19         122 $self->init(@_);
341 19         51 return ($self);
342             }
343              
344              
345              
346             #
347             # Flag the given method(s) as being private to the class
348             # (and its children unless overridden).
349             #
350             sub privatize
351             {
352 8     8 1 2218 my $class = shift;
353              
354 8         21 foreach my $method (@_)
355             {
356 11     11   184 no strict 'refs';
  11         21  
  11         2700  
357              
358             #
359             # Can't privatize something that is already private
360             # from an ancestor.
361             #
362 9         31 foreach my $private_class (keys(%PRIVATE))
363             {
364 7 100       30 next unless $PRIVATE{$private_class}->{$method};
365 1 50       280 croak("Cannot privatize ${class}::$method: already private in $private_class.")
366             unless $private_class->isa($class);
367             }
368              
369             #
370             # Can't retroactively make privatize something.
371             #
372 8         29 my $called_by = _my_caller();
373 8 100       5204 croak("Attempt to privatize ${class}::$method from $called_by. Can only privatize in your own class.")
374             if ($class ne $called_by);
375 7         27 $PRIVATE{$class}->{$method} = 1;
376              
377             #
378             # Although it is duplication of code (which I hope
379             # to come up with a clever way to avoid at some point),
380             # it is a better solution to have privatize() create
381             # these subs now. Otherwise, having the private test
382             # done in AUTOLOAD gets to be fairly convoluted.
383             # Defining them here makes the tests a lot simpler.
384             #
385 7         24 my $getter = "${class}::get_$method";
386 7         18 my $setter = "${class}::set_$method";
387 7         21 my $generic = "${class}::$method";
388              
389 7         56 *{$getter} = sub
390             {
391 1     1   2 my $self = shift;
392              
393 11     11   56 no strict 'refs';
  11         649  
  11         2394  
394 1 50       3 croak("Cannot call $getter: Private method to $class.")
395             unless $class->isa(Class::Simple::_my_caller());
396 1         9 my $ref = refaddr($self);
397 1         3 my $store_as = $self->_caller_class($method);
398 1         5 return ($STORAGE{$ref}->{$store_as});
399 7         60 };
400             *$setter = sub
401             {
402 5     5   9 my $self = shift;
403              
404 11     11   59 no strict 'refs';
  11         31  
  11         2142  
405 5 50       16 croak("Cannot call $setter: Private method to $class.")
406             unless $class->isa(Class::Simple::_my_caller());
407 5         17 my $ref = refaddr($self);
408 5         13 my $store_as = $self->_caller_class($method);
409 5 100       331 croak("$method is readonly: cannot set.")
410             if ($READONLY{$ref}->{$store_as});
411 4         26 return ($STORAGE{$ref}->{$store_as} = shift(@_));
412 7         180 };
413             *$generic = sub
414             {
415 6     6   878 my $self = shift;
416              
417 11     11   67 no strict 'refs';
  11         21  
  11         5646  
418 6 100       19 croak("Cannot call $generic: Private method to $class.")
419             unless $class->isa(Class::Simple::_my_caller());
420 3         11 my $ref = refaddr($self);
421 3 100       22 return (scalar(@_)
422             ? $self->$setter(@_)
423             : $self->$getter());
424 7         62 };
425 7         33 my $ugen = "_${generic}";
426 7         90 *$ugen = *$generic;
427             }
428             }
429              
430              
431              
432             #
433             # Bubble up the caller() stack until we leave this package.
434             #
435             sub _my_caller
436             {
437 28     28   121 for (my $i = 0; my $c = caller($i); ++$i)
438             {
439 60 100       1919 return ($c) unless $c eq __PACKAGE__;
440             }
441 0         0 return (__PACKAGE__); # Shouldn't get here but just in case
442             }
443              
444              
445              
446             #
447             # This will not be called if the child classes have
448             # their own. In case they don't (and they really shouldn't
449             # because they should be using BUILD() instead), this is the default.
450             #
451             sub init
452             {
453 19     19 1 36 my $self = shift;
454              
455 19         170 $self->_travel_isa('init', 'BUILD', @_);
456 19         43 return ($self);
457             }
458              
459              
460              
461             ##
462             ## toJson() and fromJson() are DUMP and SLURP equivalents for JSON.
463             ## I'm not sure if they're all that useful yet so they're silently
464             ## lurking here for now.
465             ##
466             #sub toJson
467             #{
468             #my $self = shift;
469             #
470             # croak("Cannot use toJson(): module JSON::XS not found.\n")
471             # unless (eval 'require JSON::XS; 1');
472             #
473             # my $ref = refaddr($self);
474             # my $json = JSON::XS->new();
475             # return $json->encode($STORAGE{$ref});
476             #}
477             #
478             #
479             #
480             #sub fromJson
481             #{
482             #my $self = shift;
483             #my $str = shift;
484             #
485             # return $self unless $str;
486             #
487             # croak("Cannot use fromJson(): module JSON::XS not found.\n")
488             # unless (eval 'require JSON::XS; 1');
489             #
490             # my $json = JSON::XS->new();
491             # my $obj = $json->decode($str);
492             # my $ref = refaddr($self);
493             # $STORAGE{$ref} = $obj;
494             #
495             # return ($self);
496             #}
497              
498              
499              
500             #
501             # Callback for Storable to serialize objects.
502             #
503             sub STORABLE_freeze
504             {
505 3     3 1 1481 my $self = shift;
506 3         5 my $cloning = shift;
507              
508 3 50       189 croak("Cannot use STORABLE_freeze(): module Storable not found.\n")
509             unless (eval 'require Storable; 1');
510              
511 3         90 $Storable::Deparse = !$No_serialized_code;
512 3         11 my $ref = refaddr($self);
513 3         13 return Storable::freeze($STORAGE{$ref});
514             }
515              
516              
517              
518             #
519             # Callback for Storable to reconstitute serialized objects.
520             #
521             sub STORABLE_thaw
522             {
523 3     3 1 1741 my $self = shift;
524 3         5 my $cloning = shift;
525 3         7 my $serialized = shift;
526              
527 3 50       220 croak("Cannot use STORABLE_thaw(): module Storable not found.\n")
528             unless (eval 'require Storable; 1');
529              
530 3         10 $Storable::Eval = !$No_serialized_code;
531 3         11 my $ref = refaddr($self);
532 3         12 $STORAGE{$ref} = Storable::thaw($serialized);
533             }
534              
535             1;
536             __END__