File Coverage

blib/lib/KiokuDB/Entry.pm
Criterion Covered Total %
statement 117 117 100.0
branch 64 70 91.4
condition 7 9 77.7
subroutine 26 26 100.0
pod 0 7 0.0
total 214 229 93.4


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package KiokuDB::Entry;
4 27     27   51441 use Moose;
  27         722714  
  27         155  
5              
6 27     27   136470 use Moose::Util::TypeConstraints;
  27         51  
  27         225  
7              
8 27     27   44098 use namespace::clean -except => 'meta';
  27         52  
  27         260  
9              
10             with qw(MooseX::Clone);
11              
12             has id => (
13             isa => "Str",
14             is => "ro",
15             writer => "_id",
16             clearer => "clear_id",
17             predicate => "has_id",
18             );
19              
20             has root => (
21             isa => "Bool",
22             is => "rw",
23             lazy_build => 1,
24             );
25              
26             sub _build_root {
27 3741     3741   4724 my $self = shift;
28              
29 3741 100 100     100934 if ( $self->has_id and my $prev = $self->prev ) {
30 842         19485 return $prev->root;
31             } else {
32 2899         64389 return 0;
33             }
34             }
35              
36             has deleted => (
37             isa => "Bool",
38             is => "ro",
39             writer => "_deleted",
40             );
41              
42             has data => (
43             is => "ro",
44             writer => "_data",
45             predicate => "has_data",
46             );
47              
48             has class => (
49             isa => "Str",
50             is => "ro",
51             writer => "_class",
52             predicate => "has_class",
53             );
54              
55             has class_meta => (
56             isa => "HashRef",
57             is => "ro",
58             writer => "_class_meta",
59             predicate => "has_class_meta",
60             );
61              
62             has class_version => (
63             isa => "Str",
64             is => "ro",
65             writer => "_class_version",
66             predicate => "has_class_version",
67             );
68              
69             my @tied = ( map { substr($_, 0, 1) } qw(HASH SCALAR ARRAY GLOB) );
70              
71             has tied => (
72             is => "ro",
73             writer => "_tied",
74             predicate => "has_tied",
75             );
76              
77             has backend_data => (
78             is => "rw",
79             predicate => "has_backend_data",
80             clearer => "clear_backend_data",
81             );
82              
83             has prev => (
84             isa => __PACKAGE__,
85             is => "rw",
86             predicate => "has_prev",
87             clearer => "clear_prev",
88             );
89              
90             sub root_prev {
91 10     10 0 11 my $self = shift;
92              
93 10 100       239 if ( $self->has_prev ) {
94 5         99 return $self->prev->root_prev;
95             } else {
96 5         21 return $self;
97             }
98             }
99              
100             has object => (
101             traits => [qw(NoClone)],
102             is => "rw",
103             weak_ref => 1,
104             predicate => "has_object",
105             clearer => "clear_object",
106             );
107              
108             sub deletion_entry {
109 171     171 0 263 my $self = shift;
110              
111 171 100       4801 ( ref $self )->new(
    50          
112             id => $self->id,
113             prev => $self,
114             deleted => 1,
115             ( $self->has_object ? ( object => $self->object ) : () ),
116             ( $self->has_backend_data ? ( backend_data => $self->backend_data ) : () ),
117             );
118             }
119              
120             sub derive {
121 112     112 0 1446 my ( $self, @args ) = @_;
122              
123 112         566 $self->clone(
124             prev => $self,
125             @args,
126             );
127             }
128              
129             has _references => (
130             traits => [qw(NoClone)],
131             isa => "ArrayRef",
132             is => "ro",
133             lazy_build => 1,
134             );
135              
136             sub _build__references {
137 2089     2089   2121 my $self = shift;
138              
139 27     27   19445 no warnings 'uninitialized';
  27         46  
  27         7185  
140 2089 100       44873 if ( $self->class eq 'KiokuDB::Set::Stored' ) { # FIXME should the typemap somehow handle this?
141 1         3 return [ map { KiokuDB::Reference->new( id => $_ ) } @{ $self->data } ];
  2         47  
  1         21  
142             } else {
143 2088         1585 my @refs;
144              
145 2088         45286 my @queue = $self->data;
146              
147 2088         3665 while ( @queue ) {
148 6480         5370 my $next = pop @queue;
149              
150 6480         6751 my $ref = ref $next;
151 6480 100       14174 if ( $ref eq 'HASH' ) {
    100          
    100          
    100          
152 67         164 push @queue, grep { ref } values %$next;
  296         418  
153             } elsif ( $ref eq 'ARRAY' ) {
154 2209         2990 push @queue, grep { ref } @$next;
  4170         7292  
155             } elsif ( $ref eq 'KiokuDB::Entry' ) {
156 2         5 push @refs, $next->references;
157             } elsif ( $ref eq 'KiokuDB::Reference' ) {
158 4201         7598 push @refs, $next;
159             }
160             }
161              
162 2088         49766 return \@refs;
163             }
164             }
165              
166             sub references {
167 2089     2089 0 1729 my $self = shift;
168              
169 2089         1502 return @{ $self->_references };
  2089         46983  
170             }
171              
172             has _referenced_ids => (
173             traits => [qw(NoClone)],
174             isa => "ArrayRef",
175             is => "ro",
176             lazy_build => 1,
177             );
178              
179             sub _build__referenced_ids {
180 2083     2083   1826 my $self = shift;
181              
182 27     27   139 no warnings 'uninitialized';
  27         50  
  27         2859  
183 2083 100       45582 if ( $self->class eq 'KiokuDB::Set::Stored' ) { # FIXME should the typemap somehow handle this?
184 1         21 return $self->data;
185             } else {
186 2082         3212 return [ map { $_->id } $self->references ];
  4196         93725  
187             }
188             }
189              
190             sub referenced_ids {
191 2085     2085 0 1803 my $self = shift;
192              
193 2085         1636 @{ $self->_referenced_ids };
  2085         47513  
194             }
195              
196 27     27   129 use constant _version => 1;
  27         41  
  27         1833  
197              
198 27     27   119 use constant _root_b => 0x01;
  27         37  
  27         1098  
199 27     27   117 use constant _deleted_b => 0x02;
  27         57  
  27         1070  
200              
201 27     27   117 use constant _tied_shift => 2;
  27         44  
  27         1326  
202 27     27   137 use constant _tied_mask => 0x03 << _tied_shift;
  27         32  
  27         3817  
203              
204             my %tied; @tied{@tied} = ( 1 .. scalar(@tied) );
205              
206             sub _pack {
207 4571     4571   5232 my $self = shift;
208              
209 4571         4455 my $flags = 0;
210              
211 4571 100       118112 $flags |= _root_b if $self->root;
212 4571 100       103818 $flags |= _deleted_b if $self->deleted;
213              
214 4571 100       118220 if ( $self->has_tied ) {
215 31         717 $flags |= $tied{$self->tied} << _tied_shift;
216             }
217              
218 27     27   136 no warnings 'uninitialized';
  27         37  
  27         6696  
219 4571         96117 pack( "C C w/a* w/a*", _version, $flags, $self->id, $self->class );
220             }
221              
222             sub _unpack {
223 11087     11087   11523 my ( $self, $packed ) = @_;
224              
225 11087         36130 my ( $v, $body ) = unpack("C a*", $packed);
226              
227 11087 100       21242 if ( $v == _version ) {
228 11075         29823 my ( $flags, $id, $class, $extra ) = unpack("C w/a w/a a*", $body);
229              
230 11075 50       22445 return $self->_unpack_old($packed) if length($extra);
231              
232 11075 100       310392 $self->_id($id) if length($id);
233              
234 11075 100       142381 $self->_class($class) if length($class);
235              
236 11075         251091 $self->root($flags & _root_b);
237 11075 100       20424 $self->_deleted(1) if $flags & _deleted_b;
238              
239 11075 100       29613 if ( my $tied = ( $flags & _tied_mask ) >> _tied_shift ) {
240 31         879 $self->_tied( $tied[$tied - 1] );
241             }
242             } else {
243 12         35 $self->_unpack_old($packed);
244             }
245             }
246              
247              
248             sub _pack_old {
249 12     12   77 my $self = shift;
250              
251 27     27   135 no warnings 'uninitialized';
  27         73  
  27         9084  
252 12         385 join(",",
253             $self->id,
254             !!$self->root,
255             $self->class,
256             $self->tied,
257             !!$self->deleted,
258             );
259             }
260              
261             sub _unpack_old {
262 12     12   23 my ( $self, $packed ) = @_;
263              
264 12         81 my ( $id, $root, $class, $tied, $deleted ) = split ',', $packed;
265              
266 12 50 66     42 die "bad entry format: $packed" if $root and $root ne '1';
267 12 50 66     48 die "bad entry format: $packed" if $deleted and $deleted ne '1';
268              
269 12 50       422 $self->_id($id) if $id;
270 12 100       72 $self->root(1) if $root;
271 12 100       75 $self->_class($class) if $class;
272 12 100       57 $self->_tied(substr($tied, 0, 1)) if $tied;
273 12 100       74 $self->_deleted(1) if $deleted;
274             }
275              
276             sub STORABLE_freeze {
277 4571     4571 0 33336 my ( $self, $cloning ) = @_;
278              
279             return (
280 4571 100       9982 $self->_pack,
    100          
    100          
    100          
281             [
282             ( $self->has_data ? $self->data : undef ),
283             ( $self->has_backend_data ? $self->backend_data : undef ),
284             ( $self->has_class_meta ? $self->class_meta : undef ),
285             ( $self->has_class_version ? $self->class_version : undef ),
286             ],
287             );
288             }
289              
290             sub STORABLE_thaw {
291 11075     11075 0 43636 my ( $self, $cloning, $attrs, $refs ) = @_;
292              
293 11075         20250 $self->_unpack($attrs);
294              
295 11075 50       20731 if ( $refs ) {
296 11075         14435 my ( $data, $backend_data, $meta, $version ) = @$refs;
297 11075 100       293809 $self->_data($data) if defined $data;
298 11075 100       20156 $self->backend_data($backend_data) if ref $backend_data;
299 11075 100       16024 $self->_class_meta($meta) if ref $meta;
300 11075 100       104140 $self->_class_version($version) if defined $version;
301             }
302             }
303              
304             __PACKAGE__->meta->make_immutable;
305              
306             __PACKAGE__
307              
308             __END__
309              
310             =pod
311              
312             =head1 NAME
313              
314             KiokuDB::Entry - An entry in the database
315              
316             =head1 SYNOPSIS
317              
318             KiokuDB::Entry->new(
319             id => ...,
320             data => ...
321             );
322              
323             =head1 DESCRIPTION
324              
325             This object provides the meta data for a single storage entry.
326              
327             =head1 ATTRIBUTES
328              
329             =over 4
330              
331             =item id
332              
333             The UUID for the entry.
334              
335             If there is no ID then the entry is intrinsic.
336              
337             =item root
338              
339             Whether or not this is a member of the root set (not subject to garbage
340             collection, because storage was explicitly requested).
341              
342             =item data
343              
344             A simplified data structure modeling this object/reference. This is a tree, not
345             a graph, and has no shared data (JSON compliant). All references are symbolic,
346             using a L<KiokuDB::Reference> object with UIDs as the
347             address space.
348              
349             =item class
350              
351             If the entry is blessed, this contains the class of that object.
352              
353             In the future this might be a complex structure for anonymous classes, e.g. the
354             class and the runtime roles.
355              
356             =item class_meta
357              
358             Optional information such as runtime roles to be applied to the object is
359             stored in this hashref.
360              
361             =item tied
362              
363             One of C<HASH>, C<ARRAY>, C<SCALAR> or C<GLOB>.
364              
365             C<data> is assumed to be a reference or an intrinsic entry for the object
366             driving the tied structure (e.g. the C<tied(%hash)>).
367              
368             =item prev
369              
370             Contains a link to a L<KiokuDB::Entry> objects that precedes this one.
371              
372             The last entry that was loaded from the store, or successfully written to the
373             store for a given UUID is kept in the live object set.
374              
375             The collapser creates transient Entry objects, which if written to the store
376             successfully replace the previous one.
377              
378             =item backend_data
379              
380             Backends can use this to store additional meta data as they see fit.
381              
382             For instance, this is used in the CouchDB backend to track entry revisions for
383             the opportunistic locking, and in L<KiokuDB::Backend::BDB::GIN> to to store
384             extracted keys.
385              
386             =item deleted
387              
388             Used for marking entries for deletion.
389              
390             Deletion entries can be generated using the C<deletion_entry> method, which
391             creates a new derived entry with no data but retaining the ID.
392              
393             =back
394              
395             =cut