CPANPLUS-Internals-Source-MetaCPAN/lib/CPANPLUS/Internals/Source/MetaCPAN/Tie.pm
package CPANPLUS::Internals::Source::MetaCPAN::Tie;
$CPANPLUS::Internals::Source::MetaCPAN::Tie::VERSION = '0.10';
#ABSTRACT: A tie for the MetaCPAN source engine
use strict;
use warnings;
use CPANPLUS::Error;
use CPANPLUS::Module;
use CPANPLUS::Module::Fake;
use CPANPLUS::Module::Author::Fake;
use CPANPLUS::Internals::Constants;
use Params::Check qw[check];
use Module::Load::Conditional qw[can_load];
use Locale::Maketext::Simple Class => 'CPANPLUS', Style => 'gettext';
use CPANPLUS::Internals::Source::MetaCPAN::HTTP;
use JSON::PP ();
require Tie::Hash;
use vars qw[@ISA];
push @ISA, 'Tie::StdHash';
sub TIEHASH {
my $class = shift;
my %hash = @_;
my $tmpl = {
idx => { required => 1 },
table => { required => 1 },
key => { required => 1 },
cb => { required => 1 },
offset => { default => 0 },
};
my $args = check( $tmpl, \%hash ) or return;
my $obj = bless { %$args, store => {} } , $class;
return $obj;
}
sub FETCH {
my $self = shift;
my $key = shift or return;
my $idx = $self->{idx};
my $cb = $self->{cb};
my $table = $self->{table};
### did we look this one up before?
if( my $obj = $self->{store}->{$key} ) {
return $obj;
}
#https://github.com/miyagawa/cpanminus/blob/devel/lib/App/cpanminus/script.pm#L596
$key = uc( $key ) if $table eq 'author';
my $url = $self->{idx} . $table . '/' . $key;
my $http = CPANPLUS::Internals::Source::MetaCPAN::HTTP->new();
my $data = {};
my $href;
{
my $str;
$http->reset;
my $status = $http->request( $url ) or return;
return unless $status eq '200';
return unless $str = $http->body;
eval { $href = JSON::PP::decode_json( $str ); };
return unless $href and keys %$href;
}
### expand author if needed
### XXX no longer generic :(
if( $table eq 'module' ) {
return if $href->{maturity} and $href->{maturity} eq 'developer';
return unless $href->{indexed};
$href->{author} = delete $href->{author};
$href->{module} = $key;
$href->{version} = delete $href->{version};
{
$http->reset;
my $durl = $self->{idx} . 'release' . '/' . $href->{distribution};
my $str;
my $status = $http->request( $durl );
return unless $status eq '200';
return unless $str = $http->body;
my $dref;
eval { $dref = JSON::PP::decode_json( $str ); };
return unless $dref and keys %$dref;
( $href->{dist_file} = $dref->{download_url} ) =~ s!^.+?authors/id/!!;
}
my ($author, $package) = $href->{dist_file} =~
m| (?:[A-Z\d-]/)?
(?:[A-Z\d-]{2}/)?
([A-Z\d-]+) (?:/[\S]+)?/
([^/]+)$
|xsg;
### remove file name from the path
$href->{dist_file} =~ s|/[^/]+$||;
$href->{path} = join '/', 'authors/id', delete $href->{dist_file};
$href->{package} = $package;
$href->{comment} = $href->{description} = $href->{dslip} = $href->{mtime} = '';
$href->{author} = $cb->author_tree( $href->{author} ) or return;
$data->{$_} = delete $href->{$_}
for qw(author comment description dslip mtime package module version path);
}
else {
$data->{author} = delete $href->{name};
$data->{cpanid} = delete $href->{pauseid};
}
my $class = {
module => 'CPANPLUS::Module',
author => 'CPANPLUS::Module::Author',
}->{ $table };
my $obj = $self->{store}->{$key} = $class->new( %$data, _id => $cb->_id );
return $obj;
}
sub STORE {
my $self = shift;
my $key = shift;
my $val = shift;
$self->{store}->{$key} = $val;
}
sub FIRSTKEY {
my $self = shift;
my $idx = $self->{'idx'};
my $table = $self->{table};
my $lkup = $table eq 'module' ? 'mod' : 'auth';
my $url = $idx . "yaml/${lkup}keys";
my $str;
my $http = CPANPLUS::Internals::Source::MetaCPAN::HTTP->new();
my $status = $http->request( $url ) or return;
return unless $status eq '200';
return unless $str = $http->body;
my $res;
eval { $res = JSON::PP::decode_json( $str ); };
return unless $res;
my $ref = $table eq 'module' ? 'mod_name' : 'cpan_id';
@{ $self->{keys} } =
map { $_->{$ref} } @$res;
$self->{offset} = 0;
return $self->{keys}->[0];
}
sub NEXTKEY {
my $self = shift;
my $idx = $self->{'idx'};
my $table = $self->{table};
my $key = $self->{keys}->[ $self->{offset} ];
$self->{offset} +=1;
if ( wantarray ) {
### use each() semantics
my $val = $self->FETCH( $key );
return ( $key, $val );
}
return $key;
}
sub EXISTS { !!$_[0]->FETCH( $_[1] ) }
### intentionally left blank
sub DELETE { }
sub CLEAR { }
qq[Tie your mother down];
__END__
=pod
=encoding UTF-8
=head1 NAME
CPANPLUS::Internals::Source::MetaCPAN::Tie - A tie for the MetaCPAN source engine
=head1 VERSION
version 0.10
=head1 DESCRIPTION
CPANPLUS::Internals::Source::MetaCPAN::Tie is a tie for L<CPANPLUS::Internals::Source::MetaCPAN>.
It has no user serviceable parts.
=head1 SEE ALSO
L<CPANPLUS>
L<CPANPLUS::Internals::Source>
=head1 AUTHOR
Chris Williams <chris@bingosnet.co.uk>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2017 by Chris Williams and Jos Boumans.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut