Data-Rebuilder

 view release on metacpan or  search on metacpan

lib/Data/Rebuilder.pm  view on Meta::CPAN


use strict;
use warnings;
package Data::Rebuilder;

=head1 NAME

Data::Rebuilder - Builds an object rebuilder.

=head1 VERSION

Version 0.01

=cut

our $VERSION = '0.01';

=head1 SYNOPSIS

  ###
  ### freeze composite ...
  ###
  
  my $builder = Data::Rebuilder->new;
  $builder->parameterize( driver => $driver );
  $builder->parameterize( user   => $user );
  my $icy_compsite = $builder->build_rebulder( $composite );
  
  ###
  ### restore composite with current context ...
  ###
  
  my $builder = eval $icy_composite;
  my $melted_composite = $builder->( driver => $driver,
                                     user   => $user );

=head1 DESCRIPTION

This approach is like to C<Data::Dumper> approach. Moreover,
an output of this module is not easy to read.

However this solution can rebuild tied values, weak references, 
and closures.
In addition, this solution can parameterize 
arbitrary nodes of composite. Users can give new objects as 
arguments of the subroutine which is result.

=cut

use B;
use Scalar::Util qw( isweak refaddr blessed looks_like_number);
use UNIVERSAL qw(isa  can);
use Carp;
use Sub::Name;
use Path::Class;
use Lexical::Alias;
use B::Deparse;
use PadWalker;
use Data::Polymorph;


=head1 STATIC METHODS

=over 4

=item safe_require

  safe_require ( $0 );                                # does not load
  safe_require ( Path::Class::file( $0 )->absolute ); # does not load
  safe_require ( 'path/to/module.pm' );               # loads
  safe_require ( '/absolute/path/to/module.pm');      # does not load

Loads module safery.

=cut


{
  my %loaded = ();
  sub safe_require ($){
    my $lib = shift;
    my $libabs = file($lib)->absolute;
    return if $loaded{$libabs};
    return if $libabs eq file($0)->absolute;
    $loaded{$libabs} = 1;
    require $lib unless grep{ $libabs eq file($_)->absolute } values %INC;
  }
}

sub _indent ($) {
  local $_ = shift;
  s/\n/\n  /mg;
  $_;
}

{ ############################################################
  package Data::Rebuilder::B::Deparse;
  our @ISA = qw( B::Deparse );

  sub coderef2textX{
    my $self = shift;
    my $code = $self->coderef2text( @_ );
    (
     globals => [ keys %{$self->{' globals '}} ],
     stashes => [ keys %{$self->{' stashes '}} ],
     code    => $code
    );
  }

  {
    my %globalnames =
      map (($_ => 1), qw(SIG STDIN STDOUT STDERR INC ENV ARGV ARGVOUT _));

    sub gv_name {
      my $self = shift;
      my $gv = shift;
      Carp::confess() unless ref($gv) eq "B::GV";
      my $stash = $gv->STASH->NAME;

lib/Data/Rebuilder.pm  view on Meta::CPAN

           },
          );


        my %freezer =
          (
           ###
           Any   => sub{ confess "caught unsupported type." },

           ###
           Undef => sub{ 'undef' },

           ###
           'Str' => sub{ B::perlstring( $_[0] ) },

           ###
           'Num' => sub{ $_[0] },

           ###
           'Glob' => sub{
             my $obj  = shift;
             my $name = "" . $obj;
             return "$name" unless $name =~ /^\*Symbol::GEN/;
             join("\n",
                  'do{',
                  '  # GrobRef',
                  '  require Symbol;',
                  '  my $__tmp = Symbol::gensym();',
                  ( map {
                    ( *{$obj}{$_}
                      ? ( sprintf('  *{$__tmp} = %s;',
                                  $self->freeze(*{$obj}{$_})) )
                      : () )
                    } qw( SCALAR
                          ARRAY
                          HASH
                          CODE )),
                  '  *$__tmp;',
                  '}' );
           },

           ###
           'ScalarRef' => sub{
             my $obj = shift;
             join( "\n",
                   'do{',
                   '  #ScalarRef',
                   '  my $__tmp = '.$self->freeze($$obj).';',
                   '  \\$__tmp;',
                   '}' );
           },

           #################################
           'CodeRef' => sub{
             my $cv     = shift;
             my $target = shift || $cv;
             my $var    = $self->ref_to_var($target);

             my $dp     =  ( $self->{_deparse}
                             ||= (__PACKAGE__."::B::Deparse")->new );
             my $closed = PadWalker::closed_over( $cv );
             my $b      = B::svref_2object($cv);
             my $name   = $b->GV->NAME;
             my @vars   = ();

             foreach my $key (keys %$closed) {

               my $val = $closed->{$key};

               if( $self->poly->type($val) eq 'RefRef' &&
                   $self->_is_cycled($$val)) {
                 push @vars,
                   sprintf('  my %s = undef; #cycled RefRef', $key);
                 my $lazy = $self->_lazy->{refaddr $$val} ||= [];

                 push
                   (@$lazy,
                    'require PadWalker;',
                    sprintf('${PadWalker::closed_over(%s)->{%s}} = %s;',
                            $var,
                            $self->freeze($key),
                            $self->freeze($$val))
                    );
               }
               else {
                 push( @vars,
                       sprintf ( "  my \%s = undef;\n".
                                 '  Lexical::Alias::alias_r( %s , \%s );',
                                 $key,
                                 $self->freeze($val),
                                 $key ) );
               }

             }

             my %info = $dp->coderef2textX($cv);

             foreach my $stash ( $b->STASH->NAME , @{$info{stashes}} ){
               $self->_stashes->{$stash} = 1;
             }

             join( "\n",
                   "do{",
                   '  # CodeRef',
                   (map{ sprintf('  %s = %s;',$_,$_) }@{$info{globals}}),
                   ( @vars ? '  require Lexical::Alias;' : () ),
                   @vars,
                   sprintf('  sub %s', _indent $info{code}),
                   "}",
                 );
           },

           #################################
           'ArrayRef' => sub{

             my $ref    = shift;
             my $target = shift || $ref;
             my $var    = $self->ref_to_var($target);

             my @body = ();
             my @tied = ();
             my @weak = ();
             local $_;

             for( my $i = 0; $i < @{$ref} ; $i++ ) {
               my $v = $ref->[$i];
               my $tied = tied ( $ref->[$i] );
               push @body, sprintf('    # %s', refaddr( \$ref->[$i] ));
               if( $tied ){

                 push @body, "    undef,";
                 push @tied , [$i => $tied];

               }
               elsif( $self->_is_cycled($v) ) {

                 push @body, "    undef,";
                 my $lazy = $self->_lazy->{ refaddr $v } ||= [];
                 push( @$lazy ,



( run in 2.799 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )