The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package MyCPAN::App::DPAN;
use strict;
use warnings;

use base qw(MyCPAN::App::BackPAN::Indexer);
use vars qw($VERSION $logger);

use Cwd qw(cwd);
use File::Spec::Functions;
use Log::Log4perl;

$VERSION = '1.28_11';

=head1 NAME

MyCPAN::App::DPAN - Create a CPAN-like structure out of some dists

=head1 SYNOPSIS

	use MyCPAN::App::DPAN;

	my $application = MyCPAN::App::DPAN->activate( @ARGV );

	# do some other stuff, anything that you like

	$application->activate_end;

=head1 DESCRIPTION

This module ties together all the bits to let the C<dpan> do its work. It
overrides the defaults in C<MyCPAN::App::BackPAN::Indexer> to provide the
right components.

The work happens in two steps. When you call C<activate>, the program goes
through all of the steps to examin each of the module distributions. It creates
a report for each distribution, then stops. This pause right after the
examination gives you the chance to do something right before the program
creates the PAUSE index files. The examination might take several minutes
(or even hours depending on how much you want to index), so you have a chance
to check the state of the world before the next step.

When you call C<activate_end>, the program takes the results from the
previous step and creates the PAUSE index files in the F<modules> directory.
This step should be very quick since all of the information is ready-to-go.

=cut

=head1 METHODS

=over 4

=cut

BEGIN {
use vars qw( $Starting_dir );
$Starting_dir = cwd();

$SIG{INT} = sub { print "Caught SIGINT\n"; chdir $Starting_dir; exit() };

my $cwd = cwd();

my $report_dir = catfile( $cwd, 'indexer_reports' );

my %Defaults = (
    author_map                  => undef,
	dpan_dir                    => $cwd,
	collator_class              => 'MyCPAN::App::DPAN::Reporter::Minimal',
	dispatcher_class            => 'MyCPAN::Indexer::Dispatcher::Serial',
	extra_reports_dir           => undef,
	fresh_start                 => defined $ENV{DPAN_FRESH_START} ? $ENV{DPAN_FRESH_START} : 0,
	i_ignore_errors_at_my_peril => 0,
	ignore_missing_dists        => 0,
	ignore_packages             => 'main MY MM DB bytes DynaLoader',
	indexer_class               => 'MyCPAN::App::DPAN::Indexer',
	organize_dists              => 1,
	parallel_jobs               => 1,
	pause_id                    => 'DPAN',
	pause_full_name             => "DPAN user <CENSORED>",
	queue_class                 => 'MyCPAN::App::DPAN::SkipQueue',
	relative_paths_in_report    => 1,
	reporter_class              => 'MyCPAN::App::DPAN::Reporter::Minimal',
	skip_perl                   => 0,
	use_real_whois              => 0,
	);

=item default_keys

Returns the list of default configuration directive.

=cut

sub default_keys
	{
	my %Seen;
	grep { ! $Seen{$_}++ } keys %Defaults, $_[0]->SUPER::default_keys;
	}

=item default( DIRECTIVE )

Returns the configuration value for DIRECTIVE.

=cut

sub default
	{
	exists $Defaults{ $_[1] }
		?
	$Defaults{ $_[1] }
		:
	$_[0]->SUPER::default( $_[1] );
	}

=item adjust_config

Adjusts the configuration to set various internal values. You don't need
to call this yourself.

=cut

sub adjust_config
	{
	my( $application ) = @_;

	my $coordinator = $application->get_coordinator;
	my $config      = $coordinator->get_config;

	# the Indexer stuff expects the directory in backpan_dir
	if( $config->exists( 'dpan_dir') )
		{
		$config->set(
			'backpan_dir',
			$config->get( 'dpan_dir' )
			);
		}

	$application->SUPER::adjust_config;
	}

$logger = Log::Log4perl->get_logger( 'backpan_indexer' );
}

=item activate_steps

Returns the list of methods to invoke from C<activate>. By overriding this
method you can change the DPAN process.

=cut

sub activate_steps
	{
	qw(
	process_options
	setup_coordinator
	setup_environment
	handle_config
	setup_logging
	fresh_start
	setup_dirs
	run_components
	);
	}

