CPAN::Indexer::Mirror - Creates the mirror.yml and mirror.json files


CPAN-Indexer-Mirror documentation Contained in the CPAN-Indexer-Mirror distribution.

Index


Code Index:

NAME

Top

CPAN::Indexer::Mirror - Creates the mirror.yml and mirror.json files

SYNOPSIS

Top

  use CPAN::Indexer::Mirror ();

  CPAN::Indexer::Mirror->new(
      root => '/cpan/root/directory',
  )->run;

DESCRIPTION

Top

This module is used to implement a small piece of functionality inside the CPAN/PAUSE indexer which generates mirror.yml and mirror.json.

These files are used to allow CPAN clients (via the Mirror::YAML or Mirror::JSON modules) to implement mirror validation and automated selection.

METHODS

Top

Anyone who needs to know more detail than the SYNOPSIS should read the (fairly straight forward) code.

SUPPORT

Top

Bugs should be reported via the CPAN bug tracker at

http://rt.cpan.org/NoAuth/ReportBug.html?Queue=CPAN-Indexer-Mirror

AUTHOR

Top

Adam Kennedy <adamk@cpan.org>

SEE ALSO

Top

Parse::CPAN::Authors, Parse::CPAN::Packages, Parse::CPAN::Modlist, Parse::CPAN::Meta, Parse::CPAN::MirroredBy

COPYRIGHT

Top


CPAN-Indexer-Mirror documentation Contained in the CPAN-Indexer-Mirror distribution.
package CPAN::Indexer::Mirror;

use 5.006;
use strict;
use File::Spec              ();
use File::Remove            ();
use YAML::Tiny              ();
use JSON                    ();
use URI                     ();
use URI::http               ();
use IO::AtomicFile          ();
use Parse::CPAN::MirroredBy ();

use vars qw{$VERSION};
BEGIN {
	$VERSION = '0.05';
}





#####################################################################
# Constructor and Accessor Methods

sub new {
	my $class = shift;
	my $self  = bless { @_ }, $class;

	# Apply defaults
	$self->{name} ||= 'Comprehensive Perl Archive Network';
	$self->{master}  ||= 'http://www.cpan.org/';

	return $self;
}

sub root {
	$_[0]->{root};
}

sub name {
	$_[0]->{name};
}

sub master {
	$_[0]->{master};
}

sub timestamp {
	$_[0]->{timestamp} || $_[0]->now;
}

sub mirrored_by {
	File::Spec->catfile( $_[0]->root, 'MIRRORED.BY' );
}

sub mirror_yml {
	File::Spec->catfile( $_[0]->root, 'mirror.yml' );
}

sub mirror_json {
	File::Spec->catfile( $_[0]->root, 'mirror.json' );
}





#####################################################################
# Process Methods

sub run {
	my $self = ref $_[0] ? shift : shift->new(@_);

	# Always randomise the mirror order, to protect against
	# weak programmers on the other end scanning them in
	# sequential order.
	my @mirrors = sort { rand() <=> rand() }
                      $self->parser->parse_file( $self->mirrored_by );

	# Generate the data structure for the files
	my $data    = {
		version   => '1.0',
		name      => $self->name,
		master    => $self->master,
		timestamp => $self->timestamp,
		mirrors   => \@mirrors,
	};

	# Write the mirror.yml and mirror.json file.
	# Make sure the closes (and thus commits) are as close together
	# as we can possibly get them, minimising race conditions.
	SCOPE: {
		local $!;
		my $yaml_file = $self->mirror_yml;
		my $json_file = $self->mirror_json;
		my $yaml_fh   = IO::AtomicFile->open($yaml_file, "w")     or die "open: $!";
		my $json_fh   = IO::AtomicFile->open($json_file, "w")     or die "open: $!";
		$yaml_fh->print( YAML::Tiny::Dump($data) )           or die "print: $!";
		$json_fh->print(  JSON->new->pretty->encode($data) ) or die "print: $!";
		$yaml_fh->close                                      or die "close: $!";
		$json_fh->close                                      or die "close: $!";
	}

	return 1;
}

sub parser {
	my $parser = Parse::CPAN::MirroredBy->new;
	$parser->add_map(  sub { $_[0]->{dst_http} } );
	$parser->add_grep( sub {
		defined $_[0]
		and
		$_[0] =~ /\/$/
	} );
	$parser->add_map( sub { URI->new( $_[0], 'http' )->canonical->as_string } );
	return $parser;
}

sub now {
	my @t = gmtime time;
	return sprintf( "%04u-%02u-%02uT%02u:%02u:%02uZ",
		$t[5] + 1900,
		$t[4] + 1,
		$t[3],
		$t[2],
		$t[1],
		$t[0],
	);
}

1;