Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
miyagawa committed Sep 2, 2009
0 parents commit 6580e22
Show file tree
Hide file tree
Showing 14 changed files with 346 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
@@ -0,0 +1,5 @@
META.yml
Makefile
inc/
pm_to_blib
*~
2 changes: 2 additions & 0 deletions .shipit
@@ -0,0 +1,2 @@
steps = FindVersion, ChangeVersion, CheckChangeLog, DistTest, Commit, Tag, MakeDist, UploadCPAN
git.push_to = origin
4 changes: 4 additions & 0 deletions Changes
@@ -0,0 +1,4 @@
Revision history for Perl extension App::CPAN::Fast

0.01 Tue Sep 1 22:03:51 2009
- original version
32 changes: 32 additions & 0 deletions MANIFEST
@@ -0,0 +1,32 @@
.gitignore
Changes
cpanf
inc/Module/Install.pm
inc/Module/Install/AuthorTests.pm
inc/Module/Install/Base.pm
inc/Module/Install/Can.pm
inc/Module/Install/Fetch.pm
inc/Module/Install/Include.pm
inc/Module/Install/Makefile.pm
inc/Module/Install/Metadata.pm
inc/Module/Install/Repository.pm
inc/Module/Install/Scripts.pm
inc/Module/Install/TestBase.pm
inc/Module/Install/Win32.pm
inc/Module/Install/WriteAll.pm
inc/Spiffy.pm
inc/Test/Base.pm
inc/Test/Base/Filter.pm
inc/Test/Builder.pm
inc/Test/Builder/Module.pm
inc/Test/More.pm
lib/App/CPAN/Fast.pm
Makefile.PL
MANIFEST This list of files
META.yml
README
t/00_compile.t
xt/perlcritic.t
xt/pod.t
xt/podspell.t
xt/synopsis.t
14 changes: 14 additions & 0 deletions MANIFEST.SKIP
@@ -0,0 +1,14 @@
\bRCS\b
\bCVS\b
\.svn/
\.git/
^MANIFEST\.
^Makefile$
~$
\.old$
^blib/
^pm_to_blib
^MakeMaker-\d
\.gz$
\.cvsignore
\.shipit
18 changes: 18 additions & 0 deletions Makefile.PL
@@ -0,0 +1,18 @@
use inc::Module::Install;
name 'App-CPAN-Fast';
all_from 'lib/App/CPAN/Fast.pm';

requires 'CPAN::Inject';
requires 'App::Cmd', '0.30';
requires 'JSON', '2.0';
requires 'URI';
requires 'LWP';

install_script 'cpanf';

build_requires 'Test::More';
use_test_base;
auto_include_deps;
author_tests('xt');
auto_set_repository;
WriteAll;
27 changes: 27 additions & 0 deletions README
@@ -0,0 +1,27 @@
This is Perl module App::CPAN::Fast.

INSTALLATION

App::CPAN::Fast installation is straightforward. If your CPAN shell is set up,
you should just be able to do

% cpan App::CPAN::Fast

Download it, unpack it, then build it as per the usual:

% perl Makefile.PL
% make && make test

Then install it:

% make install

DOCUMENTATION

App::CPAN::Fast documentation is available as in POD. So you can do:

% perldoc App::CPAN::Fast

to read the documentation online with your favorite pager.

Tatsuhiko Miyagawa
51 changes: 51 additions & 0 deletions cpanf
@@ -0,0 +1,51 @@
#!/usr/bin/perl
use strict;
use warnings;
use App::CPAN::Fast;

App::CPAN::Fast->run;

