blob: d589bbd1d8b231c5f3ea0ce984fe10fcde775cd3 [file] [log] [blame]
package CPANPLUS::Internals::Source::Memory;
use base 'CPANPLUS::Internals::Source';
use strict;
use CPANPLUS::Error;
use CPANPLUS::Module;
use CPANPLUS::Module::Fake;
use CPANPLUS::Module::Author;
use CPANPLUS::Internals::Constants;
use File::Fetch;
use Archive::Extract;
use IPC::Cmd qw[can_run];
use File::Temp qw[tempdir];
use File::Basename qw[dirname];
use Params::Check qw[allow check];
use Module::Load::Conditional qw[can_load];
use Locale::Maketext::Simple Class => 'CPANPLUS', Style => 'gettext';
$Params::Check::VERBOSE = 1;
=head1 NAME
CPANPLUS::Internals::Source::Memory - In memory implementation
=cut
### flag to show if init_trees got its' data from storable. This allows
### us to not write an existing stored file back to disk
{ my $from_storable;
sub _init_trees {
my $self = shift;
my $conf = $self->configure_object;
my %hash = @_;
my($path,$uptodate,$verbose,$use_stored);
my $tmpl = {
path => { default => $conf->get_conf('base'), store => \$path },
verbose => { default => $conf->get_conf('verbose'), store => \$verbose },
uptodate => { required => 1, store => \$uptodate },
use_stored => { default => 1, store => \$use_stored },
};
check( $tmpl, \%hash ) or return;
### retrieve the stored source files ###
my $stored = $self->__memory_retrieve_source(
path => $path,
uptodate => $uptodate && $use_stored,
verbose => $verbose,
) || {};
### we got this from storable if $stored has keys..
$from_storable = keys %$stored ? 1 : 0;
### set up the trees
$self->_atree( $stored->{_atree} || {} );
$self->_mtree( $stored->{_mtree} || {} );
return 1;
}
sub _standard_trees_completed { return $from_storable }
sub _custom_trees_completed { return $from_storable }
sub _finalize_trees {
my $self = shift;
my $conf = $self->configure_object;
my %hash = @_;
my($path,$uptodate,$verbose);
my $tmpl = {
path => { default => $conf->get_conf('base'), store => \$path },
verbose => { default => $conf->get_conf('verbose'), store => \$verbose },
uptodate => { required => 1, store => \$uptodate },
};
{ local $Params::Check::ALLOW_UNKNOWN = 1;
check( $tmpl, \%hash ) or return;
}
### write the stored files to disk, so we can keep using them
### from now on, till they become invalid
### write them if the original sources weren't uptodate, or
### we didn't just load storable files
$self->__memory_save_source() if !$uptodate or not $from_storable;
return 1;
}
### saves current memory state
sub _save_state {
my $self = shift;
return $self->_finalize_trees( @_, uptodate => 0 );
}
}
sub _add_author_object {
my $self = shift;
my %hash = @_;
my $class;
my $tmpl = {
class => { default => 'CPANPLUS::Module::Author', store => \$class },
map { $_ => { required => 1 } }
qw[ author cpanid email ]
};
my $href = do {
local $Params::Check::NO_DUPLICATES = 1;
check( $tmpl, \%hash ) or return;
};
my $obj = $class->new( %$href, _id => $self->_id );
$self->author_tree->{ $href->{'cpanid'} } = $obj or return;
return $obj;
}
sub _add_module_object {
my $self = shift;
my %hash = @_;
my $class;
my $tmpl = {
class => { default => 'CPANPLUS::Module', store => \$class },
map { $_ => { required => 1 } }
qw[ module version path comment author package description dslip mtime ]
};
my $href = do {
local $Params::Check::NO_DUPLICATES = 1;
check( $tmpl, \%hash ) or return;
};
my $obj = $class->new( %$href, _id => $self->_id );
### Every module get's stored as a module object ###
$self->module_tree->{ $href->{module} } = $obj or return;
return $obj;
}
{ my %map = (
_source_search_module_tree => [ module_tree => 'CPANPLUS::Module' ],
_source_search_author_tree => [ author_tree => 'CPANPLUS::Module::Author' ],
);
while( my($sub, $aref) = each %map ) {
no strict 'refs';
my($meth, $class) = @$aref;
*$sub = sub {
my $self = shift;
my $conf = $self->configure_object;
my %hash = @_;
my($authors,$list,$verbose,$type);
my $tmpl = {
data => { default => [],
strict_type=> 1, store => \$authors },
allow => { required => 1, default => [ ], strict_type => 1,
store => \$list },
verbose => { default => $conf->get_conf('verbose'),
store => \$verbose },
type => { required => 1, allow => [$class->accessors()],
store => \$type },
};
my $args = check( $tmpl, \%hash ) or return;
my @rv;
for my $obj ( values %{ $self->$meth } ) {
#push @rv, $auth if check(
# { $type => { allow => $list } },
# { $type => $auth->$type }
# );
push @rv, $obj if allow( $obj->$type() => $list );
}
return @rv;
}
}
}
=pod
=head2 $cb->__memory_retrieve_source(name => $name, [path => $path, uptodate => BOOL, verbose => BOOL])
This method retrieves a I<storable>d tree identified by C<$name>.
It takes the following arguments:
=over 4
=item name
The internal name for the source file to retrieve.
=item uptodate
A flag indicating whether the file-cache is up-to-date or not.
=item path
The absolute path to the directory holding the source files.
=item verbose
A boolean flag indicating whether or not to be verbose.
=back
Will get information from the config file by default.
Returns a tree on success, false on failure.
=cut
sub __memory_retrieve_source {
my $self = shift;
my %hash = @_;
my $conf = $self->configure_object;
my $tmpl = {
path => { default => $conf->get_conf('base') },
verbose => { default => $conf->get_conf('verbose') },
uptodate => { default => 0 },
};
my $args = check( $tmpl, \%hash ) or return;
### check if we can retrieve a frozen data structure with storable ###
my $storable = can_load( modules => {'Storable' => '0.0'} )
if $conf->get_conf('storable');
return unless $storable;
### $stored is the name of the frozen data structure ###
my $stored = $self->__memory_storable_file( $args->{path} );
if ($storable && -e $stored && -s _ && $args->{'uptodate'}) {
msg( loc("Retrieving %1", $stored), $args->{'verbose'} );
my $href = Storable::retrieve($stored);
return $href;
} else {
return;
}
}
=pod
=head2 $cb->__memory_save_source([verbose => BOOL, path => $path])
This method saves all the parsed trees in I<storable>d format if
C<Storable> is available.
It takes the following arguments:
=over 4
=item path
The absolute path to the directory holding the source files.
=item verbose
A boolean flag indicating whether or not to be verbose.
=back
Will get information from the config file by default.
Returns true on success, false on failure.
=cut
sub __memory_save_source {
my $self = shift;
my %hash = @_;
my $conf = $self->configure_object;
my $tmpl = {
path => { default => $conf->get_conf('base'), allow => DIR_EXISTS },
verbose => { default => $conf->get_conf('verbose') },
force => { default => 1 },
};
my $args = check( $tmpl, \%hash ) or return;
my $aref = [qw[_mtree _atree]];
### check if we can retrieve a frozen data structure with storable ###
my $storable;
$storable = can_load( modules => {'Storable' => '0.0'} )
if $conf->get_conf('storable');
return unless $storable;
my $to_write = {};
foreach my $key ( @$aref ) {
next unless ref( $self->$key );
$to_write->{$key} = $self->$key;
}
return unless keys %$to_write;
### $stored is the name of the frozen data structure ###
my $stored = $self->__memory_storable_file( $args->{path} );
if (-e $stored && not -w $stored) {
msg( loc("%1 not writable; skipped.", $stored), $args->{'verbose'} );
return;
}
msg( loc("Writing compiled source information to disk. This might take a little while."),
$args->{'verbose'} );
my $flag;
unless( Storable::nstore( $to_write, $stored ) ) {
error( loc("could not store %1!", $stored) );
$flag++;
}
return $flag ? 0 : 1;
}
sub __memory_storable_file {
my $self = shift;
my $conf = $self->configure_object;
my $path = shift or return;
### check if we can retrieve a frozen data structure with storable ###
my $storable = $conf->get_conf('storable')
? can_load( modules => {'Storable' => '0.0'} )
: 0;
return unless $storable;
### $stored is the name of the frozen data structure ###
### changed to use File::Spec->catfile -jmb
my $stored = File::Spec->rel2abs(
File::Spec->catfile(
$path, #base dir
$conf->_get_source('stored') #file
. '.s' .
$Storable::VERSION #the version of storable
. '.c' .
$self->VERSION #the version of CPANPLUS
. STORABLE_EXT #append a suffix
)
);
return $stored;
}
# Local variables:
# c-indentation-style: bsd
# c-basic-offset: 4
# indent-tabs-mode: nil
# End:
# vim: expandtab shiftwidth=4:
1;