File Coverage

blib/lib/KiokuDB/TypeMap/Entry/StorableHook.pm
Criterion Covered Total %
statement 2 4 50.0
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 4 6 66.6


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