package Class::Rebirth; ## Brings a deserialized class back to life
$Class::Rebirth::VERSION = '1.000';

use strict;
use warnings;
use Carp;

use Scalar::Util;

use vars qw(@ISA @EXPORT %EXPORT_TAGS $VERSION);
use Exporter; 

use Data::Dumper;

use List::MoreUtils qw(uniq);

@ISA = qw(Exporter);

%EXPORT_TAGS = ( all => [qw(
                      rebirth
                )] ); 

Exporter::export_ok_tags('all'); 


# When a class is deserialized from saved dump it still holds the information,
# but the methods are not accessable anymore.
#
# So far a run with eval would do the rebirth, but that does only work if
# needed classes are required/ used. Here the Class::Rebirth also cares about requiring
# the needed classes nested in the potential object.
#
# A death class I call zombie (has nothing todo with processes), as they look like
# normal classes but have no living methods.
#
#
#
# SYNOPSIS
# ========
#
#   use Class::Rebirth 'rebirth';
#   my $object = rebirth( $zombie );
#
#
# or 
#
#   use Class::Rebirth;
#   my $object = Class::Rebirth::rebirth( $zombie );
#
# 
# It is also able to use a data dump instead of an object.
#
#   my $object = Class::Rebirth::rebirth( $dump );
#
# 
#
# AUTHOR
# ======
# Andreas Hernitscheck  ahernit(AT)cpan.org


# @brief Takes a death object and and creates a living object of it.
# Such a zombie class looks like a normal class when you dump it.
# But it is not alive, means methods won't work. An Effect which
# happens by deserializing classes from a store (dumped data).
sub rebirth { # $object ($zombie)
  my $zombie = shift or croak "requires zombie";
  my $obj;

  # if zombie is dump string, build an object first
  if ($zombie =~ /^\$/){
    $zombie = _createObjectByDump( $zombie );
  }


  my @pkgs = _getUsedPackagesOfObject($zombie);

  foreach my $p (@pkgs){
    eval("require $p;");
    if ($@){die $@};
  }

  ## rerun an eval to realy bring it back to life
  my $target;
  my $ser = Data::Dumper->Dump([$zombie],['$target']);
  eval $ser;
  if ($@){die $@};


  return $target;
}


# Can use a data dump'ed string which
# starts with a variable assignment like $VAR1=
# @return object
sub _createObjectByDump{
  my $dump = shift;
  my $target;


  # relace var with own var
  $dump =~ s/^(\$[a-z0-9]+)/\$target/i;

  eval $dump;

  if ($@){die $@};

  return $target;
}



# Does a deep search in object for
# used package names.
# @return list of packages
sub _getUsedPackagesOfObject{
  my $obj = shift;
  my @list;


  if (Scalar::Util::blessed $obj){
    push @list, ref($obj);
  }


  foreach my $k (keys %$obj){
    my $value = $obj->{$k};

    # walk down any hash to find more blessed objects
    if (ref $obj->{$k} eq 'HASH'){

      my @sublist = _getUsedPackagesOfObject( $value );

      push @list, @sublist;
    }

    # is blessed object
    if (Scalar::Util::blessed $value){
      push @list, ref($obj);

      my @sublist = _getUsedPackagesOfObject( $value );
      push @list, @sublist;
    }  
  }

  # make unique strings
  @list = uniq @list;

  return @list;
}






1;




#################### pod generated by Pod::Autopod - keep this line to make pod updates possible ####################

=head1 NAME

Class::Rebirth.pm - Class::Rebirth.pm


=head1 SYNOPSIS


  use Class::Rebirth 'rebirth';
  my $object = rebirth( $zombie );


or 

  use Class::Rebirth;
  my $object = Class::Rebirth::rebirth( $zombie );


It is also able to use a data dump instead of an object.

  my $object = Class::Rebirth::rebirth( $dump );





=head1 DESCRIPTION

When a class is deserialized from saved dump it still holds the information,
but the methods are not accessable anymore.

So far a run with eval would do the rebirth, but that does only work if
needed classes are required/ used. Here the Class::Rebirth also cares about requiring
the needed classes nested in the potential object.

A death class I call zombie (has nothing todo with processes), as they look like
normal classes but have no living methods.





=head1 REQUIRES

L<List::MoreUtils> 

L<Data::Dumper> 

L<Exporter> 

L<Scalar::Util> 

L<Carp> 


=head1 METHODS

=head2 rebirth

 my $object = rebirth($zombie);

Takes a death object and and creates a living object of it.
Such a zombie class looks like a normal class when you dump it.
But it is not alive, means methods won't work. An effect which
happens by deserializing classes from a store (dumped data).



=head1 AUTHOR

Andreas Hernitscheck  ahernit(AT)cpan.org


=cut