JSAN::Transport - JavaScript Archive Network Transport and Resources


JSAN-Client documentation Contained in the JSAN-Client distribution.

Index


Code Index:

NAME

Top

JSAN::Transport - JavaScript Archive Network Transport and Resources

SYNOPSIS

Top

  # Create a transport
  my $transport = JSAN::Transport->new(
      verbose       => 1,
      mirror_remote => 'http://openjsan.org/',
      mirror_local  => '~/.jsan',
  );

  # Retrieve a file
  $transport->file_get( 'index.sqlite' );

  # Mirror a file
  $transport->mirror( '/dist/a/ad/adamk/Display.Swap-0.01.tar.gz' );

DESCRIPTION

Top

JSAN::Transport provides the primary programatic interface for creating and manipulating a JSAN client transport and resource manager.

It controls connection to JSAN, retrieval and mirroring of files.

METHODS

Top

new param => $value, ...

The new method initializes the JSAN client adapter. It takes a set of parameters and initializes the JSAN::Transport class.

mirror_remote

The mirror_remoter param specifies the JSAN mirror to be retrieve packages and other files from. If you do not specify a location, a default value of http://openjsan.org will be used.

mirror_local

A JSAN client downloads and caches various files from the repository. This primarily means the JSAN packages themselves, but also includes things like the index and other control files. This effectively creates a partial local mirror of the repository, although it will also include some working files not found on the server.

The mirror_local param specifies the path for the local mirror. If the path provided does not exist, the constructor will attempt to create it. If creation fails the constructor will throw an exception.

verbose

Mainly used for console interfaces for user feedback, the verbose flag causes the client to print various information to STDOUT as it does various tasks.

Returns true, or will throw an exception (i.e. die) on error.

The mirror_location accessor returns the URI::ToDisk of the URI to local path map.

The mirror_remote accessor returns the location of the remote mirror configured when the object was created.

The mirror_local accessor returns the location of the local mirror configured when the object was created.

The verbose accessor returns the boolean flag on whether the object is running in verbose mode.

file_location path/to/file.txt

The file_location method takes the path of a file within the repository, and returns a URI::ToDisk object representing it's location on both the server, and on the local filesystem.

Paths should always be provided in unix/web format, not the local filesystem's format.

Returns a URI::ToDisk or throws an exception if passed a bad path.

file_get path/to/file.txt

The file_get method takes the path of a file within the repository, and fetches it from the remote repository, storing it at the appropriate local path.

As all file_ operations, paths should always be provided in unix/web format, not the local filesystem's format.

Returns the URI::ToDisk for the file if retrieved successfully, false ('') if the file did not exist in the repository, or undef on error.

file_mirror path/to/file.txt

The file_mirror method takes the path of a file within the repository, and mirrors it from the remote repository, storing it at the appropriate local path.

Using this method if preferable for items like indexs for which want to ensure you have the current version, but do not want to freshly download each time.

As all file_ operations, paths should always be provided in unix/web format, not the local filesystem's format.

Returns the URI::ToDisk for the file if mirrored successfully, false ('') if the file did not exist in the repository, or undef on error.

index_file

The index_file method checks that the SQLite index is up to date, and returns the path to it on the filesystem.

TO DO

Top

- Add verbose support

SUPPORT

Top

Bugs should be reported via the CPAN bug tracker at

http://rt.cpan.org/NoAuth/ReportBug.html?Queue=JSAN-Client

For other issues, contact the author.

AUTHOR

Top

Adam Kennedy <adamk@cpan.org>

COPYRIGHT

Top


JSAN-Client documentation Contained in the JSAN-Client distribution.
package JSAN::Transport;

use 5.008005;
use strict;
use Carp           ();
use Digest::MD5    ();
use File::Spec     ();
use File::Path     ();
use File::HomeDir  ();
use File::Basename ();
use URI::ToDisk    ();
use LWP::Simple    ();

our $VERSION = '0.29';


# The path to the index
my $SQLITE_INDEX = 'index.sqlite';





#####################################################################
# Constructor


sub new {
    my $class  = shift;
    my $params = { @_ };

    # Create the empty object
    my $self = bless {
        verbose => !! $params->{verbose},
        mirror  => undef,
        }, $class;

    # Apply defaults
    my $uri  = $params->{mirror_remote} || 'http://openjsan.org';
    my $path = $params->{mirror_local}
        ||
        File::Spec->catdir(
            File::HomeDir::home(), '.jsan'
        );

    # Strip superfluous trailing slashes
    $path =~ s/\/+$//;
    $uri  =~ s/\/+$//;

    # To ensure we don't overwrite cache, hash the uri
    my $digest = Digest::MD5::md5_hex("$uri");
    $path = File::Spec->catdir( $path, $digest );

    # Create the mirror_local path if needed
    -e $path or File::Path::mkpath($path);
    -d $path or Carp::croak("mirror_local: Path '$path' is not a directory");
    -w $path or Carp::croak("mirror_local: No write permissions to path '$path'");

    # Create the location object
    $self->{mirror} = URI::ToDisk->new( $path => $uri )
        or Carp::croak("Unexpected error creating URI::ToDisk object");

    return $self;
}


sub mirror_location { shift->{mirror} }

sub mirror_remote { shift->{mirror}->uri }

sub mirror_local { shift->{mirror}->path }

sub verbose { shift->{verbose} }




#####################################################################
# JSAN::Transport Methods

sub file_location {
    my $self = shift;
    my $path = $self->_path(shift);

    # Strip any leading slash
    $path =~ s/^\///;

    # Split into parts and find the location for it.
    my @parts    = split /\//, $path;
    my $mirror   = $self->mirror_location;
    $mirror->catfile( @parts );
}

sub file_get {
    my $self     = shift;
    my $location = $self->file_location(shift);

    # Check local dir exists
    my $dir = File::Basename::dirname($location->path);
    -d $dir or File::Path::mkpath($dir);

    # Fetch the file from the server
    my $rc = LWP::Simple::getstore( $location->uri, $location->path );
    if ( LWP::Simple::is_success($rc) ) {
        return $location;
    } elsif ( $rc == LWP::Simple::RC_NOT_FOUND ) {
        return '';
    } else {
        Carp::croak("$rc error retriving file " . $location->uri);
    }
}

sub file_mirror {
    my $self     = shift;
    my $path     = $self->_path(shift);
    my $location = $self->file_location($path);

    # If any only if a path is "stable" and the file already exists,
    # it is guarenteed not to change, and we don't have to do the
    # mirroring operation.
    if ( $self->_path_stable($path) and -f $location->path ) {
        return $location;
    }

    # Check local dir exists
    my $dir = File::Basename::dirname($location->path);
    -d $dir or File::Path::mkpath($dir);

    # Fetch the file from the server
    my $rc = LWP::Simple::mirror( $location->uri, $location->path );
    if ( LWP::Simple::is_success($rc) ) {
        return $location;
    } elsif ( $rc == LWP::Simple::RC_NOT_MODIFIED ) {
        return $location;
    } elsif ( $rc == LWP::Simple::RC_NOT_FOUND ) {
        return '';
    } else {
        Carp::croak("HTTP error $rc while syncing file " . $location->uri);
    }
}

sub index_file {
    shift->_index_synced->path;
}



#####################################################################
# Support Methods

# Validate a JSAN file path
sub _path {
    my $self = shift;
    my $path = shift or Carp::croak("No JSAN file path provided");

    # Strip any leading slash
    $path =~ s(^\/)();

    $path;
}

# Is a path considered "stable" (does not change over time)
sub _path_stable {
    my $self = shift;
    my $path = $self->_path(shift);

    # Paths under the "dist" path are stable
    if ( $path =~ m{^dist/} ) {
        return 1;
    }

    '';
}

# Returns the location of the SQLite index, syncronising it if needed
sub _index_synced {
    my $self = shift;
    if ( $self->{index_synced} ) {
        return $self->file_location($SQLITE_INDEX);
    }
    my $location = $self->file_mirror($SQLITE_INDEX);
    $self->{index_synced}++;
    $location;
}


1;