__END__
=head1 NAME
cpanf - install CPAN modules from fastest mirrors
=head1 SYNOPSIS
# install Module::Name from the fastest mirror
cpanf Module::Name
# list the recent uploads
cpanf -l
# search the latest uploads for "Catalyst"
cpanf -l Catalyst
=head1 DESCRIPTION
C<cpanf> is a command line tool to find, index and download CPAN
modules from fastest mirrors. It would be useful if you want to
install modules before it's mirrored to the mirrors near you which
usually takes a day or two.
This tool utilizes FriendFeed CPAN relatime bot
(L<http://friendfeed.com/cpan>) to search and query the updates and
CPAN Testers mirror (L<http://cpan.cpantesters.org/>) to download
modules.
=head1 AUTHOR
Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>
=head1 LICENSE
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
=head1 SEE ALSO
L<App::CPAN::Fast> L<http://friendfeed.com/cpan>
=cut
167 changes: 167 additions & 0 deletions lib/App/CPAN/Fast.pm
@@ -0,0 +1,167 @@
package App::CPAN::Fast;

use strict;
use 5.008_001;
our $VERSION = '0.01';

use base qw(App::Cmd::Simple);

use Carp;
use CPAN::Inject;
use File::Temp;
use JSON;
use LWP::UserAgent;
use URI;

sub opt_spec {
return (
[ "install|i", "install the module" ],
[ "list|l", "list the recent uploads" ],
);
}

sub execute {
my($self, $opt, $args) = @_;

if ($opt->{list} && $args->[0]) {
$self->search($args->[0]);
} elsif ($opt->{list}) {
$self->recent;
} else {
$self->install($args);
}
}

sub recent {
my $self = shift;
my $res = $self->call("/feed/cpan");
$self->display_results($res);
}

sub search {
my($self, $q) = @_;
my $res = $self->call("/search", { q => "$q group:cpan" });
$self->display_results($res);
}

sub install {
my($self, $dists) = @_;

my @injected;
for my $dist (@$dists) {
my $path = $self->inject($dist);
if ($path) {
push @injected, $path;
} else {
print "$dist not found.\n";
}
}

if (@injected) {
require CPAN;
CPAN::Shell->install(@injected);
}
}

sub inject {
my($self, $dist) = @_;
$dist =~ s/::/-/g;

my $res = $self->call("/search", { q => "$dist group:cpan" });
for my $entry (@{$res->{entries}}) {
my $info = $self->parse_entry($entry->{body}) or next;
if ($info->{dist} eq $dist) {
return $self->do_inject($info);
}
}

return;
}

sub do_inject {
my($self, $info) = @_;

my $dir = File::Temp::tempdir(CLEANUP => 1);
my $local = "$dir/$info->{dist}-$info->{version}.tar.gz";

my $res = $self->new_ua->mirror($info->{url}, $local);
if ($res->is_error) {
croak "Fetching $info->{url} failed: ", $res->status_line;
}

CPAN::Inject->from_cpan_config->add(file => $local);
}

sub display_results {
my($self, $res) = @_;
for my $entry (@{$res->{entries}}) {
my $info = $self->parse_entry($entry->{body}) or next;
printf "%s-%s (%s)\n", $info->{dist}, $info->{version}, $info->{author};
}
}

sub parse_entry {
my($self, $body) = @_;

if ($body =~ /^([\w\-]+) ([0-9\._]*) by (.+?) - <a.*href="(http:.*?\.tar\.gz)"/) {
return {
dist => $1,
version => $2,
author => $3,
url => $4,
};
}

return;
}

sub new_ua {
my $self = shift;
LWP::UserAgent->new(agent => "cpanf/$VERSION", env_proxy => 1);
}

sub call {
my($self, $method, $opts) = @_;

my $uri = URI->new("http://friendfeed-api.com/v2$method");
$uri->query_form(%$opts) if $opts;

my $ua = $self->new_ua;
my $res = $ua->get($uri);

if ($res->is_error) {
croak "HTTP error: ", $res->status_line;
}

JSON::decode_json($res->content);
}

1;
__END__
=encoding utf-8
=for stopwords
=head1 NAME
App::CPAN::Fast - backend for I<cpanf> command
=head1 DESCRIPTION
App::CPAN::Fast is a backend for I<cpanf> command.
=head1 AUTHOR
Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>
=head1 LICENSE
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
=head1 SEE ALSO
L<cpanf>
=cut
4 changes: 4 additions & 0 deletions t/00_compile.t
@@ -0,0 +1,4 @@
use strict;
use Test::More tests => 1;

BEGIN { use_ok 'App::CPAN::Fast' }
5 changes: 5 additions & 0 deletions xt/perlcritic.t
@@ -0,0 +1,5 @@
use strict;
use Test::More;
eval q{ use Test::Perl::Critic };
plan skip_all => "Test::Perl::Critic is not installed." if $@;
all_critic_ok("lib");
4 changes: 4 additions & 0 deletions xt/pod.t
@@ -0,0 +1,4 @@
use Test::More;
eval "use Test::Pod 1.00";
plan skip_all => "Test::Pod 1.00 required for testing POD" if $@;
all_pod_files_ok();
9 changes: 9 additions & 0 deletions xt/podspell.t
@@ -0,0 +1,9 @@
use Test::More;
eval q{ use Test::Spelling };
plan skip_all => "Test::Spelling is not installed." if $@;
add_stopwords(<DATA>);
set_spell_cmd("aspell -l en list");
all_pod_files_spelling_ok('lib');
__DATA__
Tatsuhiko
Miyagawa
4 changes: 4 additions & 0 deletions xt/synopsis.t
@@ -0,0 +1,4 @@
use Test::More;
eval "use Test::Synopsis";
plan skip_all => "Test::Synopsis required" if $@;
all_synopsis_ok();

0 comments on commit 6580e22

Please sign in to comment.