File Coverage

blib/lib/Class/Cloneable.pm
Criterion Covered Total %
statement 74 74 100.0
branch 44 46 95.6
condition 25 27 92.5
subroutine 11 11 100.0
pod 1 1 100.0
total 155 159 97.4


line stmt bran cond sub pod time code
1              
2             package Class::Cloneable;
3              
4 1     1   30758 use strict;
  1         2  
  1         39  
5 1     1   4 use warnings;
  1         2  
  1         80  
6              
7             our $VERSION = '0.03';
8              
9             sub clone {
10 4     4 1 20975 my ($self) = @_;
11 4         11 return Class::Cloneable::Util::clone($self);
12             }
13              
14             package Class::Cloneable::Util;
15              
16 1     1   4 use strict;
  1         14  
  1         29  
17 1     1   4 use warnings;
  1         2  
  1         26  
18              
19 1     1   4129 use overload ();
  1         937  
  1         20  
20 1     1   5 use Carp qw(confess);
  1         2  
  1         74  
21 1     1   4 use Scalar::Util qw(blessed reftype weaken isweak);
  1         1  
  1         783  
22              
23             our $VERSION = '0.03';
24              
25             sub clone {
26 60 100 100 60   20586 (UNIVERSAL::isa((caller)[0], 'Class::Cloneable') ||
27             UNIVERSAL::isa((caller)[0], 'Class::Cloneable::Util'))
28             || confess "Illegal Operation : This method can only be called by a subclass of Class::Cloneable";
29 58         115 my ($to_clone, $cache) = @_;
30 58 100       312 (defined($to_clone))
31             || confess "Insufficient Arguments : Must specify the object to clone";
32             # To start with, non-reference values are
33             # not copied, just returned, cache or not
34 57 100       117 return $to_clone unless ref($to_clone);
35             # now check for an active cache
36 52 100       89 unless(defined $cache) {
37             # now we check to see what we have,
38             # and deconstruct and deep copy the
39             # top-level Class::Cloneable objects
40 6 100 100     65 if (blessed($to_clone) && $to_clone->isa('Class::Cloneable')) {
41             # now copy the object's internals and
42             # bless the new clone into the right class
43             # storing it in the cache case we run
44             # into a circular ref
45 4         20 return $cache->{$to_clone} = bless(
46             cloneRef($to_clone, ($cache = {}), reftype($to_clone)),
47             blessed($to_clone)
48             );
49             }
50             # if it is not a Class::Cloneable, then
51             # we just proceed as normal
52             }
53             # if we have it in the cache them return the cached clone
54 48 100       142 return $cache->{$to_clone} if exists $cache->{$to_clone};
55             # now try it as an object, which will in
56             # turn try it as ref if its not an object
57             # now store it in case we run into a circular ref
58 44         77 return $cache->{$to_clone} = cloneObject($to_clone, $cache);
59             }
60              
61             sub cloneObject {
62 51 100 100 51   5219 (UNIVERSAL::isa((caller)[0], 'Class::Cloneable') ||
63             UNIVERSAL::isa((caller)[0], 'Class::Cloneable::Util'))
64             || confess "Illegal Operation : This method can only be called by a subclass of Class::Cloneable";
65 49         90 my ($to_clone, $cache) = @_;
66 49 100 100     1139 (ref($to_clone) && (ref($cache) && ref($cache) eq 'HASH'))
      66        
67             || confess "Insufficient Arguments : Must specify the object to clone and a valid cache";
68             # check to see if we have an Class::Cloneable object,
69             # or check to see if its an object, with a clone method
70 44 100       118 if (blessed($to_clone)) {
71             # note, we want to be sure to respect any overriding of
72             # the clone method with Class::Cloneable objects here
73             # otherwise it would be faster to just send it directly
74             # to the Class::Cloneable::Util::clone function above
75 9 100       90 return $cache->{$to_clone} = ($to_clone->can('clone') ?
76             $to_clone->clone()
77             :
78             # or if we have an object, with no clone method, then
79             # we will respect its encapsulation, and not muck with
80             # its internals. Basically, we assume it does not want
81             # to be cloned
82             $to_clone);
83             }
84             # if all else fails, it is likely a basic ref
85 35         66 return $cache->{$to_clone} = cloneRef($to_clone, $cache);
86             }
87              
88             sub cloneRef {
89 46 100 100 46   4981 (UNIVERSAL::isa((caller)[0], 'Class::Cloneable') ||
90             UNIVERSAL::isa((caller)[0], 'Class::Cloneable::Util'))
91             || confess "Illegal Operation : This method can only be called by a subclass of Class::Cloneable";
92 44         149 my ($to_clone, $cache, $ref_type) = @_;
93 44 100 100     1107 (ref($to_clone) && (ref($cache) && ref($cache) eq 'HASH'))
      66        
94             || confess "Insufficient Arguments : Must specify the object to clone and a valid cache";
95 39 100       83 $ref_type = ref($to_clone) unless defined $ref_type;
96             # check if it is weakened
97 39         53 my $is_weak;
98 39 50       99 $is_weak = 1 if isweak($to_clone);
99 39         36 my ($clone, $tied);
100 39 100 100     107 if ($ref_type eq 'HASH') {
    100          
    100          
101 16         22 $clone = {};
102 16 100       19 tie %{$clone}, ref $tied if $tied = tied(%{$to_clone});
  2         10  
  16         42  
103 16 100       26 %{$clone} = map { ref($_) ? clone($_, $cache) : $_ } %{$to_clone};
  16         66  
  92         218  
  16         46  
104             }
105             elsif ($ref_type eq 'ARRAY') {
106 13         21 $clone = [];
107 13 100       25 tie @{$clone}, ref $tied if $tied = tied(@{$to_clone});
  2         12  
  13         39  
108 13 100       19 @{$clone} = map { ref($_) ? clone($_, $cache) : $_ } @{$to_clone};
  13         37  
  20         43  
  13         31  
109             }
110             elsif ($ref_type eq 'REF' or $ref_type eq 'SCALAR') {
111 6         9 my $var = "";
112 6         7 $clone = \$var;
113 6 100       7 tie ${$clone}, ref $tied if $tied = tied(${$to_clone});
  2         11  
  6         24  
114 6         16 ${$clone} = clone(${$to_clone}, $cache);
  6         15  
  6         13  
115             }
116             else {
117             # shallow copy reference to code, glob, regex
118 4         5 $clone = $to_clone;
119             }
120             # store it in our cache
121 39         132 $cache->{$to_clone} = $clone;
122             # and weaken it if appropriate
123 39 50       588 weaken($clone) if $is_weak;
124             # and return the clone
125 39         202 return $clone;
126             }
127              
128             1;
129              
130             __END__