=item activate_end

Runs right before C<dpan> is about to exit. It calls the postflight
handler if one if configured. It prints a short summary message to
standard output.

=cut

sub activate_end
	{
	my( $application ) = @_;

	my $coordinator = $application->get_coordinator;
	$application->cleanup;

	$application->_handle_postflight;

	print <<"HERE" unless( $coordinator->get_note( 'epic_fail' ) || $coordinator->get_note( 'postflight_failure' ) );
=================================================
Ensure you reload your indices in your CPAN tool!

For CPAN.pm, use:

	cpan> reload index

For CPANPLUS, use

	CPAN Terminal> x
=================================================
HERE

	print <<"HERE" if $coordinator->get_note( 'epic_fail' );
=================================================
Something really bad happened and I couldn't
finish creating the index files. The DPAN is
incomplete.
=================================================
HERE

	print <<"HERE" if $coordinator->get_note( 'postflight_failure' );
=================================================
I wasn't able to complete the postflight step.
DPAN might be okay, but your post processing
may have failed.
=================================================
HERE

	$application->_exit;
	}

sub _handle_postflight
	{
	my( $application ) = @_;

	$logger->info( "Handling cleanup" );

	my $config = $application->get_coordinator->get_config;

	# if it's not in the config then we're done already.
	return 1 unless $config->exists( 'postflight_class' );

	my $class = $config->get( 'postflight_class' );

	if( $application->_check_postflight_class( $class ) )
		{
		eval { $class->run( $application ) } or do {
			my $at = $@;
			$logger->error( "postflight class [$class] complained: $at" );
			$application->get_coordinator->set_note( 'postflight_failure', $at );
			return;
			};
		}

	return 1;
	}

sub _check_postflight_class
	{
	my( $application, $class ) = @_;

	if( eval( "require $class; 1" ) )
		{
		unless( eval { $class->can('run') } )
			{
			my $error = "Class [$class] does not claim to have a run() method";
			$logger->error( $error );
			$application->get_coordinator->set_note( 'postflight_class_failure', $error );
			return;
			}
		}
	else
		{
		my $at = $@;
		$logger->error( "Could not load postflight class [$class]: $at" );
		$application->get_coordinator->set_note( 'postflight_class_failure', $at );
		return;
		}

	return 1;
	}

=item components

Returns the list of components to load and the implementing classes.

=cut

sub components
	{
	(
	[ qw( queue      MyCPAN::Indexer::Queue                get_queue      ) ],
	[ qw( dispatcher MyCPAN::Indexer::Dispatcher::Serial   get_dispatcher ) ],
	[ qw( reporter   MyCPAN::App::DPAN::Reporter::Minimal  get_reporter   ) ],
	[ qw( worker     MyCPAN::Indexer::Worker               get_task       ) ],
	[ qw( collator   MyCPAN::App::DPAN::Reporter::Minimal  get_collator   ) ],
# this has to be last because it kicks off everything
	[ qw( interface  MyCPAN::Indexer::Interface::Text      do_interface   ) ],
	)
	}

=item fresh_start

If C<fresh_start> is set, this method deletes the reports in the 
report directory, leaving the directories in place.

=cut

sub fresh_start
	{
	my( $application ) = @_;

	my $config = $application->get_coordinator->get_config;
	
	return unless $config->get( 'fresh_start' );
	
	require File::Path;
	foreach my $dir ( map { my $m = "${_}_report_subdir"; $config->$m() } qw(error success) )
		{
		$logger->info( "Cleaning report directory [$dir]" );
		unlink glob( catfile( $dir, '*' ) );
		}

	return 1;
	}

1;

=back

=head1 SOURCE AVAILABILITY

This code is in Github:

      git://github.com/briandfoy/mycpan-indexer.git
      git://github.com/briandfoy/mycpan--app--dpan.git

=head1 AUTHOR

brian d foy, C<< <bdfoy@cpan.org> >>

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2008-2010, brian d foy, All Rights Reserved.

You may redistribute this under the same terms as Perl itself.

=cut