diff --git a/bin/generate_sitemap.pl b/bin/generate_sitemap.pl index 0905f1df89..2decbb6af4 100755 --- a/bin/generate_sitemap.pl +++ b/bin/generate_sitemap.pl @@ -5,37 +5,41 @@ use strict; use warnings; -use FindBin qw ($Bin); -use lib "$Bin/../lib"; - +use File::Basename; +use File::Spec; +use Cwd; +use Config::ZOMG; +my $root_dir; + +BEGIN { + my $bin_dir = File::Basename::dirname(__FILE__); + $root_dir + = Cwd::abs_path( File::Spec->catdir( $bin_dir, File::Spec->updir ) ); +} +use lib "$root_dir/lib"; use MetaCPAN::Sitemap; -my $out_dir = "$Bin/../root/static/sitemaps/"; -mkdir $out_dir; - -my @parts = ( - - # For authors, we're looking for the pauseid, and want to build a URL - # with 'author' in the path. - - { - object_type => 'author', - field_name => 'pauseid', - xml_file => "$out_dir/authors.xml.gz", - cpan_directory => 'author', - }, - - # For releases, we're looking for a download URL; since we're not - # building a URL, the cpan_directory is missing, but we also want to - # filter on only the 'latest' entries. - - { - object_type => 'release', - field_name => 'distribution', - xml_file => "$out_dir/releases.xml.gz", - cpan_directory => 'release', - filter => { status => 'latest' }, - } +my $config = Config::ZOMG->open( + name => 'MetaCPAN::Web', + path => $root_dir, ); -MetaCPAN::Sitemap->new($_)->process for @parts; +my $out_dir = "$root_dir/root/static/sitemaps/"; +mkdir $out_dir; + +my $web_host = $config->{web_host}; +$web_host =~ s{/\z}{}; +my $sitemaps = $config->{sitemap}; + +for my $file ( sort keys %$sitemaps ) { + my %sm_config = %{ $sitemaps->{$file} }; + my $full_file = $out_dir . $file; + $sm_config{url_prefix} ||= do { + my $metacpan_url = $sm_config{metacpan_url}; + s{/\z}{}, s{\A/}{} for $metacpan_url; + "$web_host/$metacpan_url/"; + }; + $sm_config{api_secure} = $config->{api_secure}; + my $sitemap = MetaCPAN::Sitemap->new(%sm_config); + $sitemap->write($full_file); +} diff --git a/cpanfile b/cpanfile index 08c846c458..687ba237fe 100644 --- a/cpanfile +++ b/cpanfile @@ -1,4 +1,3 @@ -requires 'AnyEvent::Curl::Multi'; requires 'CHI'; requires 'CPAN::Changes', '0.21'; requires 'CPAN::Meta', '2.141520'; # Avoid issues with List::Util dep under carton install. @@ -32,6 +31,7 @@ requires 'Encode', '2.51'; requires 'Exporter'; requires 'File::Path'; requires 'Format::Human::Bytes'; +requires 'Future'; requires 'Getopt::Long::Descriptive'; requires 'Gravatar::URL'; requires 'HTML::Escape'; @@ -43,6 +43,9 @@ requires 'HTTP::Request'; requires 'HTTP::Request::Common'; requires 'Hash::AsObject'; requires 'Hash::Merge'; +requires 'IO::Async::Loop'; +requires 'IO::Async::SSL'; +requires 'IO::Socket::SSL'; requires 'Importer'; requires 'JavaScript::Minifier::XS'; requires 'List::Util', '1.45'; @@ -54,18 +57,16 @@ requires 'Module::Build::Tiny', '0.037'; requires 'Module::Runtime'; requires 'Moo', '2.000002'; requires 'Moose', '2.1605'; -requires 'MooseX::ClassAttribute'; requires 'MooseX::Fastly::Role', '0.03'; requires 'MooseX::Role::Parameterized', '1.02'; -requires 'MooseX::StrictConstructor'; requires 'MooseX::Types::Common::Numeric'; requires 'MooseX::Types::Common::String'; requires 'MooseX::Types::Moose'; requires 'MooseX::Types::URI', '0.08'; +requires 'Net::Async::HTTP'; requires 'Net::Fastly', '1.05'; requires 'Params::ValidationCompiler'; requires 'Path::Tiny', '0.076'; -requires 'PerlIO::gzip'; requires 'Plack', '1.0039'; requires 'Plack::Middleware::ReverseProxy'; requires 'Plack::Middleware::Runtime'; @@ -92,7 +93,6 @@ requires 'Try::Tiny', '0.24'; requires 'URI', '1.71'; requires 'URI::Escape'; requires 'XML::Feed'; -requires 'XML::Simple'; requires 'YAML', '1.15'; # fix dep chain issue test_requires 'App::Prove'; diff --git a/cpanfile.snapshot b/cpanfile.snapshot index f87bf631f5..a5a242fa8e 100644 --- a/cpanfile.snapshot +++ b/cpanfile.snapshot @@ -7,86 +7,6 @@ DISTRIBUTIONS Algorithm::Diff::_impl 1.1903 requirements: ExtUtils::MakeMaker 0 - Any-Moose-0.27 - pathname: E/ET/ETHER/Any-Moose-0.27.tar.gz - provides: - Any::Moose 0.27 - requirements: - Carp 0 - ExtUtils::MakeMaker 0 - Moose 0 - perl 5.006002 - strict 0 - warnings 0 - Any-URI-Escape-0.01 - pathname: P/PH/PHRED/Any-URI-Escape-0.01.tar.gz - provides: - Any::URI::Escape 0.01 - requirements: - ExtUtils::MakeMaker 0 - URI::Escape 0 - AnyEvent-7.13 - pathname: M/ML/MLEHMANN/AnyEvent-7.13.tar.gz - provides: - AE undef - AE::Log::COLLECT undef - AE::Log::FILTER undef - AE::Log::LOG undef - AnyEvent 7.13 - AnyEvent::Base 7.13 - AnyEvent::CondVar 7.13 - AnyEvent::CondVar::Base 7.13 - AnyEvent::DNS undef - AnyEvent::Debug undef - AnyEvent::Debug::Backtrace undef - AnyEvent::Debug::Wrap undef - AnyEvent::Debug::Wrapped undef - AnyEvent::Debug::shell undef - AnyEvent::Handle undef - AnyEvent::IO undef - AnyEvent::IO::IOAIO undef - AnyEvent::IO::Perl undef - AnyEvent::Impl::Cocoa undef - AnyEvent::Impl::EV undef - AnyEvent::Impl::Event undef - AnyEvent::Impl::EventLib undef - AnyEvent::Impl::FLTK undef - AnyEvent::Impl::Glib undef - AnyEvent::Impl::IOAsync undef - AnyEvent::Impl::Irssi undef - AnyEvent::Impl::POE undef - AnyEvent::Impl::Perl undef - AnyEvent::Impl::Qt undef - AnyEvent::Impl::Qt::Io undef - AnyEvent::Impl::Qt::Timer undef - AnyEvent::Impl::Tk undef - AnyEvent::Impl::UV undef - AnyEvent::Log undef - AnyEvent::Log::COLLECT undef - AnyEvent::Log::Ctx undef - AnyEvent::Log::FILTER undef - AnyEvent::Log::LOG undef - AnyEvent::Loop undef - AnyEvent::Socket undef - AnyEvent::Strict undef - AnyEvent::TLS undef - AnyEvent::Util undef - requirements: - Canary::Stability 0 - ExtUtils::MakeMaker 6.52 - AnyEvent-Curl-Multi-1.1 - pathname: O/OT/OTTERLEY/AnyEvent-Curl-Multi-1.1.tar.gz - provides: - AnyEvent::Curl::Multi 1.1 - AnyEvent::Curl::Multi::Handle 1.1 - requirements: - AnyEvent 5 - ExtUtils::MakeMaker 0 - HTTP::Request 0 - HTTP::Response 0 - Object::Event 1 - Test::More 0 - WWW::Curl 4.14 Apache-LogFormat-Compiler-0.35 pathname: K/KA/KAZEBURO/Apache-LogFormat-Compiler-0.35.tar.gz provides: @@ -294,14 +214,6 @@ DISTRIBUTIONS requirements: ExtUtils::MakeMaker 0 Test::Exception 0 - Carp-Clan-Share-0.013 - pathname: R/RK/RKRIMEN/Carp-Clan-Share-0.013.tar.gz - provides: - Carp::Clan::Share 0.013 - requirements: - Carp::Clan 0 - ExtUtils::MakeMaker 6.42 - Test::More 0 Catalyst-Action-REST-1.20 pathname: J/JJ/JJNAPIORK/Catalyst-Action-REST-1.20.tar.gz provides: @@ -745,17 +657,6 @@ DISTRIBUTIONS requirements: ExtUtils::MakeMaker 0 Test::More 0 - Clone-PP-1.07 - pathname: N/NE/NEILB/Clone-PP-1.07.tar.gz - provides: - Clone::PP 1.07 - requirements: - Exporter 0 - ExtUtils::MakeMaker 0 - perl 5.006 - strict 0 - vars 0 - warnings 0 Code-TidyAll-0.59 pathname: D/DR/DROLSKY/Code-TidyAll-0.59.tar.gz provides: @@ -860,6 +761,7 @@ DISTRIBUTIONS Config::Any::XML undef Config::Any::YAML undef requirements: + Config::General 2.47 Module::Pluggable::Object 3.6 Config-General-2.63 pathname: T/TL/TLINDEN/Config-General-2.63.tar.gz @@ -886,26 +788,6 @@ DISTRIBUTIONS Mixin::Linewise::Writers 0 strict 0 warnings 0 - Config-JFDI-0.065 - pathname: R/RO/ROKR/Config-JFDI-0.065.tar.gz - provides: - Config::JFDI 0.065 - Config::JFDI::Carp undef - Config::JFDI::Source::Loader undef - requirements: - Any::Moose 0 - Carp::Clan::Share 0 - Clone 0 - Config::Any 0 - Config::General 0 - Data::Visitor 0.24 - ExtUtils::MakeMaker 6.31 - Getopt::Usaginator 0 - Hash::Merge::Simple 0 - List::MoreUtils 0 - Path::Class 0 - Sub::Install 0 - Test::Most 0 Config-Tiny-2.23 pathname: R/RS/RSAVAGE/Config-Tiny-2.23.tgz provides: @@ -988,29 +870,6 @@ DISTRIBUTIONS Data::Page 2 ExtUtils::MakeMaker 0 Test::More 0.1 - Data-Printer-0.39 - pathname: G/GA/GARU/Data-Printer-0.39.tar.gz - provides: - DDP undef - Data::Printer 0.39 - Data::Printer::Filter undef - Data::Printer::Filter::DB undef - Data::Printer::Filter::DateTime undef - Data::Printer::Filter::Digest undef - requirements: - Carp 0 - Clone::PP 0 - ExtUtils::MakeMaker 0 - Fcntl 0 - File::HomeDir 0.91 - File::Spec 0 - File::Temp 0 - Package::Stash 0.3 - Scalar::Util 0 - Sort::Naturally 0 - Term::ANSIColor 3 - Test::More 0.88 - version 0.77 Data-UUID-1.221 pathname: R/RJ/RJBS/Data-UUID-1.221.tar.gz provides: @@ -1571,19 +1430,6 @@ DISTRIBUTIONS perl 5.008004 strict 0 warnings 0 - Devel-CheckCompiler-0.07 - pathname: S/SY/SYOHEX/Devel-CheckCompiler-0.07.tar.gz - provides: - Devel::AssertC99 undef - Devel::CheckCompiler 0.07 - requirements: - Exporter 0 - ExtUtils::CBuilder 0 - File::Temp 0 - Module::Build::Tiny 0.035 - Test::More 0.98 - parent 0 - perl 5.008001 Devel-Confess-0.009004 pathname: H/HA/HAARG/Devel-Confess-0.009004.tar.gz provides: @@ -1917,6 +1763,18 @@ DISTRIBUTIONS requirements: ExtUtils::MakeMaker 6.42 Test::More 0 + Future-0.34 + pathname: P/PE/PEVANS/Future-0.34.tar.gz + provides: + Future 0.34 + Future::Mutex 0.34 + Future::Utils 0.34 + Test::Future 0.34 + requirements: + Carp 1.25 + Test::Builder::Module 0 + Time::HiRes 0 + perl 5.008 Getopt-Long-Descriptive-0.100 pathname: R/RJ/RJBS/Getopt-Long-Descriptive-0.100.tar.gz provides: @@ -1936,16 +1794,6 @@ DISTRIBUTIONS overload 0 strict 0 warnings 0 - Getopt-Usaginator-0.0012 - pathname: R/RO/ROKR/Getopt-Usaginator-0.0012.tar.gz - provides: - Getopt::Usaginator 0.0012 - requirements: - ExtUtils::MakeMaker 6.31 - File::Spec 0 - IPC::Open3 0 - Package::Pkg 0.0014 - Test::Most 0 Gravatar-URL-1.07 pathname: M/MS/MSCHWERN/Gravatar-URL-1.07.tar.gz provides: @@ -2300,6 +2148,69 @@ DISTRIBUTIONS perl 5.006 strict 0 warnings 0 + IO-Async-0.71 + pathname: P/PE/PEVANS/IO-Async-0.71.tar.gz + provides: + IO::Async 0.71 + IO::Async::Channel 0.71 + IO::Async::ChildManager 0.71 + IO::Async::Debug 0.71 + IO::Async::File 0.71 + IO::Async::FileStream 0.71 + IO::Async::Function 0.71 + IO::Async::Future 0.71 + IO::Async::Handle 0.71 + IO::Async::Listener 0.71 + IO::Async::Loop 0.71 + IO::Async::Loop::Poll 0.71 + IO::Async::Loop::Select 0.71 + IO::Async::LoopTests 0.71 + IO::Async::Notifier 0.71 + IO::Async::OS 0.71 + IO::Async::OS::MSWin32 0.71 + IO::Async::OS::cygwin 0.71 + IO::Async::OS::linux 0.71 + IO::Async::PID 0.71 + IO::Async::Process 0.71 + IO::Async::Protocol 0.71 + IO::Async::Protocol::LineStream 0.71 + IO::Async::Protocol::Stream 0.71 + IO::Async::Resolver 0.71 + IO::Async::Routine 0.71 + IO::Async::Signal 0.71 + IO::Async::Socket 0.71 + IO::Async::Stream 0.71 + IO::Async::Test 0.71 + IO::Async::Timer 0.71 + IO::Async::Timer::Absolute 0.71 + IO::Async::Timer::Countdown 0.71 + IO::Async::Timer::Periodic 0.71 + requirements: + Exporter 5.57 + File::stat 0 + Future 0.33 + Future::Utils 0.18 + IO::Poll 0 + Module::Build 0.4004 + Socket 2.007 + Storable 0 + Struct::Dumb 0 + Time::HiRes 0 + perl 5.008004 + IO-Async-SSL-0.19 + pathname: P/PE/PEVANS/IO-Async-SSL-0.19.tar.gz + provides: + IO::Async::SSL 0.19 + IO::Async::SSLStream 0.19 + requirements: + Future 0.33 + IO::Async::Handle 0.29 + IO::Async::Loop 0.66 + IO::Async::OS 0 + IO::Async::Protocol::Stream 0 + IO::Async::Stream 0.59 + IO::Socket::SSL 1.968 + Test::More 0.88 IO-HTML-1.001 pathname: C/CJ/CJM/IO-HTML-1.001.tar.gz provides: @@ -2402,7 +2313,6 @@ DISTRIBUTIONS JSON::MaybeXS 1.003009 requirements: Carp 0 - Cpanel::JSON::XS 2.3310 ExtUtils::CBuilder 0.27 ExtUtils::MakeMaker 0 File::Spec 0 @@ -2453,19 +2363,6 @@ DISTRIBUTIONS requirements: ExtUtils::MakeMaker 0 Test::More 0 - List-AllUtils-0.14 - pathname: D/DR/DROLSKY/List-AllUtils-0.14.tar.gz - provides: - List::AllUtils 0.14 - requirements: - Exporter 0 - ExtUtils::MakeMaker 0 - List::SomeUtils 0.50 - List::Util 1.45 - List::UtilsBy 0.10 - base 0 - strict 0 - warnings 0 List-Compare-0.53 pathname: J/JK/JKEENAN/List-Compare-0.53.tar.gz provides: @@ -2512,13 +2409,6 @@ DISTRIBUTIONS perl 5.006 strict 0 warnings 0 - List-UtilsBy-0.10 - pathname: P/PE/PEVANS/List-UtilsBy-0.10.tar.gz - provides: - List::UtilsBy 0.10 - requirements: - Exporter 5.57 - Module::Build 0.4004 Log-Any-1.049 pathname: P/PR/PREACTION/Log-Any-1.049.tar.gz provides: @@ -2825,21 +2715,6 @@ DISTRIBUTIONS perl 5.006 strict 0 warnings 0 - Module-Build-XSUtil-0.16 - pathname: H/HI/HIDEAKIO/Module-Build-XSUtil-0.16.tar.gz - provides: - Module::Build::XSUtil 0.16 - requirements: - Devel::CheckCompiler 0 - Devel::PPPort 0 - Exporter 0 - ExtUtils::CBuilder 0 - File::Basename 0 - File::Path 0 - Module::Build 0.4005 - XSLoader 0 - parent 0 - perl 5.008005 Module-Implementation-0.09 pathname: D/DR/DROLSKY/Module-Implementation-0.09.tar.gz provides: @@ -3314,33 +3189,6 @@ DISTRIBUTIONS parent 0.223 strict 1.03 warnings 1.03 - MooseX-ClassAttribute-0.29 - pathname: D/DR/DROLSKY/MooseX-ClassAttribute-0.29.tar.gz - provides: - MooseX::ClassAttribute 0.29 - MooseX::ClassAttribute::Meta::Role::Attribute 0.29 - MooseX::ClassAttribute::Trait::Application 0.29 - MooseX::ClassAttribute::Trait::Application::ToClass 0.29 - MooseX::ClassAttribute::Trait::Application::ToRole 0.29 - MooseX::ClassAttribute::Trait::Attribute 0.29 - MooseX::ClassAttribute::Trait::Class 0.29 - MooseX::ClassAttribute::Trait::Mixin::HasClassAttributes 0.29 - MooseX::ClassAttribute::Trait::Role 0.29 - MooseX::ClassAttribute::Trait::Role::Composite 0.29 - requirements: - ExtUtils::MakeMaker 0 - List::Util 1.45 - Moose 2.00 - Moose::Exporter 0 - Moose::Meta::Role::Attribute 0 - Moose::Role 0 - Moose::Util 0 - Moose::Util::MetaRole 0 - Scalar::Util 0 - namespace::autoclean 0.11 - namespace::clean 0.20 - strict 0 - warnings 0 MooseX-Emulate-Class-Accessor-Fast-0.00903 pathname: F/FL/FLORA/MooseX-Emulate-Class-Accessor-Fast-0.00903.tar.gz provides: @@ -3551,45 +3399,6 @@ DISTRIBUTIONS perl 5.006 strict 0 warnings 0 - Mouse-v2.4.9 - pathname: S/SY/SYOHEX/Mouse-v2.4.9.tar.gz - provides: - Mouse v2.4.9 - Mouse::Exporter undef - Mouse::Meta::Attribute undef - Mouse::Meta::Class undef - Mouse::Meta::Method undef - Mouse::Meta::Method::Accessor undef - Mouse::Meta::Method::Constructor undef - Mouse::Meta::Method::Delegation undef - Mouse::Meta::Method::Destructor undef - Mouse::Meta::Module undef - Mouse::Meta::Role undef - Mouse::Meta::Role::Application undef - Mouse::Meta::Role::Application::RoleSummation undef - Mouse::Meta::Role::Composite undef - Mouse::Meta::Role::Method undef - Mouse::Meta::TypeConstraint undef - Mouse::Object undef - Mouse::PurePerl undef - Mouse::Role v2.4.9 - Mouse::Spec v2.4.9 - Mouse::Tiny v2.4.8 - Mouse::TypeRegistry undef - Mouse::Util v2.4.9 - Mouse::Util::MetaRole undef - Mouse::Util::TypeConstraints undef - Squirrel undef - Squirrel::Role undef - Test::Mouse undef - ouse undef - requirements: - ExtUtils::CBuilder 0 - Module::Build 0.4005 - Module::Build::XSUtil 0.16 - Scalar::Util 1.14 - XSLoader 0.02 - perl 5.008005 Mozilla-CA-20160104 pathname: A/AB/ABH/Mozilla-CA-20160104.tar.gz provides: @@ -3598,6 +3407,29 @@ DISTRIBUTIONS ExtUtils::MakeMaker 0 Test 0 perl 5.006 + Net-Async-HTTP-0.41 + pathname: P/PE/PEVANS/Net-Async-HTTP-0.41.tar.gz + provides: + Net::Async::HTTP 0.41 + Net::Async::HTTP::Connection 0.41 + Net::Async::HTTP::StallTimer 0.41 + requirements: + Future 0.28 + Future::Utils 0.16 + HTTP::Cookies 0 + HTTP::Request 0 + HTTP::Request::Common 0 + HTTP::Response 0 + IO::Async::Loop 0.59 + IO::Async::Stream 0.59 + IO::Async::Test 0 + IO::Async::Timer::Countdown 0 + List::Util 1.29 + Struct::Dumb 0.07 + Test::Identity 0 + Test::More 0.88 + Test::Refcount 0 + URI 0 Net-CIDR-Lite-0.21 pathname: D/DO/DOUGW/Net-CIDR-Lite-0.21.tar.gz provides: @@ -3833,15 +3665,6 @@ DISTRIBUTIONS Carp 0 ExtUtils::MakeMaker 0 POSIX 0 - Object-Event-1.23 - pathname: E/EL/ELMEX/Object-Event-1.23.tar.gz - provides: - Object::Event 1.23 - requirements: - AnyEvent 3.5 - ExtUtils::MakeMaker 0 - Test::More 0 - common::sense 0 Object-Signature-1.07 pathname: A/AD/ADAMK/Object-Signature-1.07.tar.gz provides: @@ -4095,20 +3918,6 @@ DISTRIBUTIONS Sub::Name 0 strict 0 warnings 0 - Package-Pkg-0.0020 - pathname: R/RO/ROKR/Package-Pkg-0.0020.tar.gz - provides: - Package::Pkg 0.0020 - Package::Pkg::Lexicon undef - Package::Pkg::Loader undef - requirements: - Class::Load 0 - Clone 0 - ExtUtils::MakeMaker 6.30 - Mouse 0 - Sub::Install 0 - Test::Most 0 - Try::Tiny 0 Package-Stash-0.37 pathname: D/DO/DOY/Package-Stash-0.37.tar.gz provides: @@ -4271,204 +4080,204 @@ DISTRIBUTIONS perl 5.008001 strict 0 warnings 0 - Perl-Critic-1.126 - pathname: T/TH/THALJEF/Perl-Critic-1.126.tar.gz - provides: - Perl::Critic 1.126 - Perl::Critic::Annotation 1.126 - Perl::Critic::Command 1.126 - Perl::Critic::Config 1.126 - Perl::Critic::Document 1.126 - Perl::Critic::Exception 1.126 - Perl::Critic::Exception::AggregateConfiguration 1.126 - Perl::Critic::Exception::Configuration 1.126 - Perl::Critic::Exception::Configuration::Generic 1.126 - Perl::Critic::Exception::Configuration::NonExistentPolicy 1.126 - Perl::Critic::Exception::Configuration::Option 1.126 - Perl::Critic::Exception::Configuration::Option::Global 1.126 - Perl::Critic::Exception::Configuration::Option::Global::ExtraParameter 1.126 - Perl::Critic::Exception::Configuration::Option::Global::ParameterValue 1.126 - Perl::Critic::Exception::Configuration::Option::Policy 1.126 - Perl::Critic::Exception::Configuration::Option::Policy::ExtraParameter 1.126 - Perl::Critic::Exception::Configuration::Option::Policy::ParameterValue 1.126 - Perl::Critic::Exception::Fatal 1.126 - Perl::Critic::Exception::Fatal::Generic 1.126 - Perl::Critic::Exception::Fatal::Internal 1.126 - Perl::Critic::Exception::Fatal::PolicyDefinition 1.126 - Perl::Critic::Exception::IO 1.126 - Perl::Critic::Exception::Parse 1.126 - Perl::Critic::OptionsProcessor 1.126 - Perl::Critic::Policy 1.126 - Perl::Critic::Policy::BuiltinFunctions::ProhibitBooleanGrep 1.126 - Perl::Critic::Policy::BuiltinFunctions::ProhibitComplexMappings 1.126 - Perl::Critic::Policy::BuiltinFunctions::ProhibitLvalueSubstr 1.126 - Perl::Critic::Policy::BuiltinFunctions::ProhibitReverseSortBlock 1.126 - Perl::Critic::Policy::BuiltinFunctions::ProhibitSleepViaSelect 1.126 - Perl::Critic::Policy::BuiltinFunctions::ProhibitStringyEval 1.126 - Perl::Critic::Policy::BuiltinFunctions::ProhibitStringySplit 1.126 - Perl::Critic::Policy::BuiltinFunctions::ProhibitUniversalCan 1.126 - Perl::Critic::Policy::BuiltinFunctions::ProhibitUniversalIsa 1.126 - Perl::Critic::Policy::BuiltinFunctions::ProhibitUselessTopic 1.126 - Perl::Critic::Policy::BuiltinFunctions::ProhibitVoidGrep 1.126 - Perl::Critic::Policy::BuiltinFunctions::ProhibitVoidMap 1.126 - Perl::Critic::Policy::BuiltinFunctions::RequireBlockGrep 1.126 - Perl::Critic::Policy::BuiltinFunctions::RequireBlockMap 1.126 - Perl::Critic::Policy::BuiltinFunctions::RequireGlobFunction 1.126 - Perl::Critic::Policy::BuiltinFunctions::RequireSimpleSortBlock 1.126 - Perl::Critic::Policy::ClassHierarchies::ProhibitAutoloading 1.126 - Perl::Critic::Policy::ClassHierarchies::ProhibitExplicitISA 1.126 - Perl::Critic::Policy::ClassHierarchies::ProhibitOneArgBless 1.126 - Perl::Critic::Policy::CodeLayout::ProhibitHardTabs 1.126 - Perl::Critic::Policy::CodeLayout::ProhibitParensWithBuiltins 1.126 - Perl::Critic::Policy::CodeLayout::ProhibitQuotedWordLists 1.126 - Perl::Critic::Policy::CodeLayout::ProhibitTrailingWhitespace 1.126 - Perl::Critic::Policy::CodeLayout::RequireConsistentNewlines 1.126 - Perl::Critic::Policy::CodeLayout::RequireTidyCode 1.126 - Perl::Critic::Policy::CodeLayout::RequireTrailingCommas 1.126 - Perl::Critic::Policy::ControlStructures::ProhibitCStyleForLoops 1.126 - Perl::Critic::Policy::ControlStructures::ProhibitCascadingIfElse 1.126 - Perl::Critic::Policy::ControlStructures::ProhibitDeepNests 1.126 - Perl::Critic::Policy::ControlStructures::ProhibitLabelsWithSpecialBlockNames 1.126 - Perl::Critic::Policy::ControlStructures::ProhibitMutatingListFunctions 1.126 - Perl::Critic::Policy::ControlStructures::ProhibitNegativeExpressionsInUnlessAndUntilConditions 1.126 - Perl::Critic::Policy::ControlStructures::ProhibitPostfixControls 1.126 - Perl::Critic::Policy::ControlStructures::ProhibitUnlessBlocks 1.126 - Perl::Critic::Policy::ControlStructures::ProhibitUnreachableCode 1.126 - Perl::Critic::Policy::ControlStructures::ProhibitUntilBlocks 1.126 - Perl::Critic::Policy::ControlStructures::ProhibitYadaOperator 1.126 - Perl::Critic::Policy::Documentation::PodSpelling 1.126 - Perl::Critic::Policy::Documentation::RequirePackageMatchesPodName 1.126 - Perl::Critic::Policy::Documentation::RequirePodAtEnd 1.126 - Perl::Critic::Policy::Documentation::RequirePodLinksIncludeText 1.126 - Perl::Critic::Policy::Documentation::RequirePodSections 1.126 - Perl::Critic::Policy::ErrorHandling::RequireCarping 1.126 - Perl::Critic::Policy::ErrorHandling::RequireCheckingReturnValueOfEval 1.126 - Perl::Critic::Policy::InputOutput::ProhibitBacktickOperators 1.126 - Perl::Critic::Policy::InputOutput::ProhibitBarewordFileHandles 1.126 - Perl::Critic::Policy::InputOutput::ProhibitExplicitStdin 1.126 - Perl::Critic::Policy::InputOutput::ProhibitInteractiveTest 1.126 - Perl::Critic::Policy::InputOutput::ProhibitJoinedReadline 1.126 - Perl::Critic::Policy::InputOutput::ProhibitOneArgSelect 1.126 - Perl::Critic::Policy::InputOutput::ProhibitReadlineInForLoop 1.126 - Perl::Critic::Policy::InputOutput::ProhibitTwoArgOpen 1.126 - Perl::Critic::Policy::InputOutput::RequireBracedFileHandleWithPrint 1.126 - Perl::Critic::Policy::InputOutput::RequireBriefOpen 1.126 - Perl::Critic::Policy::InputOutput::RequireCheckedClose 1.126 - Perl::Critic::Policy::InputOutput::RequireCheckedOpen 1.126 - Perl::Critic::Policy::InputOutput::RequireCheckedSyscalls 1.126 - Perl::Critic::Policy::InputOutput::RequireEncodingWithUTF8Layer 1.126 - Perl::Critic::Policy::Miscellanea::ProhibitFormats 1.126 - Perl::Critic::Policy::Miscellanea::ProhibitTies 1.126 - Perl::Critic::Policy::Miscellanea::ProhibitUnrestrictedNoCritic 1.126 - Perl::Critic::Policy::Miscellanea::ProhibitUselessNoCritic 1.126 - Perl::Critic::Policy::Modules::ProhibitAutomaticExportation 1.126 - Perl::Critic::Policy::Modules::ProhibitConditionalUseStatements 1.126 - Perl::Critic::Policy::Modules::ProhibitEvilModules 1.126 - Perl::Critic::Policy::Modules::ProhibitExcessMainComplexity 1.126 - Perl::Critic::Policy::Modules::ProhibitMultiplePackages 1.126 - Perl::Critic::Policy::Modules::RequireBarewordIncludes 1.126 - Perl::Critic::Policy::Modules::RequireEndWithOne 1.126 - Perl::Critic::Policy::Modules::RequireExplicitPackage 1.126 - Perl::Critic::Policy::Modules::RequireFilenameMatchesPackage 1.126 - Perl::Critic::Policy::Modules::RequireNoMatchVarsWithUseEnglish 1.126 - Perl::Critic::Policy::Modules::RequireVersionVar 1.126 - Perl::Critic::Policy::NamingConventions::Capitalization 1.126 - Perl::Critic::Policy::NamingConventions::ProhibitAmbiguousNames 1.126 - Perl::Critic::Policy::Objects::ProhibitIndirectSyntax 1.126 - Perl::Critic::Policy::References::ProhibitDoubleSigils 1.126 - Perl::Critic::Policy::RegularExpressions::ProhibitCaptureWithoutTest 1.126 - Perl::Critic::Policy::RegularExpressions::ProhibitComplexRegexes 1.126 - Perl::Critic::Policy::RegularExpressions::ProhibitEnumeratedClasses 1.126 - Perl::Critic::Policy::RegularExpressions::ProhibitEscapedMetacharacters 1.126 - Perl::Critic::Policy::RegularExpressions::ProhibitFixedStringMatches 1.126 - Perl::Critic::Policy::RegularExpressions::ProhibitSingleCharAlternation 1.126 - Perl::Critic::Policy::RegularExpressions::ProhibitUnusedCapture 1.126 - Perl::Critic::Policy::RegularExpressions::ProhibitUnusualDelimiters 1.126 - Perl::Critic::Policy::RegularExpressions::ProhibitUselessTopic 1.126 - Perl::Critic::Policy::RegularExpressions::RequireBracesForMultiline 1.126 - Perl::Critic::Policy::RegularExpressions::RequireDotMatchAnything 1.126 - Perl::Critic::Policy::RegularExpressions::RequireExtendedFormatting 1.126 - Perl::Critic::Policy::RegularExpressions::RequireLineBoundaryMatching 1.126 - Perl::Critic::Policy::Subroutines::ProhibitAmpersandSigils 1.126 - Perl::Critic::Policy::Subroutines::ProhibitBuiltinHomonyms 1.126 - Perl::Critic::Policy::Subroutines::ProhibitExcessComplexity 1.126 - Perl::Critic::Policy::Subroutines::ProhibitExplicitReturnUndef 1.126 - Perl::Critic::Policy::Subroutines::ProhibitManyArgs 1.126 - Perl::Critic::Policy::Subroutines::ProhibitNestedSubs 1.126 - Perl::Critic::Policy::Subroutines::ProhibitReturnSort 1.126 - Perl::Critic::Policy::Subroutines::ProhibitSubroutinePrototypes 1.126 - Perl::Critic::Policy::Subroutines::ProhibitUnusedPrivateSubroutines 1.126 - Perl::Critic::Policy::Subroutines::ProtectPrivateSubs 1.126 - Perl::Critic::Policy::Subroutines::RequireArgUnpacking 1.126 - Perl::Critic::Policy::Subroutines::RequireFinalReturn 1.126 - Perl::Critic::Policy::TestingAndDebugging::ProhibitNoStrict 1.126 - Perl::Critic::Policy::TestingAndDebugging::ProhibitNoWarnings 1.126 - Perl::Critic::Policy::TestingAndDebugging::ProhibitProlongedStrictureOverride 1.126 - Perl::Critic::Policy::TestingAndDebugging::RequireTestLabels 1.126 - Perl::Critic::Policy::TestingAndDebugging::RequireUseStrict 1.126 - Perl::Critic::Policy::TestingAndDebugging::RequireUseWarnings 1.126 - Perl::Critic::Policy::ValuesAndExpressions::ProhibitCommaSeparatedStatements 1.126 - Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexVersion 1.126 - Perl::Critic::Policy::ValuesAndExpressions::ProhibitConstantPragma 1.126 - Perl::Critic::Policy::ValuesAndExpressions::ProhibitEmptyQuotes 1.126 - Perl::Critic::Policy::ValuesAndExpressions::ProhibitEscapedCharacters 1.126 - Perl::Critic::Policy::ValuesAndExpressions::ProhibitImplicitNewlines 1.126 - Perl::Critic::Policy::ValuesAndExpressions::ProhibitInterpolationOfLiterals 1.126 - Perl::Critic::Policy::ValuesAndExpressions::ProhibitLeadingZeros 1.126 - Perl::Critic::Policy::ValuesAndExpressions::ProhibitLongChainsOfMethodCalls 1.126 - Perl::Critic::Policy::ValuesAndExpressions::ProhibitMagicNumbers 1.126 - Perl::Critic::Policy::ValuesAndExpressions::ProhibitMismatchedOperators 1.126 - Perl::Critic::Policy::ValuesAndExpressions::ProhibitMixedBooleanOperators 1.126 - Perl::Critic::Policy::ValuesAndExpressions::ProhibitNoisyQuotes 1.126 - Perl::Critic::Policy::ValuesAndExpressions::ProhibitQuotesAsQuotelikeOperatorDelimiters 1.126 - Perl::Critic::Policy::ValuesAndExpressions::ProhibitSpecialLiteralHeredocTerminator 1.126 - Perl::Critic::Policy::ValuesAndExpressions::ProhibitVersionStrings 1.126 - Perl::Critic::Policy::ValuesAndExpressions::RequireConstantVersion 1.126 - Perl::Critic::Policy::ValuesAndExpressions::RequireInterpolationOfMetachars 1.126 - Perl::Critic::Policy::ValuesAndExpressions::RequireNumberSeparators 1.126 - Perl::Critic::Policy::ValuesAndExpressions::RequireQuotedHeredocTerminator 1.126 - Perl::Critic::Policy::ValuesAndExpressions::RequireUpperCaseHeredocTerminator 1.126 - Perl::Critic::Policy::Variables::ProhibitAugmentedAssignmentInDeclaration 1.126 - Perl::Critic::Policy::Variables::ProhibitConditionalDeclarations 1.126 - Perl::Critic::Policy::Variables::ProhibitEvilVariables 1.126 - Perl::Critic::Policy::Variables::ProhibitLocalVars 1.126 - Perl::Critic::Policy::Variables::ProhibitMatchVars 1.126 - Perl::Critic::Policy::Variables::ProhibitPackageVars 1.126 - Perl::Critic::Policy::Variables::ProhibitPerl4PackageNames 1.126 - Perl::Critic::Policy::Variables::ProhibitPunctuationVars 1.126 - Perl::Critic::Policy::Variables::ProhibitReusedNames 1.126 - Perl::Critic::Policy::Variables::ProhibitUnusedVariables 1.126 - Perl::Critic::Policy::Variables::ProtectPrivateVars 1.126 - Perl::Critic::Policy::Variables::RequireInitializationForLocalVars 1.126 - Perl::Critic::Policy::Variables::RequireLexicalLoopIterators 1.126 - Perl::Critic::Policy::Variables::RequireLocalizedPunctuationVars 1.126 - Perl::Critic::Policy::Variables::RequireNegativeIndices 1.126 - Perl::Critic::PolicyConfig 1.126 - Perl::Critic::PolicyFactory 1.126 - Perl::Critic::PolicyListing 1.126 - Perl::Critic::PolicyParameter 1.126 - Perl::Critic::PolicyParameter::Behavior 1.126 - Perl::Critic::PolicyParameter::Behavior::Boolean 1.126 - Perl::Critic::PolicyParameter::Behavior::Enumeration 1.126 - Perl::Critic::PolicyParameter::Behavior::Integer 1.126 - Perl::Critic::PolicyParameter::Behavior::String 1.126 - Perl::Critic::PolicyParameter::Behavior::StringList 1.126 - Perl::Critic::ProfilePrototype 1.126 - Perl::Critic::Statistics 1.126 - Perl::Critic::TestUtils 1.126 - Perl::Critic::Theme 1.126 - Perl::Critic::ThemeListing 1.126 - Perl::Critic::UserProfile 1.126 - Perl::Critic::Utils 1.126 - Perl::Critic::Utils::Constants 1.126 - Perl::Critic::Utils::DataConversion 1.126 - Perl::Critic::Utils::McCabe 1.126 - Perl::Critic::Utils::POD 1.126 - Perl::Critic::Utils::POD::ParseInteriorSequence 1.126 - Perl::Critic::Utils::PPI 1.126 - Perl::Critic::Utils::Perl 1.126 - Perl::Critic::Violation 1.126 - Test::Perl::Critic::Policy 1.126 + Perl-Critic-1.128 + pathname: P/PE/PETDANCE/Perl-Critic-1.128.tar.gz + provides: + Perl::Critic 1.128 + Perl::Critic::Annotation 1.128 + Perl::Critic::Command 1.128 + Perl::Critic::Config 1.128 + Perl::Critic::Document 1.128 + Perl::Critic::Exception 1.128 + Perl::Critic::Exception::AggregateConfiguration 1.128 + Perl::Critic::Exception::Configuration 1.128 + Perl::Critic::Exception::Configuration::Generic 1.128 + Perl::Critic::Exception::Configuration::NonExistentPolicy 1.128 + Perl::Critic::Exception::Configuration::Option 1.128 + Perl::Critic::Exception::Configuration::Option::Global 1.128 + Perl::Critic::Exception::Configuration::Option::Global::ExtraParameter 1.128 + Perl::Critic::Exception::Configuration::Option::Global::ParameterValue 1.128 + Perl::Critic::Exception::Configuration::Option::Policy 1.128 + Perl::Critic::Exception::Configuration::Option::Policy::ExtraParameter 1.128 + Perl::Critic::Exception::Configuration::Option::Policy::ParameterValue 1.128 + Perl::Critic::Exception::Fatal 1.128 + Perl::Critic::Exception::Fatal::Generic 1.128 + Perl::Critic::Exception::Fatal::Internal 1.128 + Perl::Critic::Exception::Fatal::PolicyDefinition 1.128 + Perl::Critic::Exception::IO 1.128 + Perl::Critic::Exception::Parse 1.128 + Perl::Critic::OptionsProcessor 1.128 + Perl::Critic::Policy 1.128 + Perl::Critic::Policy::BuiltinFunctions::ProhibitBooleanGrep 1.128 + Perl::Critic::Policy::BuiltinFunctions::ProhibitComplexMappings 1.128 + Perl::Critic::Policy::BuiltinFunctions::ProhibitLvalueSubstr 1.128 + Perl::Critic::Policy::BuiltinFunctions::ProhibitReverseSortBlock 1.128 + Perl::Critic::Policy::BuiltinFunctions::ProhibitSleepViaSelect 1.128 + Perl::Critic::Policy::BuiltinFunctions::ProhibitStringyEval 1.128 + Perl::Critic::Policy::BuiltinFunctions::ProhibitStringySplit 1.128 + Perl::Critic::Policy::BuiltinFunctions::ProhibitUniversalCan 1.128 + Perl::Critic::Policy::BuiltinFunctions::ProhibitUniversalIsa 1.128 + Perl::Critic::Policy::BuiltinFunctions::ProhibitUselessTopic 1.128 + Perl::Critic::Policy::BuiltinFunctions::ProhibitVoidGrep 1.128 + Perl::Critic::Policy::BuiltinFunctions::ProhibitVoidMap 1.128 + Perl::Critic::Policy::BuiltinFunctions::RequireBlockGrep 1.128 + Perl::Critic::Policy::BuiltinFunctions::RequireBlockMap 1.128 + Perl::Critic::Policy::BuiltinFunctions::RequireGlobFunction 1.128 + Perl::Critic::Policy::BuiltinFunctions::RequireSimpleSortBlock 1.128 + Perl::Critic::Policy::ClassHierarchies::ProhibitAutoloading 1.128 + Perl::Critic::Policy::ClassHierarchies::ProhibitExplicitISA 1.128 + Perl::Critic::Policy::ClassHierarchies::ProhibitOneArgBless 1.128 + Perl::Critic::Policy::CodeLayout::ProhibitHardTabs 1.128 + Perl::Critic::Policy::CodeLayout::ProhibitParensWithBuiltins 1.128 + Perl::Critic::Policy::CodeLayout::ProhibitQuotedWordLists 1.128 + Perl::Critic::Policy::CodeLayout::ProhibitTrailingWhitespace 1.128 + Perl::Critic::Policy::CodeLayout::RequireConsistentNewlines 1.128 + Perl::Critic::Policy::CodeLayout::RequireTidyCode 1.128 + Perl::Critic::Policy::CodeLayout::RequireTrailingCommas 1.128 + Perl::Critic::Policy::ControlStructures::ProhibitCStyleForLoops 1.128 + Perl::Critic::Policy::ControlStructures::ProhibitCascadingIfElse 1.128 + Perl::Critic::Policy::ControlStructures::ProhibitDeepNests 1.128 + Perl::Critic::Policy::ControlStructures::ProhibitLabelsWithSpecialBlockNames 1.128 + Perl::Critic::Policy::ControlStructures::ProhibitMutatingListFunctions 1.128 + Perl::Critic::Policy::ControlStructures::ProhibitNegativeExpressionsInUnlessAndUntilConditions 1.128 + Perl::Critic::Policy::ControlStructures::ProhibitPostfixControls 1.128 + Perl::Critic::Policy::ControlStructures::ProhibitUnlessBlocks 1.128 + Perl::Critic::Policy::ControlStructures::ProhibitUnreachableCode 1.128 + Perl::Critic::Policy::ControlStructures::ProhibitUntilBlocks 1.128 + Perl::Critic::Policy::ControlStructures::ProhibitYadaOperator 1.128 + Perl::Critic::Policy::Documentation::PodSpelling 1.128 + Perl::Critic::Policy::Documentation::RequirePackageMatchesPodName 1.128 + Perl::Critic::Policy::Documentation::RequirePodAtEnd 1.128 + Perl::Critic::Policy::Documentation::RequirePodLinksIncludeText 1.128 + Perl::Critic::Policy::Documentation::RequirePodSections 1.128 + Perl::Critic::Policy::ErrorHandling::RequireCarping 1.128 + Perl::Critic::Policy::ErrorHandling::RequireCheckingReturnValueOfEval 1.128 + Perl::Critic::Policy::InputOutput::ProhibitBacktickOperators 1.128 + Perl::Critic::Policy::InputOutput::ProhibitBarewordFileHandles 1.128 + Perl::Critic::Policy::InputOutput::ProhibitExplicitStdin 1.128 + Perl::Critic::Policy::InputOutput::ProhibitInteractiveTest 1.128 + Perl::Critic::Policy::InputOutput::ProhibitJoinedReadline 1.128 + Perl::Critic::Policy::InputOutput::ProhibitOneArgSelect 1.128 + Perl::Critic::Policy::InputOutput::ProhibitReadlineInForLoop 1.128 + Perl::Critic::Policy::InputOutput::ProhibitTwoArgOpen 1.128 + Perl::Critic::Policy::InputOutput::RequireBracedFileHandleWithPrint 1.128 + Perl::Critic::Policy::InputOutput::RequireBriefOpen 1.128 + Perl::Critic::Policy::InputOutput::RequireCheckedClose 1.128 + Perl::Critic::Policy::InputOutput::RequireCheckedOpen 1.128 + Perl::Critic::Policy::InputOutput::RequireCheckedSyscalls 1.128 + Perl::Critic::Policy::InputOutput::RequireEncodingWithUTF8Layer 1.128 + Perl::Critic::Policy::Miscellanea::ProhibitFormats 1.128 + Perl::Critic::Policy::Miscellanea::ProhibitTies 1.128 + Perl::Critic::Policy::Miscellanea::ProhibitUnrestrictedNoCritic 1.128 + Perl::Critic::Policy::Miscellanea::ProhibitUselessNoCritic 1.128 + Perl::Critic::Policy::Modules::ProhibitAutomaticExportation 1.128 + Perl::Critic::Policy::Modules::ProhibitConditionalUseStatements 1.128 + Perl::Critic::Policy::Modules::ProhibitEvilModules 1.128 + Perl::Critic::Policy::Modules::ProhibitExcessMainComplexity 1.128 + Perl::Critic::Policy::Modules::ProhibitMultiplePackages 1.128 + Perl::Critic::Policy::Modules::RequireBarewordIncludes 1.128 + Perl::Critic::Policy::Modules::RequireEndWithOne 1.128 + Perl::Critic::Policy::Modules::RequireExplicitPackage 1.128 + Perl::Critic::Policy::Modules::RequireFilenameMatchesPackage 1.128 + Perl::Critic::Policy::Modules::RequireNoMatchVarsWithUseEnglish 1.128 + Perl::Critic::Policy::Modules::RequireVersionVar 1.128 + Perl::Critic::Policy::NamingConventions::Capitalization 1.128 + Perl::Critic::Policy::NamingConventions::ProhibitAmbiguousNames 1.128 + Perl::Critic::Policy::Objects::ProhibitIndirectSyntax 1.128 + Perl::Critic::Policy::References::ProhibitDoubleSigils 1.128 + Perl::Critic::Policy::RegularExpressions::ProhibitCaptureWithoutTest 1.128 + Perl::Critic::Policy::RegularExpressions::ProhibitComplexRegexes 1.128 + Perl::Critic::Policy::RegularExpressions::ProhibitEnumeratedClasses 1.128 + Perl::Critic::Policy::RegularExpressions::ProhibitEscapedMetacharacters 1.128 + Perl::Critic::Policy::RegularExpressions::ProhibitFixedStringMatches 1.128 + Perl::Critic::Policy::RegularExpressions::ProhibitSingleCharAlternation 1.128 + Perl::Critic::Policy::RegularExpressions::ProhibitUnusedCapture 1.128 + Perl::Critic::Policy::RegularExpressions::ProhibitUnusualDelimiters 1.128 + Perl::Critic::Policy::RegularExpressions::ProhibitUselessTopic 1.128 + Perl::Critic::Policy::RegularExpressions::RequireBracesForMultiline 1.128 + Perl::Critic::Policy::RegularExpressions::RequireDotMatchAnything 1.128 + Perl::Critic::Policy::RegularExpressions::RequireExtendedFormatting 1.128 + Perl::Critic::Policy::RegularExpressions::RequireLineBoundaryMatching 1.128 + Perl::Critic::Policy::Subroutines::ProhibitAmpersandSigils 1.128 + Perl::Critic::Policy::Subroutines::ProhibitBuiltinHomonyms 1.128 + Perl::Critic::Policy::Subroutines::ProhibitExcessComplexity 1.128 + Perl::Critic::Policy::Subroutines::ProhibitExplicitReturnUndef 1.128 + Perl::Critic::Policy::Subroutines::ProhibitManyArgs 1.128 + Perl::Critic::Policy::Subroutines::ProhibitNestedSubs 1.128 + Perl::Critic::Policy::Subroutines::ProhibitReturnSort 1.128 + Perl::Critic::Policy::Subroutines::ProhibitSubroutinePrototypes 1.128 + Perl::Critic::Policy::Subroutines::ProhibitUnusedPrivateSubroutines 1.128 + Perl::Critic::Policy::Subroutines::ProtectPrivateSubs 1.128 + Perl::Critic::Policy::Subroutines::RequireArgUnpacking 1.128 + Perl::Critic::Policy::Subroutines::RequireFinalReturn 1.128 + Perl::Critic::Policy::TestingAndDebugging::ProhibitNoStrict 1.128 + Perl::Critic::Policy::TestingAndDebugging::ProhibitNoWarnings 1.128 + Perl::Critic::Policy::TestingAndDebugging::ProhibitProlongedStrictureOverride 1.128 + Perl::Critic::Policy::TestingAndDebugging::RequireTestLabels 1.128 + Perl::Critic::Policy::TestingAndDebugging::RequireUseStrict 1.128 + Perl::Critic::Policy::TestingAndDebugging::RequireUseWarnings 1.128 + Perl::Critic::Policy::ValuesAndExpressions::ProhibitCommaSeparatedStatements 1.128 + Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexVersion 1.128 + Perl::Critic::Policy::ValuesAndExpressions::ProhibitConstantPragma 1.128 + Perl::Critic::Policy::ValuesAndExpressions::ProhibitEmptyQuotes 1.128 + Perl::Critic::Policy::ValuesAndExpressions::ProhibitEscapedCharacters 1.128 + Perl::Critic::Policy::ValuesAndExpressions::ProhibitImplicitNewlines 1.128 + Perl::Critic::Policy::ValuesAndExpressions::ProhibitInterpolationOfLiterals 1.128 + Perl::Critic::Policy::ValuesAndExpressions::ProhibitLeadingZeros 1.128 + Perl::Critic::Policy::ValuesAndExpressions::ProhibitLongChainsOfMethodCalls 1.128 + Perl::Critic::Policy::ValuesAndExpressions::ProhibitMagicNumbers 1.128 + Perl::Critic::Policy::ValuesAndExpressions::ProhibitMismatchedOperators 1.128 + Perl::Critic::Policy::ValuesAndExpressions::ProhibitMixedBooleanOperators 1.128 + Perl::Critic::Policy::ValuesAndExpressions::ProhibitNoisyQuotes 1.128 + Perl::Critic::Policy::ValuesAndExpressions::ProhibitQuotesAsQuotelikeOperatorDelimiters 1.128 + Perl::Critic::Policy::ValuesAndExpressions::ProhibitSpecialLiteralHeredocTerminator 1.128 + Perl::Critic::Policy::ValuesAndExpressions::ProhibitVersionStrings 1.128 + Perl::Critic::Policy::ValuesAndExpressions::RequireConstantVersion 1.128 + Perl::Critic::Policy::ValuesAndExpressions::RequireInterpolationOfMetachars 1.128 + Perl::Critic::Policy::ValuesAndExpressions::RequireNumberSeparators 1.128 + Perl::Critic::Policy::ValuesAndExpressions::RequireQuotedHeredocTerminator 1.128 + Perl::Critic::Policy::ValuesAndExpressions::RequireUpperCaseHeredocTerminator 1.128 + Perl::Critic::Policy::Variables::ProhibitAugmentedAssignmentInDeclaration 1.128 + Perl::Critic::Policy::Variables::ProhibitConditionalDeclarations 1.128 + Perl::Critic::Policy::Variables::ProhibitEvilVariables 1.128 + Perl::Critic::Policy::Variables::ProhibitLocalVars 1.128 + Perl::Critic::Policy::Variables::ProhibitMatchVars 1.128 + Perl::Critic::Policy::Variables::ProhibitPackageVars 1.128 + Perl::Critic::Policy::Variables::ProhibitPerl4PackageNames 1.128 + Perl::Critic::Policy::Variables::ProhibitPunctuationVars 1.128 + Perl::Critic::Policy::Variables::ProhibitReusedNames 1.128 + Perl::Critic::Policy::Variables::ProhibitUnusedVariables 1.128 + Perl::Critic::Policy::Variables::ProtectPrivateVars 1.128 + Perl::Critic::Policy::Variables::RequireInitializationForLocalVars 1.128 + Perl::Critic::Policy::Variables::RequireLexicalLoopIterators 1.128 + Perl::Critic::Policy::Variables::RequireLocalizedPunctuationVars 1.128 + Perl::Critic::Policy::Variables::RequireNegativeIndices 1.128 + Perl::Critic::PolicyConfig 1.128 + Perl::Critic::PolicyFactory 1.128 + Perl::Critic::PolicyListing 1.128 + Perl::Critic::PolicyParameter 1.128 + Perl::Critic::PolicyParameter::Behavior 1.128 + Perl::Critic::PolicyParameter::Behavior::Boolean 1.128 + Perl::Critic::PolicyParameter::Behavior::Enumeration 1.128 + Perl::Critic::PolicyParameter::Behavior::Integer 1.128 + Perl::Critic::PolicyParameter::Behavior::String 1.128 + Perl::Critic::PolicyParameter::Behavior::StringList 1.128 + Perl::Critic::ProfilePrototype 1.128 + Perl::Critic::Statistics 1.128 + Perl::Critic::TestUtils 1.128 + Perl::Critic::Theme 1.128 + Perl::Critic::ThemeListing 1.128 + Perl::Critic::UserProfile 1.128 + Perl::Critic::Utils 1.128 + Perl::Critic::Utils::Constants 1.128 + Perl::Critic::Utils::DataConversion 1.128 + Perl::Critic::Utils::McCabe 1.128 + Perl::Critic::Utils::POD 1.128 + Perl::Critic::Utils::POD::ParseInteriorSequence 1.128 + Perl::Critic::Utils::PPI 1.128 + Perl::Critic::Utils::Perl 1.128 + Perl::Critic::Violation 1.128 + Test::Perl::Critic::Policy 1.128 requirements: B::Keywords 1.05 Carp 0 @@ -4492,12 +4301,12 @@ DISTRIBUTIONS List::Util 0 Module::Build 0.4024 Module::Pluggable 3.1 - PPI 1.220 - PPI::Document 1.220 - PPI::Document::File 1.220 - PPI::Node 1.220 - PPI::Token::Quote::Single 1.220 - PPI::Token::Whitespace 1.220 + PPI 1.224 + PPI::Document 1.224 + PPI::Document::File 1.224 + PPI::Node 1.224 + PPI::Token::Quote::Single 1.224 + PPI::Token::Whitespace 1.224 PPIx::Regexp 0.027 PPIx::Utilities::Node 1.001 PPIx::Utilities::Statement 1.001 @@ -4520,6 +4329,7 @@ DISTRIBUTIONS charnames 0 lib 0 overload 0 + perl 5.006001 strict 0 version 0.77 warnings 0 @@ -4546,12 +4356,6 @@ DISTRIBUTIONS Perl::Tidy::VerticalAligner::Line 20170521 requirements: ExtUtils::MakeMaker 0 - PerlIO-gzip-0.19 - pathname: N/NW/NWCLARK/PerlIO-gzip-0.19.tar.gz - provides: - PerlIO::gzip 0.19 - requirements: - ExtUtils::MakeMaker 0 PerlIO-utf8_strict-0.007 pathname: L/LE/LEONT/PerlIO-utf8_strict-0.007.tar.gz provides: @@ -4890,106 +4694,6 @@ DISTRIBUTIONS ExtUtils::MakeMaker 0 Test::More 0 perl 5.006 - Search-Elasticsearch-2.03 - pathname: D/DR/DRTECH/Search-Elasticsearch-2.03.tar.gz - provides: - Search::Elasticsearch 2.03 - Search::Elasticsearch::Bulk 2.03 - Search::Elasticsearch::Client::0_90::Direct 2.03 - Search::Elasticsearch::Client::0_90::Direct::Cluster 2.03 - Search::Elasticsearch::Client::0_90::Direct::Indices 2.03 - Search::Elasticsearch::Client::1_0::Direct 2.03 - Search::Elasticsearch::Client::1_0::Direct::Cat 2.03 - Search::Elasticsearch::Client::1_0::Direct::Cluster 2.03 - Search::Elasticsearch::Client::1_0::Direct::Indices 2.03 - Search::Elasticsearch::Client::1_0::Direct::Nodes 2.03 - Search::Elasticsearch::Client::1_0::Direct::Snapshot 2.03 - Search::Elasticsearch::Client::2_0::Direct 2.03 - Search::Elasticsearch::Client::2_0::Direct::Cat 2.03 - Search::Elasticsearch::Client::2_0::Direct::Cluster 2.03 - Search::Elasticsearch::Client::2_0::Direct::Indices 2.03 - Search::Elasticsearch::Client::2_0::Direct::Nodes 2.03 - Search::Elasticsearch::Client::2_0::Direct::Snapshot 2.03 - Search::Elasticsearch::Client::2_0::Direct::Tasks 2.03 - Search::Elasticsearch::Cxn::Factory 2.03 - Search::Elasticsearch::Cxn::HTTPTiny 2.03 - Search::Elasticsearch::Cxn::Hijk 2.03 - Search::Elasticsearch::Cxn::LWP 2.03 - Search::Elasticsearch::CxnPool::Sniff 2.03 - Search::Elasticsearch::CxnPool::Static 2.03 - Search::Elasticsearch::CxnPool::Static::NoPing 2.03 - Search::Elasticsearch::Error 2.03 - Search::Elasticsearch::Logger::LogAny 2.03 - Search::Elasticsearch::Role::API::0_90 2.03 - Search::Elasticsearch::Role::API::1_0 2.03 - Search::Elasticsearch::Role::API::2_0 2.03 - Search::Elasticsearch::Role::Bulk 2.03 - Search::Elasticsearch::Role::Client 2.03 - Search::Elasticsearch::Role::Client::Direct 2.03 - Search::Elasticsearch::Role::Client::Direct::Main 2.03 - Search::Elasticsearch::Role::Cxn 2.03 - Search::Elasticsearch::Role::Cxn::HTTP 2.03 - Search::Elasticsearch::Role::CxnPool 2.03 - Search::Elasticsearch::Role::CxnPool::Sniff 2.03 - Search::Elasticsearch::Role::CxnPool::Static 2.03 - Search::Elasticsearch::Role::CxnPool::Static::NoPing 2.03 - Search::Elasticsearch::Role::Is_Sync 2.03 - Search::Elasticsearch::Role::Logger 2.03 - Search::Elasticsearch::Role::Scroll 2.03 - Search::Elasticsearch::Role::Serializer 2.03 - Search::Elasticsearch::Role::Serializer::JSON 2.03 - Search::Elasticsearch::Role::Transport 2.03 - Search::Elasticsearch::Scroll 2.03 - Search::Elasticsearch::Serializer::JSON 2.03 - Search::Elasticsearch::Serializer::JSON::Cpanel 2.03 - Search::Elasticsearch::Serializer::JSON::PP 2.03 - Search::Elasticsearch::Serializer::JSON::XS 2.03 - Search::Elasticsearch::TestServer 2.03 - Search::Elasticsearch::Transport 2.03 - Search::Elasticsearch::Util 2.03 - Search::Elasticsearch::Util::API::Path 2.03 - Search::Elasticsearch::Util::API::QS 2.03 - requirements: - Any::URI::Escape 0 - Data::Dumper 0 - Devel::GlobalDestruction 0 - Encode 0 - ExtUtils::MakeMaker 0 - File::Temp 0 - HTTP::Headers 0 - HTTP::Request 0 - HTTP::Tiny 0.043 - IO::Select 0 - IO::Socket 0 - IO::Uncompress::Inflate 0 - JSON::MaybeXS 1.002002 - JSON::PP 0 - LWP::UserAgent 0 - List::Util 0 - Log::Any 1.02 - Log::Any::Adapter 0 - MIME::Base64 0 - Module::Runtime 0 - Moo 1.003 - Moo::Role 0 - POSIX 0 - Package::Stash 0.34 - Scalar::Util 0 - Sub::Exporter 0 - Time::HiRes 0 - Try::Tiny 0 - URI 0 - namespace::clean 0 - overload 0 - strict 0 - warnings 0 - Sort-Naturally-1.03 - pathname: B/BI/BINGOS/Sort-Naturally-1.03.tar.gz - provides: - Sort::Naturally 1.03 - requirements: - ExtUtils::MakeMaker 0 - perl 5 Specio-0.37 pathname: D/DR/DROLSKY/Specio-0.37.tar.gz provides: @@ -5126,6 +4830,12 @@ DISTRIBUTIONS Sub::Exporter 0.972 strict 0 warnings 0 + Struct-Dumb-0.09 + pathname: P/PE/PEVANS/Struct-Dumb-0.09.tar.gz + provides: + Struct::Dumb 0.09 + requirements: + Module::Build 0.4004 Sub-Exporter-0.987 pathname: R/RJ/RJBS/Sub-Exporter-0.987.tar.gz provides: @@ -5185,11 +4895,11 @@ DISTRIBUTIONS perl 5.006 strict 0 warnings 0 - Sub-Quote-2.003001 - pathname: H/HA/HAARG/Sub-Quote-2.003001.tar.gz + Sub-Quote-2.004000 + pathname: H/HA/HAARG/Sub-Quote-2.004000.tar.gz provides: - Sub::Defer 2.003001 - Sub::Quote 2.003001 + Sub::Defer 2.004000 + Sub::Quote 2.004000 requirements: ExtUtils::MakeMaker 0 Scalar::Util 0 @@ -5470,6 +5180,14 @@ DISTRIBUTIONS Try::Tiny 0.07 strict 0 warnings 0 + Test-Identity-0.01 + pathname: P/PE/PEVANS/Test-Identity-0.01.tar.gz + provides: + Test::Identity 0.01 + requirements: + Scalar::Util 0 + Test::Builder::Tester 0 + Test::More 0 Test-LongString-0.17 pathname: R/RG/RGARCIA/Test-LongString-0.17.tar.gz provides: @@ -5556,6 +5274,16 @@ DISTRIBUTIONS Test::More 0 strict 0 warnings 0 + Test-Refcount-0.08 + pathname: P/PE/PEVANS/Test-Refcount-0.08.tar.gz + provides: + Test::Refcount 0.08 + requirements: + B 0 + Scalar::Util 0 + Test::Builder 0 + Test::Builder::Tester 0 + Test::More 0 Test-Requires-0.10 pathname: T/TO/TOKUHIROM/Test-Requires-0.10.tar.gz provides: @@ -5923,44 +5651,44 @@ DISTRIBUTIONS perl 5.006 strict 0 warnings 0 - Type-Tiny-1.002000 - pathname: T/TO/TOBYINK/Type-Tiny-1.002000.tar.gz - provides: - Devel::TypeTiny::Perl56Compat 1.002000 - Devel::TypeTiny::Perl58Compat 1.002000 - Error::TypeTiny 1.002000 - Error::TypeTiny::Assertion 1.002000 - Error::TypeTiny::Compilation 1.002000 - Error::TypeTiny::WrongNumberOfParameters 1.002000 - Eval::TypeTiny 1.002000 - Reply::Plugin::TypeTiny 1.002000 - Test::TypeTiny 1.002000 - Type::Coercion 1.002000 - Type::Coercion::FromMoose 1.002000 - Type::Coercion::Union 1.002000 - Type::Library 1.002000 - Type::Params 1.002000 - Type::Parser 1.002000 - Type::Registry 1.002000 - Type::Tiny 1.002000 - Type::Tiny::Class 1.002000 - Type::Tiny::Duck 1.002000 - Type::Tiny::Enum 1.002000 - Type::Tiny::Intersection 1.002000 - Type::Tiny::Role 1.002000 - Type::Tiny::Union 1.002000 - Type::Utils 1.002000 - Types::Common::Numeric 1.002000 - Types::Common::String 1.002000 - Types::Standard 1.002000 - Types::Standard::ArrayRef 1.002000 - Types::Standard::CycleTuple 1.002000 - Types::Standard::Dict 1.002000 - Types::Standard::HashRef 1.002000 - Types::Standard::Map 1.002000 - Types::Standard::ScalarRef 1.002000 - Types::Standard::Tuple 1.002000 - Types::TypeTiny 1.002000 + Type-Tiny-1.002001 + pathname: T/TO/TOBYINK/Type-Tiny-1.002001.tar.gz + provides: + Devel::TypeTiny::Perl56Compat 1.002001 + Devel::TypeTiny::Perl58Compat 1.002001 + Error::TypeTiny 1.002001 + Error::TypeTiny::Assertion 1.002001 + Error::TypeTiny::Compilation 1.002001 + Error::TypeTiny::WrongNumberOfParameters 1.002001 + Eval::TypeTiny 1.002001 + Reply::Plugin::TypeTiny 1.002001 + Test::TypeTiny 1.002001 + Type::Coercion 1.002001 + Type::Coercion::FromMoose 1.002001 + Type::Coercion::Union 1.002001 + Type::Library 1.002001 + Type::Params 1.002001 + Type::Parser 1.002001 + Type::Registry 1.002001 + Type::Tiny 1.002001 + Type::Tiny::Class 1.002001 + Type::Tiny::Duck 1.002001 + Type::Tiny::Enum 1.002001 + Type::Tiny::Intersection 1.002001 + Type::Tiny::Role 1.002001 + Type::Tiny::Union 1.002001 + Type::Utils 1.002001 + Types::Common::Numeric 1.002001 + Types::Common::String 1.002001 + Types::Standard 1.002001 + Types::Standard::ArrayRef 1.002001 + Types::Standard::CycleTuple 1.002001 + Types::Standard::Dict 1.002001 + Types::Standard::HashRef 1.002001 + Types::Standard::Map 1.002001 + Types::Standard::ScalarRef 1.002001 + Types::Standard::Tuple 1.002001 + Types::TypeTiny 1.002001 requirements: Exporter::Tiny 0.026 ExtUtils::MakeMaker 6.17 @@ -6127,16 +5855,6 @@ DISTRIBUTIONS base 0 lib 0 perl 5.008 - WWW-Curl-4.17 - pathname: S/SZ/SZBALINT/WWW-Curl-4.17.tar.gz - provides: - WWW::Curl 4.17 - WWW::Curl::Easy 4.17 - WWW::Curl::Form 4.17 - WWW::Curl::Multi undef - WWW::Curl::Share undef - requirements: - ExtUtils::MakeMaker 6.42 WWW-Form-UrlEncoded-0.24 pathname: K/KA/KAZEBURO/WWW-Form-UrlEncoded-0.24.tar.gz provides: @@ -6381,16 +6099,6 @@ DISTRIBUTIONS XML::Parser 2.27 XML::SAX 0.03 XML::SAX::Base 1.00 - XML-Simple-2.24 - pathname: G/GR/GRANTM/XML-Simple-2.24.tar.gz - provides: - XML::Simple 2.24 - requirements: - ExtUtils::MakeMaker 0 - XML::NamespaceSupport 1.04 - XML::SAX 0.15 - XML::SAX::Expat 0 - perl 5.008 XML-XPath-1.40 pathname: M/MA/MANWAR/XML-XPath-1.40.tar.gz provides: diff --git a/lib/MetaCPAN/Middleware/Static.pm b/lib/MetaCPAN/Middleware/Static.pm index 4ca88ed0ae..cfc48cc507 100644 --- a/lib/MetaCPAN/Middleware/Static.pm +++ b/lib/MetaCPAN/Middleware/Static.pm @@ -82,6 +82,11 @@ sub wrap { }; } + mount '/sitemap-authors.xml.gz' => Plack::App::File->new( + file => 'root/static/sitemaps/sitemap-authors.xml.gz' )->to_app; + mount '/sitemap-releases.xml.gz' => Plack::App::File->new( + file => 'root/static/sitemaps/sitemap-releases.xml.gz' )->to_app; + mount '/favicon.ico' => Plack::App::File->new( file => 'root/static/icons/favicon.ico' ) ->to_app; diff --git a/lib/MetaCPAN/Sitemap.pm b/lib/MetaCPAN/Sitemap.pm index 7fe589f798..fd9c4d6e91 100644 --- a/lib/MetaCPAN/Sitemap.pm +++ b/lib/MetaCPAN/Sitemap.pm @@ -1,41 +1,42 @@ package MetaCPAN::Sitemap; - -=head1 DESCRIPTION - -Generate an XML file containing URLs use by the robots.txt Sitemap. We use this -module to generate one each for authors, modules and releases. - -=cut - use strict; use warnings; -use MetaCPAN::Moose; - -use autodie; - -use Carp; -use Search::Elasticsearch; -use File::Spec; -use MetaCPAN::Web::Types qw( HashRef Int Str ); -use MooseX::StrictConstructor; -use PerlIO::gzip; -use XML::Simple qw(:strict); - -has [ 'cpan_directory', 'object_type', 'field_name', 'xml_file', ] => ( - is => 'ro', - isa => Str, - required => 1, -); - -has 'filter' => ( - is => 'ro', - isa => HashRef, +use IO::Socket::SSL qw(SSL_VERIFY_PEER); +use IO::Async::Loop; +use IO::Async::SSL; +use Net::Async::HTTP; +use Cpanel::JSON::XS; +use IO::Compress::Gzip; +use HTML::Entities qw(encode_entities_numeric); + +use Moo; + +has api_secure => ( is => 'ro', required => 1 ); +has url_prefix => ( is => 'ro', required => 1 ); +has object_type => ( is => 'ro', required => 1 ); +has field_name => ( is => 'ro', required => 1 ); +has filter => ( is => 'ro' ); +has size => ( is => 'ro', default => 1000 ); +has loop => ( is => 'lazy', default => sub { IO::Async::Loop->new } ); +has ua => ( + is => 'lazy', + default => sub { + my $self = shift; + my $http = Net::Async::HTTP->new( + user_agent => + 'MetaCPAN-Web/1.0 (https://github.com/metacpan/metacpan-web)', + max_connections_per_host => 5, + SSL_verify_mode => SSL_VERIFY_PEER, + timeout => 10, + ); + $self->loop->add($http); + $http; + } ); -has 'size' => ( - is => 'ro', - isa => Int, -); +sub DEMOLISH { + $_[0]->ua->remove_from_parent; +} # Mandatory arguments to this function are # [] search object_type (author and release) @@ -48,82 +49,72 @@ has 'size' => ( # [] filter - contains filter for a field that also needs to be included in # the list of form fields. -sub process { - my $self = shift; - - # Check that a) the directory where the output file wants to be does - # actually exist and b) the directory itself is writeable. - - # Get started. Create the ES object and the scrolled search object. - # XXX Remove this hardcoded URL - my $es = Search::Elasticsearch->new( - cxn_pool => 'Static::NoPing', - nodes => ['https://fastapi.metacpan.org'], - send_get_body_as => 'POST', - ); - - my $field_name = $self->field_name; - - # Start off with standard search parameters .. - - my %search_parameters = ( - index => 'v1', - size => 5000, - type => $self->object_type, - fields => [$field_name], - ); - - # ..and augment them if necesary. - - if ( $self->filter ) { +my $json = Cpanel::JSON::XS->new->utf8->canonical; - # Copy the filter over wholesale into the search parameters, and add - # the filter fields to the field list. - - $search_parameters{'body'}{'query'}{'match'} = $self->filter; - push @{ $search_parameters{'fields'} }, keys %{ $self->filter }; - } - - my $scrolled_search = $es->scroll_helper(%search_parameters); - - # Open the output file, get ready to pump out the XML. - - open my $fh, '>:gzip', $self->xml_file; - - my @urls; - my $metacpan_url = q{}; - if ( $self->cpan_directory ) { - $metacpan_url - = 'https://metacpan.org/' . $self->cpan_directory . q{/}; +sub _request { + my ( $self, $content, $cb ) = @_; + my $url = $self->api_secure . '/'; + my $content_type = 'text/plain'; + if ( ref $content ) { + $url .= $self->object_type . '/'; + $content_type = 'application/json'; + $content = $json->encode($content); } + $url .= '_search/scroll?scroll=1m&size=' . $self->size; + $self->ua->POST( $url, $content, content_type => $content_type, )->then( + sub { + my $response = shift; + my $content = $json->decode( $response->content ); + return Future->done + if !@{ $content->{hits}{hits} }; + $cb->( $content->{hits}{hits} ); + return $self->_request( $content->{_scroll_id}, $cb ); + } + ); +} - while ( $scrolled_search->refill_buffer ) { - push @urls, - map { $metacpan_url . $_->{'fields'}->{$field_name} } - $scrolled_search->drain_buffer; - } +sub write { + my ( $self, $file ) = @_; - $_ = $_ . q{ } for @urls; + my $fh = IO::Compress::Gzip->new( $file . '.new' ); + $fh->print(<<'END_XML_HEADER'); + + +END_XML_HEADER - $self->{size} = @urls; - XMLout( + $self->_request( { - 'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9', - 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', - 'xsi:schemaLocation' => - 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd', - 'url' => [ sort @urls ], + fields => [ $self->field_name ], + query => { match_all => {} }, + ( $self->filter ? ( filter => $self->filter ) : () ), + sort => [ $self->field_name ], }, - 'KeyAttr' => [], - 'RootName' => 'urlset', - 'XMLDecl' => q//, - 'OutputFile' => $fh, - ); - - close $fh; + sub { + my $hits = shift; + for my $hit (@$hits) { + my $link_field = $hit->{fields}{ $self->field_name }; + $link_field = $link_field->[0] if ref $link_field; + my $url = $self->url_prefix . $link_field; + $fh->print( " " + . encode_entities_numeric($url) + . "\n" ); + } + } + )->get; + $fh->print("\n"); + $fh->close; + rename "$file.new", "$file"; return; } -__PACKAGE__->meta->make_immutable; - 1; +__END__ + +=head1 DESCRIPTION + +Generate an XML file containing URLs use by the robots.txt Sitemap. We use this +module to generate one each for authors, modules and releases. + +=cut diff --git a/lib/MetaCPAN/Web/Controller/Account.pm b/lib/MetaCPAN/Web/Controller/Account.pm index 8820924a29..3f21b19476 100644 --- a/lib/MetaCPAN/Web/Controller/Account.pm +++ b/lib/MetaCPAN/Web/Controller/Account.pm @@ -35,14 +35,14 @@ sub identities : Local : Args(0) { if ( $c->req->method eq 'POST' && ( my $delete = $c->req->params->{delete} ) ) { - $c->model('API::User')->delete_identity( $delete, $c->token )->recv; + $c->model('API::User')->delete_identity( $delete, $c->token )->get; $c->res->redirect('/account/identities'); } } sub profile : Local : Args(0) { my ( $self, $c ) = @_; - my $author = $c->model('API::User')->get_profile( $c->token )->recv; + my $author = $c->model('API::User')->get_profile( $c->token )->get; $c->stash( $author->{error} ? { no_profile => 1 } : { author => $author } ); my $req = $c->req; @@ -92,8 +92,7 @@ sub profile : Local : Args(0) { $data->{donation} = undef unless ( $req->params->{donations} ); - my $res - = $c->model('API::User')->update_profile( $data, $c->token )->recv; + my $res = $c->model('API::User')->update_profile( $data, $c->token )->get; if ( $res->{error} ) { $c->stash( { author => $data, errors => $res->{errors} } ); } diff --git a/lib/MetaCPAN/Web/Controller/Account/Favorite.pm b/lib/MetaCPAN/Web/Controller/Account/Favorite.pm index f54ff9768f..2ded7b346a 100644 --- a/lib/MetaCPAN/Web/Controller/Account/Favorite.pm +++ b/lib/MetaCPAN/Web/Controller/Account/Favorite.pm @@ -22,10 +22,10 @@ sub add : Local : Args(0) { my $data = $c->req->params; my $res; if ( $data->{remove} ) { - $res = $model->remove_favorite( $data, $c->token )->recv; + $res = $model->remove_favorite( $data, $c->token )->get; } else { - $res = $model->add_favorite( $data, $c->token )->recv; + $res = $model->add_favorite( $data, $c->token )->get; } # We need to purge if the rating has changes until the fav count @@ -79,7 +79,7 @@ sub _cache_key_for_user { sub _add_fav_list_to_stash { my ( $self, $c, $size ) = @_; my $user = $c->user; - my $faves = $c->model('API::Favorite')->by_user( $user->id, $size ); + my $faves = $c->model('API::Favorite')->by_user( $user->id, $size )->get; $c->stash( { faves => $faves } ); return $user; } diff --git a/lib/MetaCPAN/Web/Controller/Account/Turing.pm b/lib/MetaCPAN/Web/Controller/Account/Turing.pm index 6f2d91cf6b..71872c03a3 100644 --- a/lib/MetaCPAN/Web/Controller/Account/Turing.pm +++ b/lib/MetaCPAN/Web/Controller/Account/Turing.pm @@ -13,7 +13,7 @@ sub index : Path('') : Args(0) { my $res = $c->model('API::User')->turing( @$params{qw(recaptcha_challenge_field recaptcha_response_field)}, $c->token - )->recv; + )->get; $c->stash( { success => $res->{looks_human}, diff --git a/lib/MetaCPAN/Web/Controller/Activity.pm b/lib/MetaCPAN/Web/Controller/Activity.pm index 1599490597..fa3f6dbde3 100644 --- a/lib/MetaCPAN/Web/Controller/Activity.pm +++ b/lib/MetaCPAN/Web/Controller/Activity.pm @@ -11,7 +11,7 @@ sub index : Path : Args(0) { my %args = map { $_ => $c->req->parameters->{$_} } keys %{ $c->req->parameters }; - my $line = $c->model('API')->request( '/activity', undef, \%args )->recv; + my $line = $c->model('API')->request( '/activity', undef, \%args )->get; return unless $line and exists $line->{activity}; $c->res->content_type('image/svg+xml'); diff --git a/lib/MetaCPAN/Web/Controller/Author.pm b/lib/MetaCPAN/Web/Controller/Author.pm index c07e2f219b..1ab4008c54 100644 --- a/lib/MetaCPAN/Web/Controller/Author.pm +++ b/lib/MetaCPAN/Web/Controller/Author.pm @@ -46,18 +46,17 @@ sub index : Chained('root') PathPart('') Args(0) { my $pauseid = $c->stash->{pauseid}; - my $author_cv = $c->model('API::Author')->get($pauseid); - my $author = $author_cv->recv; + my $author = $c->model('API::Author')->get($pauseid)->get; $c->detach('/not_found') unless ( $author->{pauseid} ); - my $releases = $c->model('API::Release')->latest_by_author($pauseid); + my $releases = $c->model('API::Release')->latest_by_author($pauseid)->get; my $date = List::Util::max map { DateTime::Format::ISO8601->parse_datetime( $_->{date} ) } @{ $releases->{releases} }; $c->res->last_modified($date) if $date; - my $faves = $c->model('API::Favorite')->by_user( $author->{user} ); + my $faves = $c->model('API::Favorite')->by_user( $author->{user} )->get; my $took = $releases->{took}; @@ -88,9 +87,10 @@ sub releases : Chained('root') PathPart Args(0) { my $page = $req->page > 0 ? $req->page : 1; my $author_cv = $c->model('API::Author')->get($id); my $releases - = $c->model('API::Release')->all_by_author( $id, $page_size, $page ); + = $c->model('API::Release')->all_by_author( $id, $page_size, $page ) + ->get; - my $author = $author_cv->recv; + my $author = $author_cv->get; $c->detach('/not_found') unless ( $author->{pauseid} ); $c->stash( diff --git a/lib/MetaCPAN/Web/Controller/Changes.pm b/lib/MetaCPAN/Web/Controller/Changes.pm index d32c17dec8..0be871fa73 100644 --- a/lib/MetaCPAN/Web/Controller/Changes.pm +++ b/lib/MetaCPAN/Web/Controller/Changes.pm @@ -27,7 +27,7 @@ sub release : Local Args(2) { sub get : Private { my ( $self, $c, @args ) = @_; - my $file = $c->model('API::Changes')->get(@args)->recv; + my $file = $c->model('API::Changes')->get(@args)->get; # NOTE: There is currently no differentiation (from the API) # of whether the release doesn't exist or we couldn't find a change log. diff --git a/lib/MetaCPAN/Web/Controller/Diff.pm b/lib/MetaCPAN/Web/Controller/Diff.pm index 5f4b17d2da..0807ea916f 100644 --- a/lib/MetaCPAN/Web/Controller/Diff.pm +++ b/lib/MetaCPAN/Web/Controller/Diff.pm @@ -10,7 +10,7 @@ sub index : PathPart('diff') : Chained('/') : CaptureArgs(0) { sub release : Local : Args(4) { my ( $self, $c, @path ) = @_; - my $diff = $c->model('API::Diff')->releases(@path)->recv; + my $diff = $c->model('API::Diff')->releases(@path)->get; $c->stash( { diff => $diff, template => 'diff.html', type => 'release' } ); } @@ -18,7 +18,7 @@ sub release : Local : Args(4) { sub file : Local : Args(0) { my ( $self, $c ) = @_; my $diff = $c->model('API::Diff') - ->files( $c->req->params->{source}, $c->req->params->{target} )->recv; + ->files( $c->req->params->{source}, $c->req->params->{target} )->get; $c->stash( { diff => $diff, template => 'diff.html', type => 'source' } ); } diff --git a/lib/MetaCPAN/Web/Controller/Favorite.pm b/lib/MetaCPAN/Web/Controller/Favorite.pm index 5e5764a26e..fa61b19dda 100644 --- a/lib/MetaCPAN/Web/Controller/Favorite.pm +++ b/lib/MetaCPAN/Web/Controller/Favorite.pm @@ -6,7 +6,8 @@ sub recent : Local : Args(0) { my ( $self, $c ) = @_; my $page_size = $c->req->get_page_size(100); my $data - = $c->model('API::Favorite')->recent( $c->req->page, $page_size ); + = $c->model('API::Favorite')->recent( $c->req->page, $page_size ) + ->get; $c->stash( { header => 1, @@ -23,7 +24,7 @@ sub recent : Local : Args(0) { sub leaderboard : Local : Args(0) { my ( $self, $c ) = @_; - my $data = $c->model('API::Favorite')->leaderboard(); + my $data = $c->model('API::Favorite')->leaderboard->get; return unless $data; $c->stash( diff --git a/lib/MetaCPAN/Web/Controller/Feed.pm b/lib/MetaCPAN/Web/Controller/Feed.pm index e5c6d6988f..e834893c27 100644 --- a/lib/MetaCPAN/Web/Controller/Feed.pm +++ b/lib/MetaCPAN/Web/Controller/Feed.pm @@ -103,8 +103,7 @@ sub author : Local : Args(1) { $c->cdn_max_age('1y'); $c->add_author_key($author); - my $author_cv = $c->model('API::Author')->get($author); - my $author_info = $author_cv->recv; + my $author_info = $c->model('API::Author')->get($author)->get; # If the author can be found, we get the hashref of author info. If it # can't be found, we (confusingly) get a HashRef with "code" and "message" @@ -114,9 +113,10 @@ sub author : Local : Args(1) { $c->detach( '/not_found', [] ); } - my $releases = $c->model('API::Release')->latest_by_author($author); + my $releases = $c->model('API::Release')->latest_by_author($author)->get; - my $faves = $c->model('API::Favorite')->by_user( $author_info->{user} ); + my $faves + = $c->model('API::Favorite')->by_user( $author_info->{user} )->get; $c->stash->{feed} = $self->build_feed( host => $c->config->{web_host}, @@ -136,7 +136,7 @@ sub distribution : Local : Args(1) { $c->cdn_max_age('1y'); $c->add_dist_key($distribution); - my $data = $c->model('API::Release')->versions($distribution)->recv; + my $data = $c->model('API::Release')->versions($distribution)->get; $c->stash->{feed} = $self->build_feed( host => $c->config->{web_host}, title => "Recent CPAN uploads of $distribution - MetaCPAN", diff --git a/lib/MetaCPAN/Web/Controller/Lab.pm b/lib/MetaCPAN/Web/Controller/Lab.pm index 6f4f16a257..d43fcf2263 100644 --- a/lib/MetaCPAN/Web/Controller/Lab.pm +++ b/lib/MetaCPAN/Web/Controller/Lab.pm @@ -28,7 +28,7 @@ sub dependencies : Local : Args(0) : Does('Sortable') { my $data; if ( $module = $c->req->params->{'module'} ) { - $data = $c->model('API::Lab')->dependencies($module); + $data = $c->model('API::Lab')->dependencies($module)->get; } $c->stash( @@ -43,7 +43,7 @@ sub dependencies : Local : Args(0) : Does('Sortable') { sub dashboard : Local : Args(0) { my ( $self, $c ) = @_; - my $user = $c->model('API::User')->get_profile( $c->token )->recv; + my $user = $c->model('API::User')->get_profile( $c->token )->get; my $report; my $pauseid = $c->req->params->{'pauseid'}; @@ -55,7 +55,7 @@ sub dashboard : Local : Args(0) { $pauseid = $user->{pauseid}; if ($pauseid) { $report = $c->model('API::Lab') - ->fetch_latest_distros( 1000, $pauseid ); + ->fetch_latest_distros( 1000, $pauseid )->get; } } diff --git a/lib/MetaCPAN/Web/Controller/Login.pm b/lib/MetaCPAN/Web/Controller/Login.pm index 6d32570071..e942e12dd9 100644 --- a/lib/MetaCPAN/Web/Controller/Login.pm +++ b/lib/MetaCPAN/Web/Controller/Login.pm @@ -20,7 +20,7 @@ sub index : Path : Args(0) { client_secret => $c->config->{consumer_secret}, code => $code, }, - )->recv; + )->get; $c->req->session->set( token => $data->{access_token} ); $c->authenticate( { token => $data->{access_token} } ); my $state = $c->req->params->{state} || q{}; diff --git a/lib/MetaCPAN/Web/Controller/Mirrors.pm b/lib/MetaCPAN/Web/Controller/Mirrors.pm index dec1dcc4a9..1cae2746cd 100644 --- a/lib/MetaCPAN/Web/Controller/Mirrors.pm +++ b/lib/MetaCPAN/Web/Controller/Mirrors.pm @@ -52,7 +52,7 @@ sub index : Path : Args(0) { ) : ( sort => [ 'continent', 'country' ] ) } - )->recv; + )->get; my $latest = [ map { { diff --git a/lib/MetaCPAN/Web/Controller/Permission.pm b/lib/MetaCPAN/Web/Controller/Permission.pm index f58dc91140..cea67fd048 100644 --- a/lib/MetaCPAN/Web/Controller/Permission.pm +++ b/lib/MetaCPAN/Web/Controller/Permission.pm @@ -28,7 +28,7 @@ sub get : Private { my $c = shift; my ( $type, $name ) = @_; - my $perms = $c->model('API::Permission')->get( $type, $name ); + my $perms = $c->model('API::Permission')->get( $type, $name )->get; if ( !$perms ) { $c->stash( diff --git a/lib/MetaCPAN/Web/Controller/Pod.pm b/lib/MetaCPAN/Web/Controller/Pod.pm index ac2620a22a..6063d3d6c9 100644 --- a/lib/MetaCPAN/Web/Controller/Pod.pm +++ b/lib/MetaCPAN/Web/Controller/Pod.pm @@ -21,7 +21,7 @@ sub find : Path : Args(1) { $c->browser_max_age('1h'); # TODO: Pass size param so we can disambiguate? - $c->stash->{pod_file} = $c->model('API::Module')->find(@path)->recv; + $c->stash->{pod_file} = $c->model('API::Module')->find(@path)->get; # TODO: Disambiguate if there's more than once match. #176 @@ -41,7 +41,7 @@ sub release : Local : Args { $c->detach(); } - $c->stash->{pod_file} = $c->model('API::Module')->get(@path)->recv; + $c->stash->{pod_file} = $c->model('API::Module')->get(@path)->get; $c->stash->{permalinks} = 1; $c->forward( 'view', [@path] ); } @@ -58,7 +58,7 @@ sub distribution : Local : Args { # Get latest "author/release" of dist so we can use it to find the file. # TODO: Pass size param so we can disambiguate? my $release = try { - $c->model('API::Release')->find($dist)->recv->{hits}{hits}->[0] + $c->model('API::Release')->find($dist)->get->{hits}{hits}->[0] ->{_source}; } or $c->detach('/not_found'); @@ -66,7 +66,7 @@ sub distribution : Local : Args { unshift @path, @$release{qw( author name )}; - $c->stash->{pod_file} = $c->model('API::Module')->get(@path)->recv; + $c->stash->{pod_file} = $c->model('API::Module')->get(@path)->get; $c->forward( 'view', [@path] ); } @@ -116,9 +116,7 @@ sub view : Private { ->get( @{$data}{qw(author release)} ), }, $data, - ); - $reqs = $self->recv_all($reqs); - + )->get; $self->stash_api_results( $c, $reqs, $data ); $self->add_favorites_data( $data, $reqs->{favorites}, $data ); @@ -224,7 +222,7 @@ sub view : Private { $c->add_dist_key($dist); $c->add_author_key( $release->{author} ); - $c->stash( $c->model('API::Favorite')->find_plussers($dist) ); + $c->stash( $c->model('API::Favorite')->find_plussers($dist)->get ); $c->stash( $c->model( diff --git a/lib/MetaCPAN/Web/Controller/Raw.pm b/lib/MetaCPAN/Web/Controller/Raw.pm index 4628e6a781..a4922c1d02 100644 --- a/lib/MetaCPAN/Web/Controller/Raw.pm +++ b/lib/MetaCPAN/Web/Controller/Raw.pm @@ -8,9 +8,9 @@ BEGIN { extends 'MetaCPAN::Web::Controller' } sub index : Path : Args { my ( $self, $c, @module ) = @_; - my ( $source, $module ) = ( - $c->model('API::Module')->source(@module)->recv, - $c->model('API::Module')->get(@module)->recv + my ( $source, $module ) = map { $_->get } ( + $c->model('API::Module')->source(@module), + $c->model('API::Module')->get(@module), ); $c->detach('/not_found') unless ( $source->{raw} ); if ( $c->req->parameters->{download} ) { diff --git a/lib/MetaCPAN/Web/Controller/Recent.pm b/lib/MetaCPAN/Web/Controller/Recent.pm index 8f55e1554d..1f913e4069 100644 --- a/lib/MetaCPAN/Web/Controller/Recent.pm +++ b/lib/MetaCPAN/Web/Controller/Recent.pm @@ -14,7 +14,7 @@ sub index : Path : Args(0) { my ($data) = $c->model('API::Release') - ->recent( $req->page, $page_size, $req->params->{f} || 'l' )->recv; + ->recent( $req->page, $page_size, $req->params->{f} || 'l' )->get; my $latest = [ map { $_->{fields} } @{ $data->{hits}->{hits} } ]; single_valued_arrayref_to_scalar($latest); diff --git a/lib/MetaCPAN/Web/Controller/Recent/TopUploaders.pm b/lib/MetaCPAN/Web/Controller/Recent/TopUploaders.pm index e9367e1a38..387221e4c9 100644 --- a/lib/MetaCPAN/Web/Controller/Recent/TopUploaders.pm +++ b/lib/MetaCPAN/Web/Controller/Recent/TopUploaders.pm @@ -25,10 +25,10 @@ sub all : Local : Args(0) { sub topuploaders : Private { my ( $self, $c, $range ) = @_; - my $data = $c->model('API::Release')->topuploaders($range); + my $data = $c->model('API::Release')->topuploaders($range)->get; my $authors - = $c->model('API::Author')->get( keys %{ $data->{counts} } )->recv; + = $c->model('API::Author')->get( keys %{ $data->{counts} } )->get; $c->stash( { diff --git a/lib/MetaCPAN/Web/Controller/Release.pm b/lib/MetaCPAN/Web/Controller/Release.pm index 7e42bc70f4..ebce23af64 100644 --- a/lib/MetaCPAN/Web/Controller/Release.pm +++ b/lib/MetaCPAN/Web/Controller/Release.pm @@ -31,7 +31,7 @@ sub by_distribution : Chained('root') PathPart('') Args(1) { sub index : Chained('/') PathPart('release') CaptureArgs(1) { my ( $self, $c, $dist ) = @_; - $c->stash( $c->model('API::Favorite')->find_plussers($dist) ); + $c->stash( $c->model('API::Favorite')->find_plussers($dist)->get ); } sub plusser_display : Chained('index') PathPart('plussers') Args(0) { @@ -66,7 +66,7 @@ sub view : Private { my $model = $c->stash->{model}; my $data = delete $c->stash->{data}; - my $out = $data->recv->{hits}->{hits}->[0]->{_source}; + my $out = $data->get->{hits}->{hits}->[0]->{_source}; $c->detach('/not_found') unless ($out); @@ -81,8 +81,7 @@ sub view : Private { changes => $c->model('API::Changes')->get( $author, $release ), }, $out, - ); - $reqs = $self->recv_all($reqs); + )->get; $self->stash_api_results( $c, $reqs, $out ); $self->add_favorites_data( $out, $reqs->{favorites}, $out ); @@ -122,7 +121,8 @@ sub view : Private { )->summary_hash ); - $c->stash( $c->model('API::Favorite')->find_plussers($distribution) ); + $c->stash( + $c->model('API::Favorite')->find_plussers($distribution)->get ); # Simplify the file data we pass to the template. my @view_files = map +{ %{ $_->{fields} }, %{ $_->{_source} }, }, diff --git a/lib/MetaCPAN/Web/Controller/Requires.pm b/lib/MetaCPAN/Web/Controller/Requires.pm index e2e859f948..eb300e6b76 100644 --- a/lib/MetaCPAN/Web/Controller/Requires.pm +++ b/lib/MetaCPAN/Web/Controller/Requires.pm @@ -28,7 +28,7 @@ sub distribution : Local : Args(1) : Does('Sortable') { my $data = $c->model('API::Release') ->reverse_dependencies( $distribution, $c->req->page, $page_size, - $sort )->recv; + $sort )->get; $c->stash( { %{$data}, @@ -47,7 +47,7 @@ sub module : Local : Args(1) : Does('Sortable') { my $data = $c->model('API::Module') - ->requires( $module, $c->req->page, $page_size, $sort ); + ->requires( $module, $c->req->page, $page_size, $sort )->get; $c->stash( { %{$data}, diff --git a/lib/MetaCPAN/Web/Controller/Search.pm b/lib/MetaCPAN/Web/Controller/Search.pm index 038f60f139..68aa3d3985 100644 --- a/lib/MetaCPAN/Web/Controller/Search.pm +++ b/lib/MetaCPAN/Web/Controller/Search.pm @@ -41,14 +41,14 @@ sub index : Path : Args(0) { $query =~ s[^ (?: \\ | ! ) ][]x ) { - my $module = $model->first($query)->recv; + my $module = $model->first($query)->get; $module = $module->[0] if $module and is_arrayref($module); if ( $module && $module eq $query ) { $c->res->redirect( '/pod/' . $module ); $c->detach; } else { - my $author = $c->model('API::Author')->search($query)->recv; + my $author = $c->model('API::Author')->search($query)->get; if ( $author->{total} == 1 && $query eq $author->{results}->[0]->{pauseid} ) { @@ -68,7 +68,7 @@ sub index : Path : Args(0) { my $results = $model->search_web( $query, $from, $page_size ); my $authors = $c->model('API::Author')->search( $query, $from ); - ( $results, $authors ) = ( $results->recv, $authors->recv ); + ( $results, $authors ) = ( $results->get, $authors->get ); if ( !$results->{total} && !$authors->{total} ) { my $suggest = $query; diff --git a/lib/MetaCPAN/Web/Controller/Search/AutoComplete.pm b/lib/MetaCPAN/Web/Controller/Search/AutoComplete.pm index 8ebb0c4f3b..c8a2f77d4e 100644 --- a/lib/MetaCPAN/Web/Controller/Search/AutoComplete.pm +++ b/lib/MetaCPAN/Web/Controller/Search/AutoComplete.pm @@ -19,12 +19,12 @@ sub index : Path : Args(0) { value => "$_->{pauseid} - $_->{name}", data => { id => $_->{pauseid}, type => 'author' } }, - @{ $author_data->recv->{results} } + @{ $author_data->get->{results} } ), ( map +{ value => $_, data => { module => $_, type => 'module' } }, uniq map $_->{documentation}, - @{ $module_data->recv->{results} } + @{ $module_data->get->{results} } ), ); diff --git a/lib/MetaCPAN/Web/Controller/Source.pm b/lib/MetaCPAN/Web/Controller/Source.pm index 69311588fb..878d71f35f 100644 --- a/lib/MetaCPAN/Web/Controller/Source.pm +++ b/lib/MetaCPAN/Web/Controller/Source.pm @@ -28,19 +28,19 @@ sub index : Path : Args { my ( $source, $module ); if ( @module == 1 ) { - $module = $c->model('API::Module')->find(@module)->recv; + $module = $c->model('API::Module')->find(@module)->get; $module[0] = join q{/}, $module->{author}, $module->{release}, $module->{path}; - $source = $c->model('API::Module')->source(@module)->recv; + $source = $c->model('API::Module')->source(@module)->get; } else { - ( $source, $module ) = ( - $c->model('API::Module')->source(@module)->recv, - $c->model('API::Module')->get(@module)->recv, + ( $source, $module ) = map { $_->get } ( + $c->model('API::Module')->source(@module), + $c->model('API::Module')->get(@module), ); } if ( $module->{directory} ) { - my $files = $c->model('API::File')->dir(@module); + my $files = $c->model('API::File')->dir(@module)->get; $c->res->last_modified( $module->{date} ); $c->stash( { diff --git a/lib/MetaCPAN/Web/Model/API.pm b/lib/MetaCPAN/Web/Model/API.pm index 0ea36c78da..7705f5f9c0 100644 --- a/lib/MetaCPAN/Web/Model/API.pm +++ b/lib/MetaCPAN/Web/Model/API.pm @@ -5,22 +5,39 @@ extends 'Catalyst::Model'; use namespace::autoclean; -use AnyEvent::Curl::Multi; -use Encode (); -use HTTP::Request (); +use Encode (); use Cpanel::JSON::XS qw( decode_json encode_json ); +use IO::Async::Loop; +use IO::Async::SSL; +use IO::Socket::SSL qw(SSL_VERIFY_PEER); +use Net::Async::HTTP; +use URI; +use URI::QueryParam; use MetaCPAN::Web::Types qw( Uri ); -use MooseX::ClassAttribute; use Try::Tiny qw( catch try ); -use URI::QueryParam; use Log::Log4perl; -class_has client => ( - is => 'ro', - lazy => 1, - default => - sub { return AnyEvent::Curl::Multi->new( max_concurrency => 5 ) }, -); +my $loop; + +sub loop { + $loop ||= IO::Async::Loop->new; +} + +my $client; + +sub client { + $client ||= do { + my $http = Net::Async::HTTP->new( + user_agent => + 'MetaCPAN-Web/1.0 (https://github.com/metacpan/metacpan-web)', + max_connections_per_host => 5, + SSL_verify_mode => SSL_VERIFY_PEER, + timeout => 10, + ); + $_[0]->loop->add($http); + $http; + }; +} has api_secure => ( is => 'ro', @@ -29,18 +46,6 @@ has api_secure => ( required => 1, ); -{ - no warnings 'once'; - $AnyEvent::HTTP::PERSISTENT_TIMEOUT = 0; - $AnyEvent::HTTP::USERAGENT - = 'Mozilla/5.0 (compatible; U; MetaCPAN-Web/1.0; ' - . '+https://github.com/metacpan/metacpan-web)'; -} - -sub cv { - AE::cv; -} - =head2 COMPONENT Set C config parameters from the app config object. @@ -64,8 +69,6 @@ sub model { sub request { my ( $self, $path, $search, $params, $method ) = @_; - my $req = $self->cv; - my $url = $self->api_secure->clone; # the order of the following 2 lines matters @@ -93,39 +96,23 @@ sub request { # encode_json returns an octet string $request->add_content( encode_json($search) ) if $search; - $self->client->request($request)->cv->cb( - sub { - my $cv = shift; - try { - my ($response) = $cv->recv; - if ( !$response ) { - $req->croak( - "bad response when requesting " . $request->uri ); - return; - } - my $content_type = $response->header('content-type') || ''; - my $data = $response->content; - - if ( $content_type =~ /^application\/json/ ) { - try { - my $json = $self->process_json_response($data); - $req->send($json); - } - catch { - $req->send( $self->raw_api_response($data) ); - }; - } - else { - # Response is raw data, e.g. text/plain - $req->send( $self->raw_api_response($data) ); - } + $self->client->do_request( request => $request )->transform( + done => sub { + my $response = shift; + my $content_type = $response->header('content-type') || ''; + my $data = $response->content; + + if ( $content_type =~ /^application\/json/ ) { + my $out; + eval { $out = $self->process_json_response($data); }; + return $out + if $out; } - catch { - $req->croak($_); - }; + + # Response is raw data, e.g. text/plain + return $self->raw_api_response($data); } ); - return $req; } sub process_json_response { diff --git a/lib/MetaCPAN/Web/Model/API/Author.pm b/lib/MetaCPAN/Web/Model/API/Author.pm index d303598b17..31761d40f1 100644 --- a/lib/MetaCPAN/Web/Model/API/Author.pm +++ b/lib/MetaCPAN/Web/Model/API/Author.pm @@ -49,7 +49,6 @@ sub get { sub search { my ( $self, $query, $from ) = @_; - my $cv = $self->cv; my $search = { query => { bool => { @@ -75,23 +74,20 @@ sub search { from => $from || 0, }; - $self->request( '/author/_search', $search )->cb( - sub { - my $results = shift->recv + return $self->request( '/author/_search', $search )->transform( + done => sub { + my $results = shift || { hits => { total => 0, hits => [] } }; - $cv->send( - { - results => [ - map { +{ %{ $_->{_source} }, id => $_->{_id} } } - @{ $results->{hits}{hits} } - ], - total => $results->{hits}{total} || 0, - took => $results->{took} - } - ); - } + return { + results => [ + map { +{ %{ $_->{_source} }, id => $_->{_id} } } + @{ $results->{hits}{hits} } + ], + total => $results->{hits}{total} || 0, + took => $results->{took} + }; + }, ); - return $cv; } sub by_user { @@ -106,10 +102,11 @@ sub by_user { else { $ret = $self->request("/author/by_user/$users"); } - return unless $ret; - - my $data = $ret->recv; - return ( exists $data->{authors} ? $data->{authors} : [] ); + $ret->transform( + done => sub { + return exists $_[0]->{authors} ? $_[0]->{authors} : []; + } + ); } __PACKAGE__->meta->make_immutable; diff --git a/lib/MetaCPAN/Web/Model/API/Changes/Parser.pm b/lib/MetaCPAN/Web/Model/API/Changes/Parser.pm index ce817172e9..235f80e945 100644 --- a/lib/MetaCPAN/Web/Model/API/Changes/Parser.pm +++ b/lib/MetaCPAN/Web/Model/API/Changes/Parser.pm @@ -17,7 +17,7 @@ sub load { } sub parse { - my ( $class, $string ) = @_; + my ( undef, $string ) = @_; my @lines = split /\r\n?|\n/, $string; diff --git a/lib/MetaCPAN/Web/Model/API/Contributors.pm b/lib/MetaCPAN/Web/Model/API/Contributors.pm index 65c4ba9151..3c1a5189fe 100644 --- a/lib/MetaCPAN/Web/Model/API/Contributors.pm +++ b/lib/MetaCPAN/Web/Model/API/Contributors.pm @@ -25,19 +25,17 @@ it under the same terms as Perl itself. sub get { my ( $self, $author, $release ) = @_; - my $cv = $self->cv; # If there's no release, we'll just redirect $release //= {}; $self->request( '/release/contributors/' . $author . '/' . $release, ) - ->cb( - sub { - my ($contributors) = shift->recv; - $cv->send( $contributors->{contributors} ); + ->transform( + done => sub { + my $data = shift; + return $data->{contributors}; } ); - return $cv; } __PACKAGE__->meta->make_immutable; diff --git a/lib/MetaCPAN/Web/Model/API/Favorite.pm b/lib/MetaCPAN/Web/Model/API/Favorite.pm index 70723e2fce..73d3e9bd65 100644 --- a/lib/MetaCPAN/Web/Model/API/Favorite.pm +++ b/lib/MetaCPAN/Web/Model/API/Favorite.pm @@ -5,6 +5,7 @@ use namespace::autoclean; extends 'MetaCPAN::Web::Model::API'; use List::Util qw(uniq); +use Future; use Importer 'MetaCPAN::Web::Elasticsearch::Adapter' => qw/ single_valued_arrayref_to_scalar /; @@ -12,16 +13,14 @@ use Importer 'MetaCPAN::Web::Elasticsearch::Adapter' => sub get { my ( $self, $user, @distributions ) = @_; @distributions = uniq @distributions; - my $cv = $self->cv; # If there are no distributions this will build a query with an empty # filter and ES will return a parser error... so just skip it. if ( !@distributions ) { - $cv->send( {} ); - return $cv; + return Future->wrap( {} ); } - $self->request( + return $self->request( '/favorite/_search', { size => 0, @@ -49,113 +48,128 @@ sub get { : (), } } - )->cb( - sub { - my $data = shift->recv; - $cv->send( - { - took => $data->{took}, - favorites => { - map { $_->{key} => $_->{doc_count} } - @{ $data->{aggregations}->{favorites}->{buckets} } - }, - myfavorites => $user - ? { - map { $_->{key} => $_->{doc_count} } @{ - $data->{aggregations}->{myfavorites}->{entries} - ->{buckets} - } - } - : {}, - } - ); + )->transform( + done => sub { + my $data = shift; + return { + took => $data->{took}, + favorites => { + map { $_->{key} => $_->{doc_count} } + @{ $data->{aggregations}->{favorites}->{buckets} } + }, + myfavorites => $user + ? { + map { $_->{key} => $_->{doc_count} } @{ + $data->{aggregations}->{myfavorites}->{entries} + ->{buckets} + } + } + : {}, + }; } ); - return $cv; } sub by_user { my ( $self, $user, $size ) = @_; $size ||= 250; - my $ret = $self->request( "/favorite/by_user/$user", { size => $size } ); - return unless $ret; - my $data = $ret->recv; - return [] unless exists $data->{favorites}; - return $data->{favorites}; + my $ret + = $self->request( "/favorite/by_user/$user", { size => $size } ) + ->transform( + done => sub { + my $data = shift; + return [] unless exists $data->{favorites}; + return $data->{favorites}; + } + ); } sub recent { my ( $self, $page, $page_size ) = @_; - my $data = $self->request( '/favorite/recent', - { size => $page_size, page => $page } )->recv; - - my @user_ids = map { $_->{user} } @{ $data->{favorites} }; - return $data unless @user_ids; - - my $authors - = $self->request( '/author/by_user', undef, { user => \@user_ids } ) - ->recv; - if ( $authors and exists $authors->{authors} ) { - my %author_for_user_id - = map { $_->{user} => $_->{pauseid} } @{ $authors->{authors} }; - for my $fav ( @{ $data->{favorites} } ) { - next unless exists $author_for_user_id{ $fav->{user} }; - $fav->{clicked_by_author} = $author_for_user_id{ $fav->{user} }; + $self->request( '/favorite/recent', + { size => $page_size, page => $page } )->then( + sub { + my $data = shift; + my @user_ids = map { $_->{user} } @{ $data->{favorites} }; + return Future->done unless @user_ids; + $self->request( '/author/by_user', undef, { user => \@user_ids } ) + ->transform( + done => sub { + my $authors = shift; + if ( $authors and exists $authors->{authors} ) { + my %author_for_user_id + = map { $_->{user} => $_->{pauseid} } + @{ $authors->{authors} }; + for my $fav ( @{ $data->{favorites} } ) { + next + unless + exists $author_for_user_id{ $fav->{user} }; + $fav->{clicked_by_author} + = $author_for_user_id{ $fav->{user} }; + } + } + } + ); } - } - - return $data; + ); } sub leaderboard { my ($self) = @_; - my $data = $self->request('/favorite/leaderboard')->recv; - return $data; + $self->request('/favorite/leaderboard'); } sub find_plussers { my ( $self, $distribution ) = @_; # search for all users, match all according to the distribution. - my $plusser = $self->by_dist($distribution); - my $plusser_data = $plusser->recv; - - # store in an array. - my @plusser_users = map { $_->{user} } - map { single_valued_arrayref_to_scalar( $_->{_source} ) } - @{ $plusser_data->{hits}->{hits} }; - my $total_plussers = @plusser_users; - - # find plussers by pause ids. - my $authors - = @plusser_users - ? $self->plusser_by_id( \@plusser_users )->recv->{hits}->{hits} - : []; - - my @plusser_details = map { - { - id => $_->{_source}->{pauseid}, - pic => $_->{_source}->{gravatar_url}, + $self->by_dist($distribution)->then( + sub { + my $plusser_data = shift; + + # store in an array. + my @plusser_users = map { $_->{user} } + map { single_valued_arrayref_to_scalar( $_->{_source} ) } + @{ $plusser_data->{hits}->{hits} }; + my $total_plussers = @plusser_users; + + # find plussers by pause ids. + return Future->done( { hits => { hits => [] } }, $total_plussers ) + unless @plusser_users; + $self->plusser_by_id( \@plusser_users )->transform( + done => sub { + return ( $_[0], $total_plussers ); + } + ); } - } @{$authors}; - - my $total_authors = @plusser_details; + )->transform( + done => sub { + my ( $data, $total_plussers ) = @_; + my $authors = $data->{hits}{hits}; + my @plusser_details = map { + { + id => $_->{_source}->{pauseid}, + pic => $_->{_source}->{gravatar_url}, + } + } @{$authors}; + my $total_authors = @plusser_details; - # find total non pauseid users who have ++ed the dist. - my $total_nonauthors = ( $total_plussers - $total_authors ); + # find total non pauseid users who have ++ed the dist. + my $total_nonauthors = ( $total_plussers - $total_authors ); - # number of pauseid users can be more than total plussers - # then set 0 to non pauseid users - $total_nonauthors = 0 if $total_nonauthors < 0; + # number of pauseid users can be more than total plussers + # then set 0 to non pauseid users + $total_nonauthors = 0 if $total_nonauthors < 0; - return ( - { - plusser_authors => \@plusser_details, - plusser_others => $total_nonauthors, - plusser_data => $distribution + return ( + { + plusser_authors => \@plusser_details, + plusser_others => $total_nonauthors, + plusser_data => $distribution + } + ); } - ); - + ); } # to search for v0/favorite/_search/{user} for the particular $distribution. diff --git a/lib/MetaCPAN/Web/Model/API/File.pm b/lib/MetaCPAN/Web/Model/API/File.pm index 59d27f9015..ba3ca8f100 100644 --- a/lib/MetaCPAN/Web/Model/API/File.pm +++ b/lib/MetaCPAN/Web/Model/API/File.pm @@ -15,8 +15,11 @@ sub source { sub dir { my ( $self, @path ) = @_; my $path = join '/', @path; - my $data = $self->request("/file/dir/$path")->recv; - return $data->{dir}; + my $data = $self->request("/file/dir/$path")->transform( + done => sub { + $_[0]->{dir}; + } + ); } __PACKAGE__->meta->make_immutable; diff --git a/lib/MetaCPAN/Web/Model/API/Lab.pm b/lib/MetaCPAN/Web/Model/API/Lab.pm index 05756e272e..4c86618a10 100644 --- a/lib/MetaCPAN/Web/Model/API/Lab.pm +++ b/lib/MetaCPAN/Web/Model/API/Lab.pm @@ -27,12 +27,14 @@ sub dependencies { } $deps{$module}{orig} = 1; - return [ - map { $deps{$_} } - reverse - sort { $deps{$a}{date} cmp $deps{$b}{date} } - keys %deps - ]; + return Future->done( + [ + map { $deps{$_} } + reverse + sort { $deps{$a}{date} cmp $deps{$b}{date} } + keys %deps + ] + ); } my %CORE = map { $_ => 1 } qw( @@ -55,13 +57,13 @@ sub _handle_module { } # get the distribution that provides this module - my $rm = $self->request("/module/$module")->recv; + my $rm = $self->request("/module/$module")->get; my %dep = ( dist => $rm->{distribution}, date => $rm->{date}, ); - my $rd = $self->request("/release/$rm->{distribution}")->recv; + my $rd = $self->request("/release/$rm->{distribution}")->get; $dep{license} = $rd->{license}; @@ -73,7 +75,7 @@ sub _handle_module { sub fetch_latest_distros { my ( $self, $size, $pauseid ) = @_; - my $r = $self->request( + $self->request( '/release/_search', { query => { @@ -94,64 +96,73 @@ sub fetch_latest_distros { ], size => $size, }, - )->recv; - my %licenses; - my %distros; + )->transform( + done => sub { + my $data = shift; + my %licenses; + my %distros; - foreach my $d ( @{ $r->{hits}{hits} } ) { - my $license = $d->{_source}{license}[0]; - my $distro = $d->{_source}{distribution}; - my $repo = $d->{_source}{'resources.repository'}; + foreach my $d ( @{ $data->{hits}{hits} } ) { + my $license = $d->{_source}{license}[0]; + my $distro = $d->{_source}{distribution}; + my $repo = $d->{_source}{'resources.repository'}; - next if $distros{$distro}; # show the first one + next if $distros{$distro}; # show the first one # TODO: can we fetch the bug count in one call for all the distributions? - my $distribution = $self->request("/distribution/$distro")->recv; - if ( $distribution->{bugs} ) { - $distros{$distro}{bugs} = $distribution->{bugs}{active}; - } + my $distribution + = $self->request("/distribution/$distro")->get; + if ( $distribution->{bugs} ) { + $distros{$distro}{bugs} = $distribution->{bugs}{active}; + } - $distros{$distro}{test} = $d->{_source}{tests}; - my $total = 0; - $total += ( $distros{$distro}{test}{$_} // 0 ) for qw(pass fail na); - $distros{$distro}{test}{ratio} - = $total - ? int( 100 * ( $distros{$distro}{test}{pass} // 0 ) / $total ) - : q{}; - - if ( $license - and $license ne 'unknown' - and $license ne 'open_source' ) - { - $licenses{$license}++; - } - else { - $distros{$distro}{license} = 1; - } + $distros{$distro}{test} = $d->{_source}{tests}; + my $total = 0; + $total += ( $distros{$distro}{test}{$_} // 0 ) + for qw(pass fail na); + $distros{$distro}{test}{ratio} + = $total + ? int( + 100 * ( $distros{$distro}{test}{pass} // 0 ) / $total ) + : q{}; + + if ( $license + and $license ne 'unknown' + and $license ne 'open_source' ) + { + $licenses{$license}++; + } + else { + $distros{$distro}{license} = 1; + } - $distros{$distro}{unauthorized} - = $d->{_source}{authorized} eq 'false' ? 1 : 0; + $distros{$distro}{unauthorized} + = $d->{_source}{authorized} eq 'false' ? 1 : 0; - # See also root/inc/release-infro.html - if ( $repo and ( $repo->{url} or $repo->{web} ) ) { + # See also root/inc/release-infro.html + if ( $repo and ( $repo->{url} or $repo->{web} ) ) { - # TODO: shall we collect the types and list them? - } - else { - $distros{$distro}{repo} = 1; - } - if ( not $d->{_source}{abstract} ) { - $distros{$distro}{abstract} = 1; - } + # TODO: shall we collect the types and list them? + } + else { + $distros{$distro}{repo} = 1; + } + if ( not $d->{_source}{abstract} ) { + $distros{$distro}{abstract} = 1; + } - ( $distros{$distro}{date} = $d->{_source}{date} ) =~ s/\.\d+Z$//; - $distros{$distro}{version} = $d->{_source}{'metadata.version'}; - } + ( $distros{$distro}{date} = $d->{_source}{date} ) + =~ s/\.\d+Z$//; + $distros{$distro}{version} + = $d->{_source}{'metadata.version'}; + } - return { - licenses => \%licenses, - distros => \%distros, - }; + return { + licenses => \%licenses, + distros => \%distros, + }; + } + ); } __PACKAGE__->meta->make_immutable; diff --git a/lib/MetaCPAN/Web/Model/API/Module.pm b/lib/MetaCPAN/Web/Model/API/Module.pm index d9750f2da1..f8c1a8f9e4 100644 --- a/lib/MetaCPAN/Web/Model/API/Module.pm +++ b/lib/MetaCPAN/Web/Model/API/Module.pm @@ -33,63 +33,46 @@ sub find { sub autocomplete { my ( $self, $query ) = @_; - my $cv = $self->cv; $self->request( "/search/autocomplete", undef, - { q => $query, size => 50 } )->cb( - sub { - my $data = shift->recv; - $cv->send( - { - results => [ - map { $_->{fields} } @{ $data->{hits}->{hits} || [] } - ] - } - ); + { q => $query, size => 50 } )->transform( + done => sub { + my $data = shift; + return { results => + [ map { $_->{fields} } @{ $data->{hits}->{hits} || [] } ] + }; } ); - return $cv; } sub search_web { my ( $self, $query, $from, $page_size ) = @_; - my $cv = $self->cv; $self->request( "/search/web", undef, - { q => $query, size => $page_size // 20, from => $from // 0 } )->cb( - sub { - my $data = shift->recv; - $cv->send($data); - } - ); - return $cv; + { q => $query, size => $page_size // 20, from => $from // 0 } ); } sub first { my ( $self, $query ) = @_; - my $cv = $self->cv; - $self->request( "/search/simple", undef, { q => $query } )->cb( - sub { - my ($result) = shift->recv; - return $cv->send(undef) unless ( $result->{hits}->{total} ); - $cv->send( - $result->{hits}->{hits}->[0]->{fields}->{documentation} ); + $self->request( "/search/simple", undef, { q => $query } )->transform( + done => sub { + my $data = shift; + return undef + unless ( $data->{hits}->{total} ); + return $data->{hits}->{hits}->[0]->{fields}->{documentation}; } ); - return $cv; } sub requires { my ( $self, $module, $page, $page_size ) = @_; - my $data = $self->request( + $self->request( "/release/requires/$module", undef, { page => $page, page_size => $page_size, }, - )->recv; - - return $data; + ); } __PACKAGE__->meta->make_immutable; diff --git a/lib/MetaCPAN/Web/Model/API/Permission.pm b/lib/MetaCPAN/Web/Model/API/Permission.pm index 3acbc67188..82aba9a9ee 100644 --- a/lib/MetaCPAN/Web/Model/API/Permission.pm +++ b/lib/MetaCPAN/Web/Model/API/Permission.pm @@ -16,10 +16,14 @@ sub get { my ( $self, $type, $name ) = @_; if ( $type eq 'module' ) { - my $module = $self->request( '/permission/' . $name )->recv; - - # return undef if there's a 404 - return $module->{code} ? undef : $module; + return $self->request( '/permission/' . $name )->transform( + done => sub { + + # return undef if there's a 404 + my $data = shift; + return $data->{code} ? undef : $data; + } + ); } if ( $type eq 'distribution' ) { @@ -51,32 +55,40 @@ sub _get_author_modules { sub _get_modules_in_distribution { my $self = shift; my $name = shift; - return undef unless $name; + return Future->done(undef) unless $name; - my $res = $self->request("/package/modules/$name")->recv; - my @modules = $res->{modules} ? @{ $res->{modules} } : undef; + $self->request("/package/modules/$name")->then( + sub { + my $res = shift; + my @modules = $res->{modules} ? @{ $res->{modules} } : undef; - return undef unless @modules; + return Future->done(undef) unless @modules; - my @perm_search - = map { +{ term => { module_name => $_ } } } @modules; + my @perm_search + = map { +{ term => { module_name => $_ } } } @modules; - my $search = { - query => { bool => { should => \@perm_search } }, - size => 1_000, - }; + my $search = { + query => { bool => { should => \@perm_search } }, + size => 1_000, + }; - return $self->_search_perms($search); + return $self->_search_perms($search); + } + ); } sub _search_perms { my $self = shift; my $search = shift; - my $perms_found = $self->request( '/permission/_search', $search )->recv; - my @perms = sort { $a->{module_name} cmp $b->{module_name} } - map { $_->{_source} } @{ $perms_found->{hits}->{hits} }; - return @perms ? \@perms : undef; + $self->request( '/permission/_search', $search )->transform( + done => sub { + my $perms = shift; + my @perms = sort { $a->{module_name} cmp $b->{module_name} } + map { $_->{_source} } @{ $perms->{hits}->{hits} }; + return @perms ? \@perms : undef; + } + ); } __PACKAGE__->meta->make_immutable; diff --git a/lib/MetaCPAN/Web/Model/API/Rating.pm b/lib/MetaCPAN/Web/Model/API/Rating.pm index b06da355a1..e2d64db5f3 100644 --- a/lib/MetaCPAN/Web/Model/API/Rating.pm +++ b/lib/MetaCPAN/Web/Model/API/Rating.pm @@ -1,6 +1,7 @@ package MetaCPAN::Web::Model::API::Rating; use Moose; use namespace::autoclean; +use Future; extends 'MetaCPAN::Web::Model::API'; @@ -28,16 +29,14 @@ use List::Util qw(uniq); sub get { my ( $self, @distributions ) = @_; @distributions = uniq @distributions; - my $cv = $self->cv; # If there are no distributions this will build a query with an empty # filter and ES will return a parser error... so just skip it. if ( !@distributions ) { - $cv->send( {} ); - return $cv; + return Future->done( {} ); } - $self->request( + return $self->request( '/rating/_search', { size => 0, @@ -59,22 +58,18 @@ sub get { } } } - )->cb( - sub { - my ($ratings) = shift->recv; - $cv->send( - { - took => $ratings->{took}, - ratings => { - map { $_->{key} => $_->{ratings_dist} } @{ - $ratings->{aggregations}->{ratings}->{buckets} - } - } + )->transform( + done => sub { + my $data = shift; + return { + took => $data->{took}, + ratings => { + map { $_->{key} => $_->{ratings_dist} } + @{ $data->{aggregations}->{ratings}->{buckets} } } - ); + }; } ); - return $cv; } __PACKAGE__->meta->make_immutable; diff --git a/lib/MetaCPAN/Web/Model/API/Release.pm b/lib/MetaCPAN/Web/Model/API/Release.pm index c19ec33415..c59d8e43e7 100644 --- a/lib/MetaCPAN/Web/Model/API/Release.pm +++ b/lib/MetaCPAN/Web/Model/API/Release.pm @@ -49,15 +49,13 @@ sub distribution { sub latest_by_author { my ( $self, $pauseid ) = @_; - my $data = $self->request("/release/latest_by_author/$pauseid")->recv; - return $data; + $self->request("/release/latest_by_author/$pauseid"); } sub all_by_author { my ( $self, $pauseid, $page, $page_size ) = @_; - my $data = $self->request( "/release/all_by_author/$pauseid", - undef, { page => $page, page_size => $page_size } )->recv; - return $data; + $self->request( "/release/all_by_author/$pauseid", + undef, { page => $page, page_size => $page_size } ); } sub recent { @@ -208,11 +206,10 @@ sub find { sub reverse_dependencies { my ( $self, $distribution, $page, $page_size, $sort ) = @_; $sort ||= { date => 'desc' }; - my $cv = $self->cv; # TODO: do we need to do a taint-check on $distribution before inserting it into the url? # maybe the fact that it came through as a Catalyst Arg is enough? - $self->request( + return $self->request( "/search/reverse_dependencies/$distribution", { query => { @@ -227,20 +224,16 @@ sub reverse_dependencies { from => $page * $page_size - $page_size, sort => [$sort], } - )->cb( - sub { - my $data = shift->recv; - $cv->send( - { - data => - [ map { $_->{_source} } @{ $data->{hits}->{hits} } ], - total => $data->{hits}->{total}, - took => $data->{took} - } - ); + )->transform( + done => sub { + my $data = shift; + return { + data => [ map { $_->{_source} } @{ $data->{hits}->{hits} } ], + total => $data->{hits}->{total}, + took => $data->{took} + }; } ); - return $cv; } sub interesting_files { @@ -360,17 +353,14 @@ sub favorites { sub topuploaders { my ( $self, $range ) = @_; my $param = $range ? { range => $range } : (); - my $data - = $self->request( '/release/top_uploaders', undef, $param )->recv; - return $data; + $self->request( '/release/top_uploaders', undef, $param ); } sub no_latest { my ( $self, @distributions ) = @_; - my $cv = $self->cv; # If there are no distributions return - return {} unless (@distributions); + return Future->done( {} ) unless (@distributions); @distributions = uniq @distributions; $self->request( @@ -387,13 +377,13 @@ sub no_latest { }, fields => [qw(distribution status)] } - )->cb( - sub { - my $data = shift->recv; + )->transform( + done => sub { + my $data = shift; my @latest = map { $_->{fields}->{distribution} } @{ $data->{hits}->{hits} }; - $cv->send( + return ( { took => $data->{took}, no_latest => { @@ -408,7 +398,6 @@ sub no_latest { ); } ); - return $cv; } __PACKAGE__->meta->make_immutable; diff --git a/lib/MetaCPAN/Web/Role/ReleaseInfo.pm b/lib/MetaCPAN/Web/Role/ReleaseInfo.pm index 94074b9623..50d99dcc2a 100644 --- a/lib/MetaCPAN/Web/Role/ReleaseInfo.pm +++ b/lib/MetaCPAN/Web/Role/ReleaseInfo.pm @@ -1,6 +1,7 @@ package MetaCPAN::Web::Role::ReleaseInfo; use Moose::Role; +use Future; use Importer 'MetaCPAN::Web::Elasticsearch::Adapter' => qw/ single_valued_arrayref_to_scalar /; @@ -28,7 +29,7 @@ sub add_favorites_data { sub api_requests { my ( $self, $c, $reqs, $data ) = @_; - return { + my %reqs = ( author => $c->model('API::Author')->get( $data->{author} ), favorites => $c->model('API::Favorite')->get( @@ -46,7 +47,16 @@ sub api_requests { distribution => $c->model('API::Release')->distribution( $data->{distribution} ), %$reqs, - }; + ); + my @names = keys %reqs; + my @futures = values %reqs; + return Future->needs_all(@futures)->transform( + done => sub { + my %results; + @results{@names} = @_; + return \%results; + } + ); } # organize the api results into simple variables for the template @@ -70,10 +80,4 @@ sub stash_api_results { $c->stash( \%stash ); } -# call recv() on all values in the provided hashref -sub recv_all { - my ( $self, $condvars ) = @_; - return +{ map { $_ => $condvars->{$_}->recv } keys %$condvars }; -} - 1; diff --git a/lib/MetaCPAN/Web/Test.pm b/lib/MetaCPAN/Web/Test.pm index 0d48514f31..fcf958d57a 100644 --- a/lib/MetaCPAN/Web/Test.pm +++ b/lib/MetaCPAN/Web/Test.pm @@ -15,6 +15,7 @@ use Test::More; use Test::XPath; use Try::Tiny; use Encode; +use Future; use base 'Exporter'; our @EXPORT = qw( GET @@ -28,7 +29,7 @@ our @EXPORT = qw( # TODO: use Sub:Override? # save a copy in case we override -my $orig_request = \&AnyEvent::Curl::Multi::request; +my $orig_request = \&Net::Async::HTTP::do_request; sub override_api_response { require MetaCPAN::Web::Model::API; @@ -37,17 +38,14 @@ sub override_api_response { my $matches = {@_}; no warnings 'redefine'; - *AnyEvent::Curl::Multi::request = sub { - if ( ( $matches->{if} ? $matches->{if}->(@_) : 1 ) - and my $res = $responder->(@_) ) + *Net::Async::HTTP::do_request = sub { + my ( $self, %args ) = @_; + my $request = $args{request}; + if ( ( $matches->{if} ? $matches->{if}->( $self, $request ) : 1 ) + and my $res = $responder->( $self, $request ) ) { $res = HTTP::Response->from_psgi($res) if ref $res eq 'ARRAY'; - - # return an object with a ->cv that's ready so that the cb will fire - my $ret = bless { cv => AE::cv() }, - 'AnyEvent::Curl::Multi::Handle'; - $ret->cv->send( $res, {} ); - return $ret; + return Future->wrap($res); } else { goto &$orig_request; diff --git a/lib/MetaCPAN/Web/User.pm b/lib/MetaCPAN/Web/User.pm index e186873b82..793e535599 100644 --- a/lib/MetaCPAN/Web/User.pm +++ b/lib/MetaCPAN/Web/User.pm @@ -28,7 +28,7 @@ sub for_session { sub from_session { my ( $self, $c, $id ) = @_; - my $user = $c->model('API::User')->get($id)->recv; + my $user = $c->model('API::User')->get($id)->get; $self->obj( Hash::AsObject->new($user) ) if ($user); return $user ? $self : undef; } @@ -36,7 +36,7 @@ sub from_session { sub find_user { my ( $self, $auth, $c ) = @_; my $obj = Hash::AsObject->new( - $c->model('API::User')->get( $auth->{token} )->recv ); + $c->model('API::User')->get( $auth->{token} )->get ); $self->obj($obj); return $self; } diff --git a/lib/MetaCPAN/Web/View/JSON.pm b/lib/MetaCPAN/Web/View/JSON.pm index dea32a79ea..b33dfd3315 100644 --- a/lib/MetaCPAN/Web/View/JSON.pm +++ b/lib/MetaCPAN/Web/View/JSON.pm @@ -6,7 +6,7 @@ use Cpanel::JSON::XS (); extends 'Catalyst::View::JSON'; sub encode_json { - my ( $self, $c, $data ) = @_; + my ( $self, undef, $data ) = @_; Cpanel::JSON::XS->new->utf8->encode($data); } diff --git a/lib/Plack/Middleware/Session/Cookie/MetaCPAN.pm b/lib/Plack/Middleware/Session/Cookie/MetaCPAN.pm index 2c19779d1e..699dcc47c1 100644 --- a/lib/Plack/Middleware/Session/Cookie/MetaCPAN.pm +++ b/lib/Plack/Middleware/Session/Cookie/MetaCPAN.pm @@ -64,7 +64,7 @@ sub prepare_app { sub save_state { my $self = shift; - my ( $id, $res, $env ) = @_; + my ( undef, undef, $env ) = @_; return if $env->{'psgix.session.options'}{no_store}; diff --git a/metacpan_web.conf b/metacpan_web.conf index b5425a5ce5..a3dd0edd54 100644 --- a/metacpan_web.conf +++ b/metacpan_web.conf @@ -36,3 +36,19 @@ mark_unauthorized_releases = 0 public_key 6LeH2MsSAAAAANwz3AA73Gw5OjCVjT6I51Ev-ior + + object_type = author + field_name = pauseid + metacpan_url = author + + + + object_type = release + field_name = distribution + metacpan_url = release + + + status = latest + + + diff --git a/root/robots.txt b/root/robots.txt index 28938f5fa3..61b426cc94 100644 --- a/root/robots.txt +++ b/root/robots.txt @@ -9,5 +9,5 @@ Disallow: /raw/ # Do not allow changing the default per page as is not useful Disallow: /*?*size=* -Sitemap: https://metacpan.org/static/sitemaps/authors.xml.gz -Sitemap: https://metacpan.org/static/sitemaps/releases.xml.gz +Sitemap: https://metacpan.org/sitemap-authors.xml.gz +Sitemap: https://metacpan.org/sitemap-releases.xml.gz diff --git a/t/encoding.t b/t/encoding.t index e6760fe86c..9b94ae71a3 100644 --- a/t/encoding.t +++ b/t/encoding.t @@ -25,7 +25,7 @@ my $model sub get_json { $res_body = shift; $content_type = shift if @_; - return $model->source(qw( who cares ))->recv; + return $model->source(qw( who cares ))->get; } sub get_raw { diff --git a/t/metacpan/sitemap.t b/t/metacpan/sitemap.t deleted file mode 100644 index 00f6b04ee5..0000000000 --- a/t/metacpan/sitemap.t +++ /dev/null @@ -1,82 +0,0 @@ -use strict; -use warnings; - -use File::Temp qw/ tempdir /; -use MetaCPAN::Sitemap; -use Test::More; -use Try::Tiny; -use XML::Simple; - -# Test each of the three things that the production script is going to do, -# but limit the searches to a single chunk of 250 results to speed things -# along. - -my @tests = ( - { - inputs => { - object_type => 'author', - field_name => 'pauseid', - xml_file => '', - cpan_directory => 'author', - }, - pattern => qr{https:.+/author/[a-z0-9A-Z-]+}, - }, - { - inputs => { - object_type => 'release', - field_name => 'distribution', - xml_file => '', - cpan_directory => 'release', - filter => { status => 'latest' }, - }, - pattern => qr{https?:.+/release/[a-z0-9A-Z-]+}, - } -); - -my $temp_dir = tempdir( CLEANUP => 1 ); - -foreach my $test (@tests) { - - # Generate the XML file into a file in a temporary directory, then - # check that the file exists, is valid XML, and has the right number - # of URLs. - - my $args = $test->{inputs}; - $args->{size} = 250; - $args->{xml_file} = File::Spec->catfile( $temp_dir, - "$test->{inputs}{object_type}.xml.gz" ); - my $sitemap = MetaCPAN::Sitemap->new($args); - $sitemap->process(); - - ok( -e $args->{xml_file}, - "XML output file for $args->{object_type} exists" ); - - open( my $xml_fh, '<:gzip', $args->{xml_file} ); - - my $xml = XMLin($xml_fh); - ok( defined $xml, "XML for $args->{object_type} checks out" ); - - ok( @{ $xml->{url} }, 'We have some URLs to look at' ); - is( - $sitemap->{size}, - scalar @{ $xml->{url} }, - "Number of URLs is correct" - ); - - # Check that each of the urls has the right pattern. - - note 'Checking urls'; - my $url_tests; - foreach my $url ( @{ $xml->{url} } ) { - - # Test that the url matches - # but only print a TAP line for the first test or if there's a failure. - # ~30,000 tests is a lot of output to sift through. - if ( !$url_tests++ || $url !~ $test->{pattern} ) { - like( $url, $test->{pattern}, 'URL matches' ); - } - } - ok( $url_tests, "Tested $url_tests urls" ); -} - -done_testing(); diff --git a/t/model/release.t b/t/model/release.t index c1c0014ed7..f24b7161a0 100644 --- a/t/model/release.t +++ b/t/model/release.t @@ -13,7 +13,7 @@ sub search_release { return map { @{ $_->{hits}{hits} } } - MetaCPAN::Web->model('API::Release')->$method(@args)->recv; + MetaCPAN::Web->model('API::Release')->$method(@args)->get; } my ( $true, $false ) = @{ decode_json('[true, false]') };