Acme-State
view release on metacpan or search on metacpan
lib/Acme/State.pm view on Meta::CPAN
package Acme::State;
use 5.008000;
use strict;
use warnings;
our $VERSION = '0.03';
use B;
use Storable;
use Devel::Caller 'caller_cv';
use IO::Handle;
my @stop_modules = (
'1' .. '9', ':',
'SIG', 'stderr', '__ANON__', 'utf8::', 'CORE::', 'DynaLoader::', 'strict::',
'stdout', 'attributes::', 'stdin', 'ARGV', 'INC', 'Scalar::', 'ENV',
'Regexp::', 'XSLoader::', 'UNIVERSAL::', 'overload::', 'B::', 'Carp::',
'Data::', 'PerlIO::', '0', 'BEGIN', 'STDOUT', 'IO::', '_', 'Dumper',
'Exporter::', 'bytes::', 'STDERR', 'Internals::', 'STDIN', 'Config::',
'warnings::', 'DB::',
'APR::', 'Apache2::', 'Apache::', 'autobox::', 'BSD::', 'CGITempFile::', 'Compress::',
'Devel::', 'Dos::', 'EPOC::', 'Encode::', 'Fh::', 'File::', 'HTTP::', 'LWP::', 'List::', 'Log::',
'MIME::', 'Mac::', 'MacPerl::', 'O::', 'POSIX::', 'Scope::', 'Sys::', 'Term::', 'Thread::', 'Time::', 'VMS::',
'fields::', 'blackhole::', 'Autobox::', 'Module::', 'Win32::', 'MultipartBuffer::', 'q::', 'sort::',
);
sub import {
my $save_fn = save_file_name();
if(-f $save_fn) {
local $Storable::Eval = 1;
my $save = Storable::retrieve $save_fn;
sub {
my $package = shift;
my $tree = shift;
no strict 'refs';
for my $k (keys %$tree) {
if($k =~ m/::$/) {
caller_cv(0)->($package.$k, $tree->{$k});
} elsif(ref($tree->{$k})) {
*{$package.$k} = $tree->{$k};
} else {
die $package.$k . " doesn't contain a ref";
}
}
}->('main::', $save);
}
}
sub save_file_name {
my $zero = $0 || 'untitledprogram';
$zero =~ s{.*/}{};
return +(getpwuid $<)[7].'/'.$zero.'.store';
}
sub save_state {
our $wantcoderefs;
my $tree = sub {
my $package = shift;
my $node = shift() || { };
no strict 'refs';
for my $k (keys %$package) {
next if $k =~ m/main::$/;
next if $k =~ m/[^\w:]/;
next if grep $_ eq $k, @stop_modules;
if($k =~ m/::$/) {
# recurse into that namespace unless it corresponds to a .pm module that got used at some point
my $modulepath = $package.$k;
for($modulepath) { s{^main::}{}; s{::$}{}; s{::}{/}g; $_ .= '.pm'; }
next if exists $INC{$modulepath};
$node->{$k} ||= { };
caller_cv(0)->($package.$k, $node->{$k});
} elsif( *{$package.$k}{HASH} ) {
$node->{$k} = *{$package.$k}{HASH};
} elsif( *{$package.$k}{ARRAY} ) {
$node->{$k} = *{$package.$k}{ARRAY};
} elsif( *{$package.$k}{CODE} ) {
next unless $wantcoderefs;
# save coderefs but only if they aren't XS (can't serialize those) and weren't exported from elsewhere.
my $ob = B::svref_2object(*{$package . $k}{CODE});
my $rootop = $ob->ROOT;
my $stashname = $$rootop ? $ob->STASH->NAME . '::' : '(none)';
if($$rootop and ($stashname eq $package or 'main::'.$stashname eq $package or $stashname eq 'main::' )) {
# when we eval something in code in main::, it comes up as being exported from main::. *sigh*
$node->{$k} = *{$package . $k}{CODE};
}
} else {
$node->{$k} = *{$package.$k}{SCALAR} unless ref(*{$package.$k}{SCALAR}) eq 'GLOB';
}
}
return $node;
}->('main::');
# use Data::Dumper; print "debug: ", Data::Dumper::Dumper($tree), "\n";
local $Storable::Deparse = $wantcoderefs;
my $save_fn = save_file_name();
# $save_fn =~ s{/-}{/x}g; warn "saving to: ``$save_fn.new''";
Storable::nstore $tree, $save_fn.'.new' or die "saving state failed: $!";
# warn "okay, Storable::nstore done";
rename $save_fn, $save_fn.'.last'; # it's okay if it fails... file might not exist
rename $save_fn.'.new', $save_fn or die "renaming new save file into place as ``$save_fn'' failed: $!";
return 1;
}
( run in 0.587 second using v1.01-cache-2.11-cpan-97f6503c9c8 )