File Coverage

blib/lib/Class/Std/Fast/Storable.pm
Criterion Covered Total %
statement 74 74 100.0
branch 27 34 79.4
condition 3 6 50.0
subroutine 12 12 100.0
pod 2 2 100.0
total 118 128 92.1


line stmt bran cond sub pod time code
1             package Class::Std::Fast::Storable;
2              
3 2     2   28807 use version; $VERSION = qv('0.0.8');
  2         2583  
  2         13  
4 2     2   157 use strict;
  2         3  
  2         67  
5 2     2   9 use warnings;
  2         4  
  2         60  
6 2     2   10 use Carp;
  2         24  
  2         199  
7 2     2   2575 use Storable;
  2         8829  
  2         185  
8              
9             BEGIN {
10 2     2   1011 require Class::Std::Fast;
11             }
12              
13             my $attributes_of_ref = {};
14             my @exported_subs = qw(
15             Class::Std::Fast::ident
16             Class::Std::Fast::DESTROY
17             Class::Std::Fast::MODIFY_CODE_ATTRIBUTES
18             Class::Std::Fast::AUTOLOAD
19             Class::Std::Fast::_DUMP
20             STORABLE_freeze
21             STORABLE_thaw
22             MODIFY_HASH_ATTRIBUTES
23             );
24              
25             sub import {
26 5     5   1483 my $caller_package = caller;
27              
28 5 50 33     45 my %flags = (@_>=3)
    50          
29             ? @_[1..$#_]
30             : (@_==2) && $_[1] >=2
31             ? ( constructor => 'basic', cache => 0 )
32             : ( constructor => 'normal', cache => 0);
33 5 50       19 $flags{cache} = 0 if not defined $flags{cache};
34 5 50       12 $flags{constructor} = 'normal' if not defined $flags{constructor};
35              
36 5         26 Class::Std::Fast::_init_import(
37             $caller_package, %flags
38             );
39              
40 2     2   14 no strict qw(refs);
  2         5  
  2         729  
41 5         13 for my $name ( @exported_subs ) {
42 40         230 my ($sub_name) = $name =~ m{(\w+)\z}xms;
43 40         52 *{ $caller_package . '::' . $sub_name } = \&{$name};
  40         428  
  40         96  
44             }
45             }
46              
47             sub MODIFY_HASH_ATTRIBUTES {
48 7     7   104 my $caller_package = $_[0];
49 7         17 my @unhandled = Class::Std::Fast::MODIFY_HASH_ATTRIBUTES(@_);
50 7         9 my $i = 0;
51 12 100       56 $attributes_of_ref->{$caller_package} = {
52             map {
53 7 50       18 $_->{name} eq '????' ? '????_' . $i++ : $_->{name}
54             => $_->{ref};
55 7         9 } @{Class::Std::Fast::_get_internal_attributes($caller_package) || []}
56             };
57 7         23 return @unhandled;
58             }
59              
60             # It's a constant - so there's no use creating it in each freeze again
61             my $FROZEN_ANON_SCALAR = Storable::freeze(\(my $anon_scalar));
62              
63             sub STORABLE_freeze {
64             # TODO do we really need to unpack @_? We're getting called for
65             # Zillions of objects...
66 18     18 1 1603 my($self, $cloning) = @_;
67 18 100       75 Class::Std::Fast::real_can($self, 'STORABLE_freeze_pre')
68             && $self->STORABLE_freeze_pre($cloning);
69              
70 18         325 my %frozen_attr; #to be constructed
71 18         19 my $id = ${$self};
  18         37  
72 18         41 my @package_list = ref $self;
73 18         37 my %package_seen = ( $package_list[0] => 1 ); # ignore diamond/looped base classes :-)
74              
75 2     2   35 no strict qw(refs);
  2         5  
  2         1086  
76              
77             PACKAGE:
78 18         45 while( my $package = shift @package_list) {
79             #make sure we add any base classes to the list of
80             #packages to examine for attributes.
81              
82             # Original line:
83             # push @package_list, grep { ! $package_seen{$_}++; } @{"${package}::ISA"};
84             # This one's faster...
85 30 50       36 push @package_list, grep { ! exists $package_seen{$_} && do { $package_seen{$_} = undef; 1; } } @{"${package}::ISA"};
  12         1233  
  12         24  
  12         27  
  30         88  
86              
87             #look for any attributes of this object for this package
88 30 50       92 my $attr_ref = $attributes_of_ref->{$package} or next PACKAGE;
89              
90             # TODO replace inner my variable by $_ - faster...
91 30         87 ATTR: # examine attributes from known packages only
92 30         30 for ( keys %{$attr_ref} ) {
93             #nothing to do if attr not set for this object
94 66 100       350 exists $attr_ref->{$_}{$id}
95             and $frozen_attr{$package}{ $_ } = $attr_ref->{$_}{$id}; # save the attr by name into the package hash
96             }
97             }
98 18 100       79 Class::Std::Fast::real_can($self, 'STORABLE_freeze_post')
99             && $self->STORABLE_freeze_post($cloning, \%frozen_attr);
100              
101 18         557 return ($FROZEN_ANON_SCALAR, \%frozen_attr);
102             }
103              
104             sub STORABLE_thaw {
105             # croak "must be called from Storable" unless caller eq 'Storable';
106             # unfortunately, Storable never appears on the call stack.
107              
108             # TODO do we really need to unpack @_? We're getting called for
109             # zillions of objects...
110 21     21 1 1347 my $self = shift;
111 21         26 my $cloning = shift;
112 21         25 my $frozen_attr_ref = $_[1]; # $_[0] is the frozen anon scalar.
113              
114 21 100       83 Class::Std::Fast::real_can($self, 'STORABLE_thaw_pre')
115             && $self->STORABLE_thaw_pre($cloning, $frozen_attr_ref);
116              
117 21   66     25 my $id = ${$self} ||= Class::Std::Fast::ID();
  21         84  
118              
119 51         159 PACKAGE:
120 21         30 while( my ($package, $pkg_attr_ref) = each %{$frozen_attr_ref} ) {
121             # TODO This test is quite expensive. Is there a better one?
122 33 100       296 $self->isa($package)
123             or croak "unknown base class '$package' seen while thawing "
124             . ref $self;
125 32         82 ATTR:
126 32         40 for ( keys %{$attributes_of_ref->{$package}} ) {
127             # for known attrs...
128             # nothing to do if frozen attr doesn't exist
129 70 100       180 exists $pkg_attr_ref->{$_} or next ATTR;
130              
131             # block attempts to meddle with existing objects
132 60 100       316 exists $attributes_of_ref->{$package}->{$_}->{$id}
133             and croak "trying to modify existing attributes for $package";
134              
135             # ok, set the attribute
136 59         175 $attributes_of_ref->{$package}->{$_}->{$id}
137             = delete $pkg_attr_ref->{$_};
138             }
139             # this is probably serious enough to throw an exception.
140             # however, TODO: it would be nice if the class could somehow
141             # indicate to ignore this problem.
142 31 100       193 %$pkg_attr_ref
143             and croak "unknown attribute(s) seen while thawing class $package:"
144             . join q{, }, keys %$pkg_attr_ref;
145             }
146              
147 18 100       198 Class::Std::Fast::real_can($self, 'STORABLE_thaw_post')
148             && $self->STORABLE_thaw_post($cloning);
149             }
150              
151             1;
152              
153             __END__