File Coverage

blib/lib/KiokuDB/TypeMap/Entry/StorableHook.pm
Criterion Covered Total %
statement 63 78 80.7
branch 18 30 60.0
condition n/a
subroutine 12 15 80.0
pod 0 4 0.0
total 93 127 73.2


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package KiokuDB::TypeMap::Entry::StorableHook;
4 13     13   26353 use Moose;
  13         339210  
  13         88  
5              
6 13     13   68517 use Scalar::Util qw(reftype);
  13         19  
  13         863  
7 13     13   65 use Carp qw(croak);
  13         20  
  13         547  
8              
9 13     13   54 no warnings 'recursion';
  13         24  
  13         888  
10              
11             # predeclare for namespace::clean;
12             sub _type ($);
13             sub _new ($;$);
14             sub _clear ($);
15              
16 13     13   69 use namespace::clean -except => 'meta';
  13         18  
  13         115  
17              
18             with qw(
19             KiokuDB::TypeMap::Entry::Std
20             KiokuDB::TypeMap::Entry::Std::Expand
21             );
22              
23             sub compile_collapse_body {
24 36     36 0 77 my ( $self, $class, @args ) = @_;
25              
26 36 50       464 my $attach = $class->can("STORABLE_attach") ? 1 : 0;
27              
28             return sub {
29 36     36   113 my ( $self, %args ) = @_;
30              
31 36         124 my $object = $args{object};
32              
33 36         122 my @type = _type($object);
34              
35 36         236 my ( $str, @refs ) = $object->STORABLE_freeze(0);
36              
37 36         494 my $data;
38              
39 36 100       98 if ( @refs ) {
40 35 50       160 croak sprintf "Freeze cannot return references if %s class is using STORABLE_attach", $class if $attach;
41              
42 35 50       79 if ( my @non_refs = grep { not ref } @refs ) {
  68         200  
43 0         0 croak blessed($object) . "::STORABLE_freeze returned non reference values: @non_refs";
44             }
45              
46 35         205 my @collapsed = $self->visit(@refs);
47              
48 35         307 foreach my $ref ( @collapsed ) {
49 68 50       214 next unless ref($ref) eq 'KiokuDB::Reference';
50 68 50       225 next if $self->may_compact($ref);
51 0         0 $ref = $ref->id; # don't save a bunch of Reference objects when all we need is the ID
52             }
53              
54 35         141 $data = [ @type, $str, @collapsed ],
55             } else {
56 1 50       2 unless ( $attach ) {
57 1 50       3 if ( @type == 1 ) {
58 1         3 $data = ( $type[0] . $str );
59             } else {
60 0         0 $data = [ @type, $str ];
61             }
62             } else {
63 0         0 $data = $str;
64             }
65             }
66              
67 36         222 return $self->make_entry(
68             %args,
69             data => $data,
70             );
71 36         381 };
72             }
73              
74             sub compile_create {
75 36     36 0 74 my ( $self, $class, @args ) = @_;
76              
77 36 50       180 unless ( $class->can("STORABLE_attach") ) {
78             # normal form, STORABLE_freeze
79             return sub {
80 36     36   102 my ( $self, $entry ) = @_;
81              
82 36         921 my $data = $entry->data;
83              
84 36 100       213 my ( $reftype, @args ) = ref $data ? @$data : ( substr($data, 0, 1), substr($data, 1) );
85              
86 36         66 my $instance;
87              
88 36 50       426 if ( ref $args[0] ) {
89 0         0 my $tied;
90 0         0 $self->queue_ref(shift(@args), \$tied);
91 0         0 $instance = _new( $reftype, $tied );
92             } else {
93 36         139 $instance = _new( $reftype );
94             }
95              
96 36         878 bless $instance, $entry->class;
97 36         283 };
98             } else {
99             # esotheric STORABLE_attach form
100             return sub {
101 0     0   0 my ( $self, $entry ) = @_;
102              
103 0         0 $entry->class->STORABLE_attach( 0, $entry->data ); # FIXME support non ref
104 0         0 };
105             }
106             }
107              
108             sub compile_clear {
109 36     36 0 70 my ( $self, $class, @args ) = @_;
110              
111             return sub {
112 0     0   0 my ( $linker, $instance ) = @_;
113              
114 0         0 _clear($instance);
115 36         221 };
116             }
117              
118             sub compile_expand_data {
119 72     72 0 133 my ( $self, $class, @args ) = @_;
120              
121 72 50       276 unless ( $class->can("STORABLE_attach") ) {
122             # normal form, STORABLE_freeze
123             return sub {
124 36     36   78 my ( $self, $instance, $entry ) = @_;
125              
126 36         849 my $data = $entry->data;
127              
128 36 100       207 my ( $reftype, @args ) = ref $data ? @$data : ( substr($data, 0, 1), substr($data, 1) );
129              
130 36 50       172 shift @args if ref $args[0]; # tied
131              
132 36         92 my ( $str, @refs ) = @args;
133              
134 36         53 my @inflated;
135              
136 36         113 foreach my $ref ( @refs ) {
137 68         88 push @inflated, undef;
138              
139 68 50       150 if ( ref $ref ) {
140 68         281 $self->inflate_data($ref, \$inflated[-1]);
141             } else {
142 0         0 $self->queue_ref($ref, \$inflated[-1]);
143             }
144             }
145              
146             $self->queue_finalizer(sub {
147 36         276 $instance->STORABLE_thaw( 0, $str, @inflated );
148 36         317 });
149 72         486 };
150             } else {
151             # esotheric STORABLE_attach form
152 0     0     return sub { };
  0            
153             }
154             }
155              
156             sub _type ($) {
157             my $obj = shift;
158              
159             my $type = reftype($obj);
160              
161             if ( $type eq 'SCALAR' or $type eq 'REF' ) {
162             if ( my $tied = tied $$obj ) {
163             return ( S => $tied );
164             } else {
165             return 'S';
166             }
167             } elsif ( $type eq 'HASH' ) {
168             if ( my $tied = tied %$obj ) {
169             return ( H => $tied );
170             } else {
171             return 'H';
172             }
173             } elsif ( $type eq 'ARRAY' ) {
174             if ( my $tied = tied @$obj ) {
175             return ( A => $tied );
176             } else {
177             return 'A';
178             }
179             } else {
180             croak sprintf "Unexpected object type (%s)", reftype($obj);
181             }
182             }
183              
184             sub _new ($;$) {
185             my ( $type, $tied ) = @_;
186              
187             if ( $type eq 'S' ) {
188             my $ref = \( my $x );
189             tie $x, "To::Object", $tied if ref $tied;
190             return $ref;
191             } elsif ( $type eq 'H' ) {
192             my $ref = {};
193             tie %$ref, "To::Object", $tied if ref $tied;
194             return $ref;
195             } elsif ( $type eq 'A' ) {
196             my $ref = [];
197             tie @$ref, "To::Object", $tied if ref $tied;
198             return $ref;
199             } else {
200             croak sprintf "Unexpected object type (%d)", $type;
201             }
202             }
203              
204             sub _clear ($) {
205             my $obj = shift;
206              
207             my $type = reftype($obj);
208              
209             if ( $type eq 'SCALAR' or $type eq 'REF' ) {
210             undef $$obj;
211             } elsif ( $type eq 'HASH' ) {
212             %$obj = ();
213             } elsif ( $type eq 'ARRAY' ) {
214             @$obj = ();
215             } else {
216             croak sprintf "Unexpected object type (%s)", reftype($obj);
217             }
218             }
219              
220              
221             __PACKAGE__->meta->make_immutable;
222              
223             __PACKAGE__
224              
225             __END__
226              
227             =pod
228              
229             =head1 NAME
230              
231             KiokuDB::TypeMap::Entry::StorableHook - Reuse existing L<Storable> hooks for
232             L<KiokuDB> storage.
233              
234             =head1 SYNOPSIS
235              
236             use KiokuDB::TypeMap::Entry::StorableHook;
237              
238             =head1 DESCRIPTION
239              
240             =cut
241              
242