File Coverage

blib/lib/Data/Rebuilder.pm
Criterion Covered Total %
statement 128 154 83.1
branch 22 36 61.1
condition 7 16 43.7
subroutine 27 32 84.3
pod 12 12 100.0
total 196 250 78.4


line stmt bran cond sub pod time code
1              
2 6     6   85974 use strict;
  6         12  
  6         202  
3 6     6   32 use warnings;
  6         9  
  6         373  
4             package Data::Rebuilder;
5              
6             =head1 NAME
7              
8             Data::Rebuilder - Builds an object rebuilder.
9              
10             =head1 VERSION
11              
12             Version 0.01
13              
14             =cut
15              
16             our $VERSION = '0.01';
17              
18             =head1 SYNOPSIS
19              
20             ###
21             ### freeze composite ...
22             ###
23            
24             my $builder = Data::Rebuilder->new;
25             $builder->parameterize( driver => $driver );
26             $builder->parameterize( user => $user );
27             my $icy_compsite = $builder->build_rebulder( $composite );
28            
29             ###
30             ### restore composite with current context ...
31             ###
32            
33             my $builder = eval $icy_composite;
34             my $melted_composite = $builder->( driver => $driver,
35             user => $user );
36              
37             =head1 DESCRIPTION
38              
39             This approach is like to C approach. Moreover,
40             an output of this module is not easy to read.
41              
42             However this solution can rebuild tied values, weak references,
43             and closures.
44             In addition, this solution can parameterize
45             arbitrary nodes of composite. Users can give new objects as
46             arguments of the subroutine which is result.
47              
48             =cut
49              
50 6     6   35 use B;
  6         14  
  6         332  
51 6     6   34 use Scalar::Util qw( isweak refaddr blessed looks_like_number);
  6         10  
  6         801  
52 6     6   6031 use UNIVERSAL qw(isa can);
  6         79  
  6         32  
53 6     6   3033 use Carp;
  6         11  
  6         493  
54 6     6   5385 use Sub::Name;
  6         4791  
  6         332  
55 6     6   9590 use Path::Class;
  6         532377  
  6         441  
56 6     6   6767 use Lexical::Alias;
  6         5107  
  6         377  
57 6     6   94 use B::Deparse;
  6         11  
  6         88  
58 6     6   5010 use PadWalker;
  6         4925  
  6         287  
59 6     6   6152 use Data::Polymorph;
  6         19527  
  6         83  
60              
61              
62             =head1 STATIC METHODS
63              
64             =over 4
65              
66             =item safe_require
67              
68             safe_require ( $0 ); # does not load
69             safe_require ( Path::Class::file( $0 )->absolute ); # does not load
70             safe_require ( 'path/to/module.pm' ); # loads
71             safe_require ( '/absolute/path/to/module.pm'); # does not load
72              
73             Loads module safery.
74              
75             =cut
76              
77              
78             {
79             my %loaded = ();
80             sub safe_require ($){
81 11     11 1 26233 my $lib = shift;
82 11         71 my $libabs = file($lib)->absolute;
83 11 100       1494 return if $loaded{$libabs};
84 5 50       166 return if $libabs eq file($0)->absolute;
85 5         1389 $loaded{$libabs} = 1;
86 5 50       242 require $lib unless grep{ $libabs eq file($_)->absolute } values %INC;
  496         112945  
87             }
88             }
89              
90             sub _indent ($) {
91 168     168   272 local $_ = shift;
92 168         936 s/\n/\n /mg;
93 168         1172 $_;
94             }
95              
96             { ############################################################
97             package Data::Rebuilder::B::Deparse;
98             our @ISA = qw( B::Deparse );
99              
100             sub coderef2textX{
101 4     4   7 my $self = shift;
102 4         12147 my $code = $self->coderef2text( @_ );
103             (
104 4         28 globals => [ keys %{$self->{' globals '}} ],
  4         32  
105 4         14 stashes => [ keys %{$self->{' stashes '}} ],
106             code => $code
107             );
108             }
109              
110             {
111             my %globalnames =
112             map (($_ => 1), qw(SIG STDIN STDOUT STDERR INC ENV ARGV ARGVOUT _));
113              
114             sub gv_name {
115 0     0   0 my $self = shift;
116 0         0 my $gv = shift;
117 0 0       0 Carp::confess() unless ref($gv) eq "B::GV";
118 0         0 my $stash = $gv->STASH->NAME;
119 0         0 $self->{' stashes '}->{$stash} = 1;
120 0         0 my $name = $gv->SAFENAME;
121 0 0 0     0 if ($stash eq 'main' && $name =~ /^::/) {
    0 0        
122 0         0 $stash = '::';
123             }
124             elsif (($stash eq 'main' && $globalnames{$name})
125             # or ($stash eq $self->{'curstash'} && !$globalnames{$name}
126             # && ($stash eq 'main' || $name !~ /::/))
127             # or $name =~ /^[^A-Za-z_:]/
128             )
129             {
130 0         0 $stash = "";
131             } else {
132 0         0 $stash = $stash . "::";
133             }
134 0 0       0 if ($name =~ /^(\^..|{)/) {
135 0         0 $name = "{$name}"; # ${^WARNING_BITS}, etc and ${
136             }
137 0         0 return $stash . $name;
138             }
139              
140             sub stash_variable{
141 0     0   0 my $self = shift;
142 0         0 my $ret = $self->SUPER::stash_variable(@_);
143 0         0 my $name = $ret;
144 0         0 $name =~ s/^\W//;
145 0 0       0 $self->{' globals '}->{$ret} = 1 unless $globalnames{$name};
146 0         0 $ret;
147             }
148             }
149             } ############################################################
150              
151              
152             {
153             my @template =
154             ([poly => sub{
155             my ($self) = @_;
156             my $poly = Data::Polymorph->new;
157             my %blank =
158             (
159             Undef => sub{ 'do{my $a;\$a}' },
160             HashRef => sub{ "{}" },
161             ArrayRef => sub{ "[]" },
162             ScalarRef => sub{ 'do{my $a;\$a}' },
163             GlobRef => sub{ $self->poly->apply($_[0] => 'freeze') },
164             Glob => sub{ $self->poly->apply($_[0] => 'freeze') },
165             Str => sub{ $_[0]. "" },
166             Num => sub{ $_[0]. "" },
167             );
168              
169             my %tier =
170             (
171             HashRef => sub{
172             my ( $obj, $objexpr ) = @_;
173             (sprintf('%%{%s}', $objexpr), "TIEHASH")
174             },
175              
176             ArrayRef => sub{
177             my ( $obj, $objexpr ) = @_;
178             (sprintf('@{%s}', $objexpr), "TIEARRAY")
179             },
180              
181             ScalarRef => sub{
182             my ( $obj, $objexpr ) = @_;
183             (sprintf('${%s}', $objexpr),"TIESCALAR")
184             },
185              
186             GlobRef => sub{
187             my ( $obj, $objexpr ) = @_;
188             (sprintf('*{%s}', $objexpr),"TIEHANDLE")
189             },
190              
191             Glob => sub{
192             my ( $obj, $objexpr ) = @_;
193             (sprintf('*{%s}', $objexpr),"TIEHANDLE");
194             },
195              
196             Str => sub{
197             my ( $obj, $objexpr ) = @_;
198             ( $objexpr , "TIESCALAR" );
199             },
200              
201             Num => sub{
202             my ( $obj, $objexpr ) = @_;
203             ( $objexpr , "TIESCALAR" );
204             },
205              
206             );
207              
208             my %tied =
209             (
210             HashRef => sub{ tied %{$_[0]} },
211             ArrayRef => sub{ tied @{$_[0]} },
212             ScalarRef => sub{ tied ${$_[0]} },
213             GlobRef => sub{ tied *{$_[0]} },
214             Glob => sub{ tied *{$_[0]} },
215             Str => sub{ tied $_[0] },
216             Num => sub{ tied $_[0] },
217             Any => sub{ undef },
218             );
219              
220             my %module_loader =
221             (
222             Any => sub{()},
223              
224             UNIVERSAL => sub{
225             my $obj = shift;
226             my $class = blessed $obj || $obj;
227             my $pm = $class;
228             $pm =~ s#::#/#g;
229             $pm =~ s#$#.pm#;
230             return "require $class;" if exists $INC{$pm};
231              
232 6     6   8282 my $stashglob = do{no strict 'refs'; *{"${class}::"}};
  6         20  
  6         27976  
233             my %stash = %{$stashglob};
234             my %files;
235             foreach my $glob ( values %stash ) {
236             my $code = *{$glob}{CODE};
237             next unless $code;
238             my $b = B::svref_2object($code);
239             next if $b->XSUB;
240             my $file = $b->FILE;
241             $files{$file} = 1;
242             }
243             map( sprintf( '%s::safe_require %s;',
244             __PACKAGE__,
245             $self->freeze(file($_)->absolute->stringify)),
246             keys %files );
247             },
248              
249             Regexp => sub{
250             ( exists( $INC{'Regexp.pm'} )
251             ? ("require Regexp;")
252             : () )
253             },
254             );
255              
256              
257             my %freezer =
258             (
259             ###
260             Any => sub{ confess "caught unsupported type." },
261              
262             ###
263             Undef => sub{ 'undef' },
264              
265             ###
266             'Str' => sub{ B::perlstring( $_[0] ) },
267              
268             ###
269             'Num' => sub{ $_[0] },
270              
271             ###
272             'Glob' => sub{
273             my $obj = shift;
274             my $name = "" . $obj;
275             return "$name" unless $name =~ /^\*Symbol::GEN/;
276             join("\n",
277             'do{',
278             ' # GrobRef',
279             ' require Symbol;',
280             ' my $__tmp = Symbol::gensym();',
281             ( map {
282             ( *{$obj}{$_}
283             ? ( sprintf(' *{$__tmp} = %s;',
284             $self->freeze(*{$obj}{$_})) )
285             : () )
286             } qw( SCALAR
287             ARRAY
288             HASH
289             CODE )),
290             ' *$__tmp;',
291             '}' );
292             },
293              
294             ###
295             'ScalarRef' => sub{
296             my $obj = shift;
297             join( "\n",
298             'do{',
299             ' #ScalarRef',
300             ' my $__tmp = '.$self->freeze($$obj).';',
301             ' \\$__tmp;',
302             '}' );
303             },
304              
305             #################################
306             'CodeRef' => sub{
307             my $cv = shift;
308             my $target = shift || $cv;
309             my $var = $self->ref_to_var($target);
310              
311             my $dp = ( $self->{_deparse}
312             ||= (__PACKAGE__."::B::Deparse")->new );
313             my $closed = PadWalker::closed_over( $cv );
314             my $b = B::svref_2object($cv);
315             my $name = $b->GV->NAME;
316             my @vars = ();
317              
318             foreach my $key (keys %$closed) {
319              
320             my $val = $closed->{$key};
321              
322             if( $self->poly->type($val) eq 'RefRef' &&
323             $self->_is_cycled($$val)) {
324             push @vars,
325             sprintf(' my %s = undef; #cycled RefRef', $key);
326             my $lazy = $self->_lazy->{refaddr $$val} ||= [];
327              
328             push
329             (@$lazy,
330             'require PadWalker;',
331             sprintf('${PadWalker::closed_over(%s)->{%s}} = %s;',
332             $var,
333             $self->freeze($key),
334             $self->freeze($$val))
335             );
336             }
337             else {
338             push( @vars,
339             sprintf ( " my \%s = undef;\n".
340             ' Lexical::Alias::alias_r( %s , \%s );',
341             $key,
342             $self->freeze($val),
343             $key ) );
344             }
345              
346             }
347              
348             my %info = $dp->coderef2textX($cv);
349              
350             foreach my $stash ( $b->STASH->NAME , @{$info{stashes}} ){
351             $self->_stashes->{$stash} = 1;
352             }
353              
354             join( "\n",
355             "do{",
356             ' # CodeRef',
357             (map{ sprintf(' %s = %s;',$_,$_) }@{$info{globals}}),
358             ( @vars ? ' require Lexical::Alias;' : () ),
359             @vars,
360             sprintf(' sub %s', _indent $info{code}),
361             "}",
362             );
363             },
364              
365             #################################
366             'ArrayRef' => sub{
367              
368             my $ref = shift;
369             my $target = shift || $ref;
370             my $var = $self->ref_to_var($target);
371              
372             my @body = ();
373             my @tied = ();
374             my @weak = ();
375             local $_;
376              
377             for( my $i = 0; $i < @{$ref} ; $i++ ) {
378             my $v = $ref->[$i];
379             my $tied = tied ( $ref->[$i] );
380             push @body, sprintf(' # %s', refaddr( \$ref->[$i] ));
381             if( $tied ){
382              
383             push @body, " undef,";
384             push @tied , [$i => $tied];
385              
386             }
387             elsif( $self->_is_cycled($v) ) {
388              
389             push @body, " undef,";
390             my $lazy = $self->_lazy->{ refaddr $v } ||= [];
391             push( @$lazy ,
392             sprintf('%s->[%s] = %s;',
393             $var, $i, $self->freeze($v)));
394             push( @$lazy ,
395             sprintf('Scalar::Util::weaken(%s->[%s]);',
396             $var, $i))
397             if isweak($ref->[$i]);
398              
399             }
400             elsif( $self->poly->type($v) eq 'RefRef' and
401             $self->_is_cycled($$v)){
402             push @body, " undef, #cycled RefRef ";
403             my $lazy = $self->_lazy->{refaddr $$v} ||= [];
404             push @{$lazy}, sprintf('%s->[%s] = %s;',
405             $var,
406             $i,
407             $self->poly->apply( $v => 'freeze'));
408             push( @$lazy ,
409             sprintf('Scalar::Util::weaken(%s->[%s]);',
410             $var, $i))
411             if isweak($ref->[$i]);
412             }
413             else {
414              
415             push @body , " ". $self->freeze($v).",";
416             push @weak , $i , if isweak( $ref->[$i] );
417              
418             }
419             }
420              
421             join
422             (
423             "\n" ,
424             "do{ ",
425             ' # ArrayRef',
426             " my \$__tmp = [",
427             @body ,
428             " ];",
429             " "._indent( join "\n",
430             map{ $self->tier('$__tmp->['.$_->[0].']',
431             'TIESCALAR',
432             $_->[1]) } @tied ),
433             " "._indent( join "\n",
434             map{ sprintf(' Scalar::Util::weaken('.
435             ' $__tmp->[%s] );' ,
436             $_) } @weak ),
437             ' $__tmp;',
438             "}"
439             );
440             },
441              
442             #################################
443             'HashRef' => sub{
444             my $ref = shift;
445             my $target = shift || $ref;
446             my $var = $self->ref_to_var($target);
447             my @body = ();
448             my @tied = ();
449             my @weak = ();
450              
451             foreach my $key ( sort keys %{$ref} ){
452             my $v = $ref->{$key};
453             my $tied = tied ( $ref->{$key} );
454             if( $tied ){
455             push @body ,
456             sprintf(' %s => undef,', $self->freeze($key)),
457             push @tied , [$key => $tied];
458             }
459             elsif( $self->_is_cycled($v) ) {
460              
461             push @body ,
462             sprintf(' %s => undef, # cycled', $self->freeze($key));
463              
464             my $lazy = $self->_lazy->{ refaddr $v } ||= [];
465              
466             push( @$lazy , sprintf('%s->{%s} = %s;',
467             $var,
468             $self->freeze($key),
469             $self->freeze($v)));
470              
471             push( @$lazy ,
472             sprintf('Scalar::Util::weaken(%s->{%s});',
473             $var,
474             $self->freeze($key)
475             )) if isweak($ref->{$key});
476              
477             }
478             elsif( $self->poly->type($v) eq 'RefRef' and
479             $self->_is_cycled($$v)){
480              
481             push @body, sprintf(' %s => undef, # cycled RefRef',
482             $self->freeze($key));
483              
484             my $lazy = $self->_lazy->{refaddr $$v} ||= [];
485              
486             push @{$lazy}, sprintf('%s->{%s} = %s;',
487             $var,
488             $self->freeze($key),
489             $self->freeze($v));
490              
491             push( @$lazy ,
492             sprintf('Scalar::Util::weaken(%s->{%s});',
493             $var,
494             $self->freeze($key),
495             )) if isweak($ref->{$key});
496              
497             }
498             else {
499             push @body ,
500             sprintf(' %s => %s,',
501             $self->freeze($key), $self->freeze($v));
502             push @weak , $key, if isweak( $ref->{$key} );
503             }
504             }
505              
506             join
507             (
508             "\n" ,
509             "do{ ",
510             ' # HashRef',
511             " my \$__tmp = {",
512             @body ,
513             " };",
514             ( map{ $self->tier('$__tmp->{'.$self->freeze($_->[0]).'}',
515             'TIESCALAR',
516             $_->[1]) } @tied ),
517             ( map{ sprintf(' Scalar::Util::weaken( \ $__tmp->{%s} );' ,
518             $self->freeze($_)) }
519             @weak ),
520             ' $__tmp;',
521             "}"
522             );
523             },
524              
525             #################################
526             'GlobRef' => sub{
527             my $glob = shift;
528             my $target = shift;
529             my $var = $self->ref_to_var($target);
530             my $name = "".$$glob;
531              
532             return '\\ '.$name
533             if( $name =~ /\*main::(STD(?:IN|OUT|ERR)|ARGV)/ &&
534             refaddr( $glob ) == refaddr( \$main::{$1} ) );
535              
536             my @slots = ();
537             foreach my $slot ( qw(SCALAR HASH ARRAY CODE)) {
538              
539             next unless my $ref = *{$glob}{$slot};
540              
541             if( $self->poly->type($slot) eq 'RefRef' &&
542             $self->_is_cycled($$slot) ) {
543             my $lazy = ($self->_lazy->{refaddr $$slot} ||= []);
544             push @$lazy,
545             sprintf(' *{%s} = %s;',
546             $var,
547             $self->freeze(*{$glob}{$slot}) );
548             }
549             else {
550             push @slots,
551             sprintf(' *{$__tmp} = %s;',
552             $self->freeze(*{$glob}{$slot}) );
553             }
554              
555             }
556             join ("\n",
557             'do {',
558             ' require Symbol;',
559             sprintf(' my $__tmp = Symbol::gensym();', $name),
560             @slots,
561             ' $__tmp;',
562             '}',
563             );
564             },
565              
566             ###
567             'RefRef' => sub{
568             my $ref = shift;
569             my $target = shift || $ref;
570             "\\ ". $self->freeze( ${$ref} , ${$target} );
571             },
572              
573             ###
574             UNIVERSAL => sub {
575             my $obj = shift;
576             my $target = shift || $obj;
577             $self->_stashes->{blessed $obj} = 1;
578             join
579             (
580             "\n",
581             'do{',
582             sprintf(" bless(\%s,\n \%s)",
583             _indent( $poly->super($obj => 'freeze' , $target) ),
584             $self->freeze(blessed $obj)),
585             '}'
586             );
587             },
588              
589             ###
590             Regexp => sub {
591             my $obj = shift;
592             my $target = shift || $obj;
593             join( "\n",
594             "do{",
595             " ". _indent( $self->module_loader('Regexp') ),
596             sprintf('my $__tmp = %s ;', $self->freeze("". $obj)),
597             "}");
598             },
599             );
600              
601             my %pre_freeze =
602             (
603             Any => sub{
604             ()
605             },
606              
607             Ref => sub{
608             my $ref = shift;
609             my $target = shift || $ref;
610             $self->_dumped->{ refaddr $target } = $self->ref_to_var($target);
611             ()
612             },
613              
614             ArrayRef => sub{
615             my $ref = shift;
616             my $target = shift || $ref;
617             my $var = $self->ref_to_var($target);
618             $self->poly->super($ref => 'pre_freeze' , $target);
619             for( my $i = 0; $i < @$ref; $i++ ) {
620             $self->_dumped->{ refaddr( \ $ref->[$i] ) } =
621             sprintf('\ %s->[%s]', $var, $i);
622             }
623             ()
624             },
625              
626             HashRef => sub{
627             my $ref = shift;
628             my $target = shift || $ref;
629             my $var = $self->ref_to_var( $target );
630             $self->poly->super($ref => 'pre_freeze' , $target);
631             foreach my $key ( keys %$ref ) {
632             $self->_dumped->{ refaddr( \ $ref->{ $key } ) } =
633             sprintf('\ %s->{%s}', $var, $self->freeze($key));
634             }
635             ()
636             },
637             );
638              
639             my %post_freeze =
640             (
641             Any => sub{ () },
642              
643             Ref => sub{
644             my $ref = shift;
645             my $target = shift || $ref;
646             my $addr = refaddr $target;
647             my $lazy = delete($self->_lazy->{$addr}) || [];
648             $self->_complete->{$addr} = 1;
649             @$lazy;
650             },
651              
652             ArrayRef => sub{
653             my $ref = shift;
654             my $target = shift || $ref;
655             (
656             $self->poly->super($ref => 'post_freeze', $target) ,
657             map { $self->poly->apply( \$ref->[$_] => 'post_freeze') }
658             ( 0 ... $#{$ref} )
659             );
660             },
661              
662             HashRef => sub{
663             my $ref = shift;
664             my $target = shift || $ref;
665             (
666             $self->poly->super($ref => 'post_freeze' , $target) ,
667             ( map { $self->poly->apply( \$ref->{$_} => 'post_freeze') }
668             sort keys %$ref )
669             );
670             },
671             );
672              
673             my %sleep =
674             (
675             Any => sub{ $_[0] },
676             __PACKAGE__ , sub{
677             my %sleepy = %{$_[0]};
678             delete $sleepy{$_} foreach qw( _stashes
679             _deparse
680             _result
681             _params
682             _complete
683             _lazy );
684             bless \%sleepy, blessed $_[0];
685             },
686             );
687              
688              
689             foreach ( [tied => \%tied],
690             [tier => \%tier],
691             [blank => \%blank],
692             [module_loader => \%module_loader],
693             [pre_freeze => \%pre_freeze],
694             [freeze => \%freezer],
695             [sleep => \%sleep],
696             [post_freeze => \%post_freeze]) {
697             my ( $meth, $dic ) = @$_;
698             while( my ($class, $sub) = each %{$dic} ) {
699             $poly->define( $class => $meth =>
700             ( subname "$class->$meth" => $sub ) );
701             }
702             }
703              
704             $poly;
705             }],
706              
707             [_stashes => sub{ {} }],
708             [_deparse => sub{ undef }],
709             [_result => sub{ [] }],
710             [_params => sub{ {} }],
711             [_dumped => sub{ {} }],
712             [_complete => sub{ {} }],
713             [_lazy => sub{ {} }],
714             );
715              
716             sub{
717             my $caller = caller;
718             foreach ( @_ ) {
719             my $name = $_;
720 6     6   83 my $glob = do{no strict 'refs'; \*{"${caller}::$name"}};
  6         15  
  6         10262  
721             *{$glob} = sub ($;$){
722 1348     1348   2168 my $self = shift;
723 1348 100       8224 return $self->{$name} unless @_;
724 115         632 $self->{$name} = shift;
725             };
726             }
727             }->( map { $_->[0]} @template );
728              
729             =item C
730              
731             my $builder = Data::Rebuilder->new;
732              
733             Creates and returns new object.
734             It does not receives any arguments.
735              
736             =back
737              
738             =head1 ATTRIBUTES
739              
740             =over 4
741              
742             =item C
743              
744             Contains C instance.
745              
746             =back
747              
748             =cut
749              
750             sub new {
751 22     22 1 48494 my ($self) = @_;
752 22   33     244 $self = bless {},( blessed $self ) || $self;
753 22         67 foreach my $slot ( @template ) {
754 176         462 $self->{$slot->[0]} = $slot->[1]->($self);
755             }
756 22         89 $self;
757             }
758              
759             }
760              
761              
762             =head1 DYNAMIC METHODS
763              
764             =over 4
765              
766             =item C
767              
768             my $var = $builder->ref_to_var( $ref ); # returns $__17898432__
769              
770             Makes a reference to a variable name.
771              
772             =cut
773              
774 208   50 208 1 2789 sub ref_to_var{ sprintf( '$__%d__', refaddr( $_[1] ) || '') }
775              
776             sub _is_cycled {
777 57     57   524 my ( $self, $v ) = @_;
778 57 100       310 return 0 unless ref $v;
779 41         96 my $addr = refaddr $v;
780 41 100       80 return 0 if $self->_complete->{ $addr };
781 39         88 exists $self->_dumped->{ $addr };
782             }
783              
784             =item C
785              
786             $builder->parameterize( a_object => $a_object );
787              
788             Register an object as a parameter of rebuilders.
789              
790             =cut
791              
792             sub parameterize {
793 2     2 1 8474 my ( $self, $key, $rv ) = @_;
794 2         11 $self->_params->{ $key } = $rv;
795             }
796              
797             =item C
798              
799             $builder->register_freezer( 'Target::Class' => sub{ ... } );
800              
801             same as
802              
803             $builder->poly->define( 'Target::Class' => freeze => sub{ ... } );
804              
805             Registers freezer method for the types (or classes).
806              
807             Customization of this approach is not easy way. As other way, you can customize
808             by C and C.
809              
810             =cut
811              
812             sub register_freezer {
813 0     0 1 0 my ($self, $class, $code) = @_;
814 0         0 $self->poly->define( $class => freeze => $code );
815             }
816              
817             =item C
818              
819             $builder->register_sleep( 'Target::Class' => sub{
820             my $self = shift;
821             return ( { foo => $self->foo,
822             bar => $self->bar,
823             bazz => $self->bazz } , sub{
824             my $obj = shift;
825             bless $obj , blessed $self;
826             $obj->init;
827             $obj;
828             } )
829             } );
830              
831             Registers "sleep" method for the class.
832              
833             You can drop some properties that is not necessary
834             for the serialization by these methods.
835              
836             The "sleep" method returns an object and an optional subroutine reference.
837             They are a information for serializer and a restructuring procedure for
838             the information.
839             So , when rebuilding the object , a rebuilder uses these informations.
840              
841             =cut
842              
843             sub register_sleep {
844 0     0 1 0 my ($self, $class, $code) = @_;
845 0         0 $self->poly->define( $class => sleep => $code );
846             }
847              
848             =item C
849              
850             $builder->register_module_loader( 'Foo::Class' => sub{ 'require Foo;' } );
851              
852             Registers a module loader builder.
853             The default method of this searches files from any CVs
854             in the symbol table of the class, and builds loading code
855             with these information.
856              
857             =cut
858              
859             sub register_module_loader {
860 0     0 1 0 my ($self, $class, $code) = @_;
861 0         0 $self->poly->define( $class => module_loader => $code );
862             }
863              
864             =item C
865              
866             # returns 'require Symbol;'
867             $exp = $dumper->module_loader('Symbol');
868            
869             # returns 'B::Rebuilder::safe_require "/path/to/your/UNIVERSAL.pm"'
870             $exp = $dumper->module_loader('UNIVERSAL');
871              
872             Returns an expression which reads module for the given package name.
873              
874             =cut
875              
876             sub module_loader {
877 11     11 1 23 my ($self, $class) = @_;
878 11         26 my $meth = $self->poly->class_method( $class, 'module_loader' );
879 11 50       255 join("\n", $meth ? $meth->($class) : ());
880             }
881              
882             =item C
883              
884             $exp = $dumper->blank( { foo => 'bar' } ); # returns '{}'
885             $exp = $dumper->blank( [ foo => 'bar' ] ); # returns '[]'
886             $exp = $dumper->blank( FileHandle->new ); # returns 'Symbol::gensym()'
887              
888             A return value of this method is for tiers.
889              
890             =cut
891              
892             sub blank {
893 4     4 1 6 my ( $self, $val ) = @_;
894 4         9 $self->poly->apply( $val => 'blank' );
895             }
896              
897             =item C
898              
899             $exp = $builder->tier( '$foo', 'TIEHANDLE', $obj );
900              
901             Returns a expression which ties variable with the tied object.
902              
903             =cut
904              
905             sub tier {
906 4     4 1 8 my ( $self , $varexpr, $tier, $tied ) = @_;
907 4         15 my $pkg = blessed $tied;
908 4         27 join ("\n",
909             sprintf('do{'),
910             sprintf(' no warnings;'),
911             sprintf(' my %%old = ();'),
912             sprintf(' foreach my $s (qw(SCALAR ARRAY HASH CODE)){'),
913             sprintf(' $old{$s} = *%s::%s{$s};', $pkg , $tier),
914             sprintf(' }'),
915             sprintf(' *%s::%s = sub{ %s }; ', $pkg, $tier, $self->freeze($tied)),
916             sprintf(' tie %s , %s;' , $varexpr, $self->freeze($pkg)),
917             sprintf(' delete $%s::{%s};', $pkg, $tier),
918             sprintf(' foreach my $s (qw(SCALAR ARRAY HASH CODE)){'),
919             sprintf(' *%s::%s = $old{$s} if defined $old{$s};', $pkg, $tier),
920             sprintf(' }'),
921             sprintf('};'),
922             );
923             }
924              
925             =item C
926              
927             my $icy = $dumper->freeze( $obj );
928              
929             Makes Perl source code which builds given object.
930             This method should not be used from applications, because
931             it modifies the objects state. This method should be used from extensions.
932              
933             =cut
934              
935             sub freeze {
936              
937 191     191 1 8977 my ( $self, $val ) = @_;
938              
939 191 100       1209 return $self->poly->apply( $val => freeze => ) unless ref $val;
940              
941 67         180 my $addr = refaddr( $val );
942              
943 67 100       124 return $self->_dumped->{ $addr } if exists $self->_dumped->{ $addr };
944              
945 48         126 my ($sleep, $rebuilder) = $self->poly->apply( $val => 'sleep' );
946              
947 48         188 my $var = $self->ref_to_var( $val );
948              
949 48         121 $self->poly->apply( $sleep => 'pre_freeze' => $val );
950              
951 48 100 100     118 if( my $tied = $self->poly->apply( $val => 'tied' ) ){
    100          
952              
953 4         12 my $var = $self->ref_to_var( $val );
954              
955 4         4 push @{$self->_result},
  4         10  
956             join("\n",
957             sprintf('my %s = %s;', $var, $self->blank($val)),
958             $self->tier( $self->poly->apply( $val => 'tier', $var ), $tied ));
959              
960             }
961             elsif( $self->poly->type($sleep) eq 'RefRef' and
962             $self->_is_cycled($$sleep)){
963              
964 4   100     11 my $lazy = $self->_lazy->{refaddr $$sleep} ||= [];
965 4         9 push @{$lazy}, sprintf('%s = %s;',
  4         23  
966             $var, $self->poly->apply( $sleep => 'freeze' ,
967             $val ));
968 4         11 push @{$self->_result}, sprintf('my %s = undef;', $var);
  4         12  
969             }
970             else {
971 40         1211 push @{$self->_result},
  40         97  
972             sprintf( 'my %s = %s;',
973             $self->ref_to_var( $val ) ,
974             _indent( $self->poly->apply( $sleep => 'freeze', $val ) ));
975             }
976              
977 48         137 push @{$self->_result}, $self->poly->apply( $sleep => 'post_freeze', $val );
  48         600  
978 48 50       142 push ( @{$self->_result},
  0         0  
979             sprintf('%s->(%s);', $self->freeze($rebuilder), $var) )
980             if $rebuilder;
981              
982 48         188 $var;
983              
984             }
985              
986             =item C
987              
988             my $icy = $dumper->build_rebulder( $obj );
989              
990             Builds Perl source code which is object rebuilder subroutine.
991              
992             =cut
993              
994             sub rebuilder {
995              
996 24     24 1 1973 my ($self, $rv) = @_;
997 24 100       111 return sprintf('sub{%s}', $self->freeze($rv)) unless ref $rv;
998 23         49 my @checker = ();
999 23         63 my @result = ();
1000 23         53 my $_complete = {};
1001 23         40 my $_dumped = {};
1002 23         90 my $_params = $self->_params;
1003              
1004 23         90 foreach my $key (keys %$_params) {
1005 2         9 my $dkey = $self->freeze($key);
1006 2         11 my $slot = sprintf('$__ARGS__{%s}', $dkey);
1007 2         11 my $addr = refaddr($_params->{$key});
1008 2         8 $_dumped->{ $addr } = $slot;
1009 2         5 $_complete->{ $addr } = 1;
1010 2         54 push (
1011             @result ,
1012             sprintf( 'my %s = %s;',
1013             $self->ref_to_var($_params->{$key}),
1014             $slot )
1015             );
1016 2         21 push ( @checker ,
1017              
1018             sprintf('Carp::confess %s." is not specified"'."\n".
1019             ' unless exists %s;',
1020             $dkey, $slot),
1021              
1022             sprintf('Carp::confess %s." is not a reference"'."\n".
1023             ' unless ref %s;',
1024             $dkey, $slot) );
1025             }
1026              
1027 23         87 $self->_stashes({});
1028 23         76 $self->_dumped( $_dumped );
1029 23         96 $self->_result( \@result );
1030 23         73 $self->_complete( $_complete );
1031 23         78 $self->_lazy( {} );
1032              
1033 23         82 my $var = $self->freeze($rv);
1034              
1035 11         45 return join (
1036             "\n",
1037             'do{ ',
1038             ' require '.__PACKAGE__.';',
1039             ( map{
1040 23         53 " ".$self->module_loader($_)
1041 23         59 }(keys %{$self->_stashes})),
1042             ' my $RETVAL = sub (%){',
1043             ' require Scalar::Util; ',
1044             " require Carp;",
1045             ' my %__ARGS__ = @_;',
1046             " "._indent(_indent(join "\n",@checker)),
1047 23         60 " ". _indent(_indent(join "\n", @{ $self->_result })),
1048             " $var;",
1049             ' };',
1050             ' $RETVAL',
1051             '}'
1052             );
1053             }
1054              
1055              
1056             1; # End of Data::Rebuilder
1057              
1058             __END__