@@ -1,18 +1,18 @@
-# This file was automatically generated by Dist::Zilla::Plugin::ModuleBuild v5.015.
+# This file was automatically generated by Dist::Zilla::Plugin::ModuleBuild v5.020.
use strict;
use warnings;
-use Module::Build 0.3601;
+use Module::Build 0.28;
my %module_build_args = (
"build_requires" => {
- "Module::Build" => "0.3601"
+ "Module::Build" => "0.28"
},
"configure_requires" => {
- "ExtUtils::MakeMaker" => "6.30",
- "Module::Build" => "0.3601"
+ "ExtUtils::MakeMaker" => 0,
+ "Module::Build" => "0.28"
},
"dist_abstract" => "A comprehensive, DWIM-featured client to the MetaCPAN API",
"dist_author" => [
@@ -20,7 +20,7 @@ my %module_build_args = (
"Mickey Nasriachi <mickey\@cpan.org>"
],
"dist_name" => "MetaCPAN-Client",
- "dist_version" => "1.003000",
+ "dist_version" => "1.008000",
"license" => "perl",
"module_name" => "MetaCPAN::Client",
"recommends" => {},
@@ -31,6 +31,7 @@ my %module_build_args = (
"JSON::MaybeXS" => 0,
"Moo" => 0,
"Moo::Role" => 0,
+ "Safe::Isa" => 0,
"Search::Elasticsearch" => "1.10",
"Search::Elasticsearch::Scroll" => 0,
"Try::Tiny" => 0,
@@ -44,6 +45,8 @@ my %module_build_args = (
"IPC::Open3" => 0,
"Test::Fatal" => 0,
"Test::More" => 0,
+ "Test::Requires" => 0,
+ "base" => 0,
"perl" => "5.006"
}
);
@@ -53,9 +56,11 @@ my %fallback_build_requires = (
"File::Spec" => 0,
"IO::Handle" => 0,
"IPC::Open3" => 0,
- "Module::Build" => "0.3601",
+ "Module::Build" => "0.28",
"Test::Fatal" => 0,
"Test::More" => 0,
+ "Test::Requires" => 0,
+ "base" => 0,
"perl" => "5.006"
);
@@ -1,5 +1,34 @@
Revision history for MetaCPAN-Client (previously MetaCPAN-API)
+1.008000 22.11.14
+ * RT #99498: added API for 'match_all' queries via all($type) (oalders, mickeyn)
+ * GH #21: make 'domain' and 'version' settable via new() (oalders)
+ * RT #94491: document nested queries (neilb, mickeyn)
+
+1.007001 09.10.14
+ * GH #18: HTTP::Tiny::Mech and WWW::Mechanize::Cached downgraded to being non-essential for tests (kentnl)
+ * GH #19: Include 'metadata' in known_fields for ::Release (kentnl)
+
+1.007000 14.08.14
+ * Ensure passing user specified ua values to all parts internally,
+ including to Elasticsearch (kentnl) GH #17 RT#95796
+ * Entity consuming roles now have a 'client' attribute which will lazy build,
+ or reference the MetaCPAN::Client that created them via new_from_request (kentnl) GH #17
+
+1.006000 24.06.14
+ * Add 'recent' functionality (latest releases)
+
+1.005000 09.06.14
+ * Add Pod object to allow direct POD fetching (reneeb)
+ * Support single element without wrapping arrayref in structures
+ * Updated documents - basic/complex search links and wording (tsibley)
+
+1.004001 27.05.14
+ * correct rev_deps query
+
+1.004000 27.05.14
+ * reworked ResultSet to allow RS in non-scrolled searches.
+
1.003000 05.05.14
* Add proper POD fetching from module/file objects.
* GH #1: Switch from JSON.pm to JSON::MaybeXS.
@@ -22,7 +22,7 @@ This is free software, licensed under:
Version 1, February 1989
Copyright (C) 1989 Free Software Foundation, Inc.
- 51 Franklin St, Suite 500, Boston, MA 02110-1335 USA
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -1,4 +1,4 @@
-# This file was automatically generated by Dist::Zilla::Plugin::Manifest v5.015.
+# This file was automatically generated by Dist::Zilla::Plugin::Manifest v5.020.
Build.PL
Changes
LICENSE
@@ -12,17 +12,24 @@ examples/author.pl
examples/author_releases.pl
examples/complex-either-and.pl
examples/complex-either-not.pl
+examples/complex-nested-either-and.pl
examples/complex.pl
examples/distribution.pl
+examples/module.pl
+examples/pod.pl
+examples/recent.pl
+examples/recent_today.pl
examples/release.pl
examples/rev_deps-recursive.pl
examples/rev_deps.pl
+examples/totals.pl
lib/MetaCPAN/Client.pm
lib/MetaCPAN/Client/Author.pm
lib/MetaCPAN/Client/Distribution.pm
lib/MetaCPAN/Client/Favorite.pm
lib/MetaCPAN/Client/File.pm
lib/MetaCPAN/Client/Module.pm
+lib/MetaCPAN/Client/Pod.pm
lib/MetaCPAN/Client/Rating.pm
lib/MetaCPAN/Client/Release.pm
lib/MetaCPAN/Client/Request.pm
@@ -37,6 +44,7 @@ t/api/distribution.t
t/api/favorite.t
t/api/file.t
t/api/module.t
+t/api/pod.t
t/api/rating.t
t/api/release.t
t/author-critic.t
@@ -46,3 +54,4 @@ t/release-pod-coverage.t
t/release-pod-syntax.t
t/request.t
t/resultset.t
+t/ua_trap.t
@@ -7,15 +7,17 @@ build_requires:
File::Spec: 0
IO::Handle: 0
IPC::Open3: 0
- Module::Build: 0.3601
+ Module::Build: 0.28
Test::Fatal: 0
Test::More: 0
+ Test::Requires: 0
+ base: 0
perl: 5.006
configure_requires:
- ExtUtils::MakeMaker: 6.30
- Module::Build: 0.3601
+ ExtUtils::MakeMaker: 0
+ Module::Build: 0.28
dynamic_config: 0
-generated_by: 'Dist::Zilla version 5.015, CPAN::Meta::Converter version 2.120921'
+generated_by: 'Dist::Zilla version 5.020, CPAN::Meta::Converter version 2.142060'
license: perl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -30,11 +32,12 @@ requires:
JSON::MaybeXS: 0
Moo: 0
Moo::Role: 0
+ Safe::Isa: 0
Search::Elasticsearch: 1.10
Search::Elasticsearch::Scroll: 0
Try::Tiny: 0
strict: 0
warnings: 0
resources:
- repository: git://github.com/CPAN-API/metacpan-client.git
-version: 1.003000
+ repository: https://github.com/CPAN-API/metacpan-client.git
+version: 1.008000
@@ -1,11 +1,11 @@
-# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v5.015.
+# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v5.020.
use strict;
use warnings;
-use ExtUtils::MakeMaker 6.30;
+use ExtUtils::MakeMaker ;
@@ -13,11 +13,11 @@ my %WriteMakefileArgs = (
"ABSTRACT" => "A comprehensive, DWIM-featured client to the MetaCPAN API",
"AUTHOR" => "Sawyer X <xsawyerx\@cpan.org>, Mickey Nasriachi <mickey\@cpan.org>",
"BUILD_REQUIRES" => {
- "Module::Build" => "0.3601"
+ "Module::Build" => "0.28"
},
"CONFIGURE_REQUIRES" => {
- "ExtUtils::MakeMaker" => "6.30",
- "Module::Build" => "0.3601"
+ "ExtUtils::MakeMaker" => 0,
+ "Module::Build" => "0.28"
},
"DISTNAME" => "MetaCPAN-Client",
"EXE_FILES" => [],
@@ -29,6 +29,7 @@ my %WriteMakefileArgs = (
"JSON::MaybeXS" => 0,
"Moo" => 0,
"Moo::Role" => 0,
+ "Safe::Isa" => 0,
"Search::Elasticsearch" => "1.10",
"Search::Elasticsearch::Scroll" => 0,
"Try::Tiny" => 0,
@@ -40,9 +41,11 @@ my %WriteMakefileArgs = (
"IO::Handle" => 0,
"IPC::Open3" => 0,
"Test::Fatal" => 0,
- "Test::More" => 0
+ "Test::More" => 0,
+ "Test::Requires" => 0,
+ "base" => 0
},
- "VERSION" => "1.003000",
+ "VERSION" => "1.008000",
"test" => {
"TESTS" => "t/*.t t/api/*.t"
}
@@ -56,14 +59,17 @@ my %FallbackPrereqs = (
"IO::Handle" => 0,
"IPC::Open3" => 0,
"JSON::MaybeXS" => 0,
- "Module::Build" => "0.3601",
+ "Module::Build" => "0.28",
"Moo" => 0,
"Moo::Role" => 0,
+ "Safe::Isa" => 0,
"Search::Elasticsearch" => "1.10",
"Search::Elasticsearch::Scroll" => 0,
"Test::Fatal" => 0,
"Test::More" => 0,
+ "Test::Requires" => 0,
"Try::Tiny" => 0,
+ "base" => 0,
"strict" => 0,
"warnings" => 0
);
@@ -3,13 +3,13 @@ NAME
API
VERSION
- version 1.003000
+ version 1.008000
SYNOPSIS
# simple usage
my $mcpan = MetaCPAN::Client->new();
my $author = $mcpan->author('XSAWYERX');
- my $dist = $mcpan->distribuion('MetaCPAN-Client');
+ my $dist = $mcpan->distribution('MetaCPAN-Client');
# advanced usage with cache (contributed by Kent Fredric)
use CHI;
@@ -76,8 +76,8 @@ METHODS
search.
distribution
- my $dist = $mcpan->dist('MetaCPAN-Client');
- my $dist = $mcpan->dist($search_spec);
+ my $dist = $mcpan->distribution('MetaCPAN-Client');
+ my $dist = $mcpan->distribution($search_spec);
Finds a distribution by either its distribution name or by a search spec
defined by a hash reference. Since it is common to many other searches,
@@ -113,14 +113,31 @@ METHODS
reverse_dependencies
my $deps = $mcpan->reverse_dependencies('ElasticSearch');
- Return an array (ref) of MetaCPAN::Client::Release matching all releases
- that are dependent on a given module.
+ all MetaCPAN::Client::Release objects of releases that are dependent on
+ a given module, returned as MetaCPAN::Client::ResultSet.
rev_deps
Alias to "reverse_dependencies" described above.
+ recent
+ my $recent = $mcpan->recent(10);
+ my $recent = $mcpan->recent('today');
+
+ return the latest N releases, or all releases from today.
+
+ returns a MetaCPAN::Client::ResultSet of MetaCPAN::Client::Release.
+
pod
- Not implemented yet.
+ Get POD for given file/module name. returns a MetaCPAN::Client::POD
+ object, which supports various output formats (html, plain, x_pod &
+ x_markdown).
+
+ my $pod = $mcpan->pod('Moo')->html;
+
+ all
+ Retrieve all matches for authors/modules/distributions or releases.
+
+ my $all_releases = $mcpan->all('releases')
BUILDARGS
Internal construction wrapper. Do not use.
@@ -129,8 +146,7 @@ SEARCH SPEC
The hash-based search spec is common to many searches. It is quite
feature-rich and allows to disambiguate different types of searches.
- Simple
- Simple searches just contain keys and values:
+ Basic search specs just contain a hash of keys and values:
my $author = $mcpan->author( { name => 'Micha Nasriachi' } );
@@ -167,6 +183,20 @@ SEARCH SPEC
]
} );
+ If you want to do something even more complicated, You can also nest
+ your queries, e.g.:
+
+ my $gmail_daves_or_cpan_sams = $mcpan->author( {
+ either => [
+ { all => [ { name => 'Dave *' },
+ { email => '*gmail.com' } ]
+ },
+ { all => [ { name => 'Sam *' },
+ { email => '*cpan.org' } ]
+ },
+ ],
+ } );
+
NOT
If you want to filter out some of the results of an either/all query
adding a *NOT* filter condition, such as "not these", you can use the
@@ -5,7 +5,7 @@ license = Perl_5
copyright_holder = Sawyer X
copyright_year = 2014
-version = 1.003000
+version = 1.008000
[@Basic]
[PodSyntaxTests]
@@ -19,6 +19,11 @@ Search::Elasticsearch = 1.10
[AutoPrereqs]
skip = ^t::lib::Functions$
+[Prereqs::Soften]
+module = HTTP::Tiny::Mech
+module = WWW::Mechanize::Cached
+copy_to = develop.requires
+
[ModuleBuild]
[PodWeaver]
[ReadmeFromPod]
@@ -1,3 +1,6 @@
+
+# examples/author-country.pl
+
use strict;
use warnings;
use Data::Printer;
@@ -1,3 +1,7 @@
+
+
+# examples/author.pl
+
use strict;
use warnings;
use Data::Printer;
@@ -15,3 +19,4 @@ my %output = (
);
p %output;
+
@@ -1,3 +1,6 @@
+
+# examples/complex-either-and.pl
+
use strict;
use warnings;
use Data::Printer;
@@ -9,9 +12,7 @@ my $authors =
{ name => 'Dave *' },
{ name => 'David *' },
],
- all => [
- { email => '*gmail.com' }
- ],
+ all => { email => '*gmail.com' },
});
my %output = (
@@ -1,3 +1,6 @@
+
+# examples/complex-either-not.pl
+
use strict;
use warnings;
use Data::Printer;
@@ -16,4 +19,5 @@ my $authors =
});
print "\n";
-p ( $authors->total );
+my %out = ( TOTAL => $authors->total );
+p %out;
@@ -0,0 +1,26 @@
+
+# examples/complex-nested-either-and.pl
+
+use strict;
+use warnings;
+use Data::Printer;
+use MetaCPAN::Client;
+
+my $authors =
+ MetaCPAN::Client->new->author({
+ either => [
+ { all => [ { name => 'Dave *' },
+ { email => '*gmail.com' } ]
+ },
+ { all => [ { name => 'Sam *' },
+ { email => '*cpan.org' } ]
+ },
+ ],
+ });
+
+my %output = (
+ TOTAL => $authors->total,
+ NAMES => [ map { $authors->next->name } 0 .. 9 ],
+);
+
+p %output;
@@ -1,3 +1,6 @@
+
+# examples/complex.pl
+
use strict;
use warnings;
use Data::Printer;
@@ -1,3 +1,6 @@
+
+# examples/distribution.pl
+
use strict;
use warnings;
use Data::Printer;
@@ -0,0 +1,23 @@
+
+# examples/module.pl
+
+use strict;
+use warnings;
+use DDP;
+
+use MetaCPAN::Client;
+
+my $module =
+ MetaCPAN::Client->new->module('Moo');
+
+my %output = (
+ NAME => $module->name,
+ ABSTRACT => $module->abstract,
+ DESCRIPTION => $module->description,
+ RELEASE => $module->release,
+ AUTHOR => $module->author,
+ VERSION => $module->version,
+);
+
+p %output;
+
@@ -0,0 +1,11 @@
+
+# examples/pod.pl
+
+use strict;
+use warnings;
+use MetaCPAN::Client;
+
+my $pod =
+ MetaCPAN::Client->new->pod('Moo');
+
+print $pod->html;
@@ -0,0 +1,22 @@
+
+# examples/recent.pl
+
+use strict;
+use warnings;
+use Data::Printer;
+use MetaCPAN::Client;
+
+my $recent =
+ MetaCPAN::Client->new->recent(3);
+
+while ( my $rel = $recent->next ) {
+ my %output = (
+ NAME => $rel->name,
+ AUTHOR => $rel->author,
+ DATE => $rel->date,
+ VERSION => $rel->version,
+ );
+
+ p %output;
+}
+
@@ -0,0 +1,19 @@
+use strict;
+use warnings;
+use Data::Printer;
+use MetaCPAN::Client;
+
+my $recent =
+ MetaCPAN::Client->new->recent('today');
+
+while ( my $rel = $recent->next ) {
+ my %output = (
+ NAME => $rel->name,
+ AUTHOR => $rel->author,
+ DATE => $rel->date,
+ VERSION => $rel->version,
+ );
+
+ p %output;
+}
+
@@ -1,3 +1,6 @@
+
+# examples/rev_deps-recursive.pl
+
use strict;
use warnings;
use Term::ANSIColor;
@@ -5,7 +8,7 @@ use MetaCPAN::Client;
$|=1;
-my $dist = 'Hijk';
+my $dist = shift || 'Hijk';
my $mcpan = MetaCPAN::Client->new;
print "\n\n", colored( "* $dist", 'green' ), "\n";
@@ -17,16 +20,16 @@ sub dig {
my $res = $mcpan->reverse_dependencies($dist);
- for ( @{$res} ) {
+ while ( my $item = $res->next ) {
if ( $level ) {
printf "%s%s\n",
colored( '....' x $level, 'yellow' ),
- $_->distribution;
+ $item->distribution;
} else {
printf "\n>> %s\n",
- colored( $_->distribution, 'blue' );
+ colored( $item->distribution, 'blue' );
}
- dig( $_->distribution, $level + 1 );
+ dig( $item->distribution, $level + 1 );
}
-};
+}
@@ -1,17 +1,26 @@
+
+# examples/rev_deps.pl
+
use strict;
use warnings;
use Data::Printer;
use MetaCPAN::Client;
+my $module = shift || 'Hijk';
+
my $deps =
- MetaCPAN::Client->new->rev_deps('Hijk');
+ MetaCPAN::Client->new->rev_deps($module);
+
+my @output;
-my @output = (
- map +{
- name => $_->name,
- author => $_->author,
- },
- @{$deps},
-);
+while ( my $rel = $deps->next ) {
+ push @output => {
+ name => $rel->name,
+ author => $rel->author,
+ };
+}
+print "\n";
+my $title = "Reverse dependencies for 'Hijk':";
+p $title;
p @output;
@@ -0,0 +1,17 @@
+use strict;
+use warnings;
+use Data::Printer;
+use MetaCPAN::Client;
+
+my $mcpan = MetaCPAN::Client->new;
+
+my $all_authors = $mcpan->all('authors');
+my $all_dists = $mcpan->all('distributions');
+my $all_modules = $mcpan->all('modules');
+my $all_releases = $mcpan->all('releases');
+
+print "totals:\n";
+printf "authors : %d\n", $all_authors->total;
+printf "distributions : %d\n", $all_dists->total;
+printf "modules : %d\n", $all_modules->total;
+printf "releases : %d\n", $all_releases->total;
@@ -2,7 +2,7 @@ use strict;
use warnings;
package MetaCPAN::Client::Author;
# ABSTRACT: An Author data object
-$MetaCPAN::Client::Author::VERSION = '1.003000';
+$MetaCPAN::Client::Author::VERSION = '1.008000';
use Moo;
with 'MetaCPAN::Client::Role::Entity';
@@ -29,10 +29,7 @@ sub releases {
my $self = shift;
my $id = $self->pauseid;
- require MetaCPAN::Client;
-
- return
- MetaCPAN::Client->new->release({
+ return $self->client->release({
all => [
{ author => $id },
{ status => 'latest' },
@@ -46,13 +43,15 @@ __END__
=pod
+=encoding UTF-8
+
=head1 NAME
MetaCPAN::Client::Author - An Author data object
=head1 VERSION
-version 1.003000
+version 1.008000
=head1 DESCRIPTION
@@ -2,7 +2,7 @@ use strict;
use warnings;
package MetaCPAN::Client::Distribution;
# ABSTRACT: A Distribution data object
-$MetaCPAN::Client::Distribution::VERSION = '1.003000';
+$MetaCPAN::Client::Distribution::VERSION = '1.008000';
use Moo;
with 'MetaCPAN::Client::Role::Entity';
@@ -29,13 +29,15 @@ __END__
=pod
+=encoding UTF-8
+
=head1 NAME
MetaCPAN::Client::Distribution - A Distribution data object
=head1 VERSION
-version 1.003000
+version 1.008000
=head1 DESCRIPTION
@@ -2,7 +2,7 @@ use strict;
use warnings;
package MetaCPAN::Client::Favorite;
# ABSTRACT: A Favorite data object
-$MetaCPAN::Client::Favorite::VERSION = '1.003000';
+$MetaCPAN::Client::Favorite::VERSION = '1.008000';
use Moo;
with 'MetaCPAN::Client::Role::Entity';
@@ -29,13 +29,15 @@ __END__
=pod
+=encoding UTF-8
+
=head1 NAME
MetaCPAN::Client::Favorite - A Favorite data object
=head1 VERSION
-version 1.003000
+version 1.008000
=head1 DESCRIPTION
@@ -2,7 +2,7 @@ use strict;
use warnings;
package MetaCPAN::Client::File;
# ABSTRACT: A File data object
-$MetaCPAN::Client::File::VERSION = '1.003000';
+$MetaCPAN::Client::File::VERSION = '1.008000';
use Moo;
use Carp;
@@ -52,13 +52,15 @@ __END__
=pod
+=encoding UTF-8
+
=head1 NAME
MetaCPAN::Client::File - A File data object
=head1 VERSION
-version 1.003000
+version 1.008000
=head1 DESCRIPTION
@@ -2,7 +2,7 @@ use strict;
use warnings;
package MetaCPAN::Client::Module;
# ABSTRACT: A Module data object
-$MetaCPAN::Client::Module::VERSION = '1.003000';
+$MetaCPAN::Client::Module::VERSION = '1.008000';
use Moo;
extends 'MetaCPAN::Client::File';
@@ -12,13 +12,15 @@ __END__
=pod
+=encoding UTF-8
+
=head1 NAME
MetaCPAN::Client::Module - A Module data object
=head1 VERSION
-version 1.003000
+version 1.008000
=head1 DESCRIPTION
@@ -0,0 +1,108 @@
+use strict;
+use warnings;
+package MetaCPAN::Client::Pod;
+# ABSTRACT: A Pod object
+$MetaCPAN::Client::Pod::VERSION = '1.008000';
+use Moo;
+
+has name => ( is => 'ro', required => 1 );
+
+my @known_formats = qw<
+ html plain x_pod x_markdown
+>;
+
+foreach my $format (@known_formats) {
+ has $format => (
+ is => 'ro',
+ lazy => 1,
+ default => sub {
+ my $self = shift;
+ return $self->_request( $format );
+ },
+ );
+}
+
+sub _request {
+ my $self = shift;
+ my $ctype = shift || "plain";
+
+ $ctype =~ s/_/-/;
+
+ my $name = $self->name;
+
+ require MetaCPAN::Client::Request;
+
+ return
+ MetaCPAN::Client::Request->new->fetch(
+ "pod/${name}?content-type=text/${ctype}"
+ );
+}
+
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+MetaCPAN::Client::Pod - A Pod object
+
+=head1 VERSION
+
+version 1.008000
+
+=head1 SYNOPSIS
+
+ use strict;
+ use warnings;
+ use MetaCPAN::Client;
+
+ my $pod = MetaCPAN::Client->new->pod('Moo');
+
+ print $pod->html;
+
+=head1 DESCRIPTION
+
+=head1 ATTRIBUTES
+
+=head2 name
+
+=head2 x_pod
+
+=head2 html
+
+=head2 x_markdown
+
+=head2 plain
+
+Get the plaintext version of the documentation
+
+ $pod = MetaCPAN::Client->new->pod( "MetaCPAN::Client" );
+ print $pod->plain;
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+Sawyer X <xsawyerx@cpan.org>
+
+=item *
+
+Mickey Nasriachi <mickey@cpan.org>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Sawyer X.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
@@ -2,7 +2,7 @@ use strict;
use warnings;
package MetaCPAN::Client::Rating;
# ABSTRACT: A Rating data object
-$MetaCPAN::Client::Rating::VERSION = '1.003000';
+$MetaCPAN::Client::Rating::VERSION = '1.008000';
use Moo;
with 'MetaCPAN::Client::Role::Entity';
@@ -31,13 +31,15 @@ __END__
=pod
+=encoding UTF-8
+
=head1 NAME
MetaCPAN::Client::Rating - A Rating data object
=head1 VERSION
-version 1.003000
+version 1.008000
=head1 DESCRIPTION
@@ -2,7 +2,7 @@ use strict;
use warnings;
package MetaCPAN::Client::Release;
# ABSTRACT: A Release data object
-$MetaCPAN::Client::Release::VERSION = '1.003000';
+$MetaCPAN::Client::Release::VERSION = '1.008000';
use Moo;
with 'MetaCPAN::Client::Role::Entity';
@@ -10,7 +10,7 @@ with 'MetaCPAN::Client::Role::Entity';
my @known_fields = qw<
resources status date author maturity dependency id authorized
download_url first archive version name version_numified license
- distribution stat provides tests abstract
+ distribution stat provides tests abstract metadata
>;
foreach my $field (@known_fields) {
@@ -32,57 +32,61 @@ __END__
=pod
+=encoding UTF-8
+
=head1 NAME
MetaCPAN::Client::Release - A Release data object
=head1 VERSION
-version 1.003000
+version 1.008000
=head1 DESCRIPTION
=head1 ATTRIBUTES
-#head2 resources
+=head2 resources
+
+=head2 status
-#head2 status
+=head2 date
-#head2 date
+=head2 author
-#head2 author
+=head2 maturity
-#head2 maturity
+=head2 dependency
-#head2 dependency
+=head2 id
-#head2 id
+=head2 authorized
-#head2 authorized
+=head2 download_url
-#head2 download_url
+=head2 first
-#head2 first
+=head2 archive
-#head2 archive
+=head2 version
-#head2 version
+=head2 name
-#head2 name
+=head2 version_numified
-#head2 version_numified
+=head2 license
-#head2 license
+=head2 distribution
-#head2 distribution
+=head2 stat
-#head2 stat
+=head2 provides
-#head2 provides
+=head2 tests
-#head2 tests
+=head2 abstract
-#head2 abstract
+=head2 metadata
=head1 AUTHORS
@@ -2,7 +2,7 @@ use strict;
use warnings;
package MetaCPAN::Client::Request;
# ABSTRACT: Object used for making requests to MetaCPAN
-$MetaCPAN::Client::Request::VERSION = '1.003000';
+$MetaCPAN::Client::Request::VERSION = '1.008000';
use Moo;
use Carp;
use JSON::MaybeXS qw<decode_json encode_json>;
@@ -30,10 +30,17 @@ has base_url => (
},
);
+has _user_ua => (
+ init_arg => 'ua',
+ is => 'ro',
+ predicate => '_has_user_ua',
+);
+
has ua => (
- is => 'ro',
- lazy => 1,
- builder => '_build_ua',
+ init_arg => undef,
+ is => 'ro',
+ lazy => 1,
+ builder => '_build_ua',
);
has ua_args => (
@@ -45,6 +52,13 @@ has ua_args => (
sub _build_ua {
my $self = shift;
+ # This level of indirection is so that if a user has not specified a custom UA
+ # MetaCPAN::Client and ElasticSearch will have their own UA's
+ #
+ # But if the user **has** specified a custom UA, that UA is used for both.
+ if ( $self->_has_user_ua ) {
+ return $self->_user_ua;
+ }
return HTTP::Tiny->new( @{ $self->ua_args } );
}
@@ -72,6 +86,7 @@ sub ssearch {
nodes => $self->domain,
cxn_pool => 'Static::NoPing',
send_get_body_as => 'POST',
+ ( $self->_has_user_ua ? ( handle => $self->_user_ua ) : () )
);
my $scroller = Search::Elasticsearch::Scroll->new(
@@ -120,7 +135,9 @@ sub _build_body {
my $self = shift;
my $args = shift;
- my $query = _build_query_rec($args);
+ my $query = $args->{__MATCH_ALL__}
+ ? { match_all => {} }
+ : _build_query_rec($args);
return +{ query => $query };
}
@@ -140,6 +157,7 @@ sub _build_query_rec {
KEY: for my $k ( qw/ all either not / ) {
my $v = delete $args->{$k} || next KEY;
+ ref $v eq 'HASH' and $v = [ $v ];
ref $v eq 'ARRAY' or croak "invalid value for key $k";
undef $basic_element;
@@ -179,13 +197,15 @@ __END__
=pod
+=encoding UTF-8
+
=head1 NAME
MetaCPAN::Client::Request - Object used for making requests to MetaCPAN
=head1 VERSION
-version 1.003000
+version 1.008000
=head1 ATTRIBUTES
@@ -2,20 +2,10 @@ use strict;
use warnings;
package MetaCPAN::Client::ResultSet;
# ABSTRACT: A Result Set
-$MetaCPAN::Client::ResultSet::VERSION = '1.003000';
+$MetaCPAN::Client::ResultSet::VERSION = '1.008000';
use Moo;
use Carp;
-has scroller => (
- is => 'ro',
- isa => sub {
- ref $_[0] eq 'Search::Elasticsearch::Scroll'
- or croak 'scroller must be an Search::Elasticsearch::Scroll object';
- },
- handles => ['total'],
- required => 1,
-);
-
has type => (
is => 'ro',
isa => sub {
@@ -26,31 +16,64 @@ has type => (
required => 1,
);
-has facets => (
+# in case we're returning from a scrolled search
+has scroller => (
+ is => 'ro',
+ isa => sub {
+ use Safe::Isa;
+ $_[0]->$_isa('Search::Elasticsearch::Scroll')
+ or croak 'scroller must be an Search::Elasticsearch::Scroll object';
+ },
+ predicate => 'has_scroller',
+);
+
+# in case we're returning from a fetch
+has items => (
+ is => 'ro',
+ isa => sub {
+ ref $_[0] eq 'ARRAY'
+ or croak 'items must be an array ref';
+ },
+);
+
+has total => (
is => 'ro',
- lazy => 1,
- builder => '_get_facets',
+ default => sub {
+ my $self = shift;
+
+ return $self->has_scroller ? $self->scroller->total
+ : scalar @{ $self->items };
+ },
);
-sub _get_facets {
- my $self = shift;
+sub BUILDARGS {
+ my ( $class, %args ) = @_;
- return $self->scroller->facets || {};
-}
+ exists $args{scroller} or exists $args{items}
+ or croak 'ResultSet must get either scroller or items';
+
+ exists $args{scroller} and exists $args{items}
+ and croak 'ResultSet must get either scroller or items, not both';
+ return \%args;
+}
sub next {
my $self = shift;
- my $result = $self->scroller->next;
+ my $result = $self->has_scroller ? $self->scroller->next
+ : shift @{ $self->items };
- defined $result
- or return;
+ defined $result or return;
my $class = 'MetaCPAN::Client::' . ucfirst $self->type;
-
return $class->new_from_request( $result->{'_source'} || $result->{'fields'} );
}
+sub facets {
+ my $self = shift;
+
+ return $self->has_scroller ? $self->scroller->facets : {};
+}
1;
@@ -58,25 +81,31 @@ __END__
=pod
+=encoding UTF-8
+
=head1 NAME
MetaCPAN::Client::ResultSet - A Result Set
=head1 VERSION
-version 1.003000
+version 1.008000
=head1 DESCRIPTION
Object representing a result from Elastic Search. This is used for the complex
-(as in non-simple) queries to MetaCPAN. It provides easy access to the scroller
-and facets.
+(as in L<non-simple/MetaCPAN::Client/"SEARCH SPEC">) queries to MetaCPAN. It
+provides easy access to the scroller and facets.
=head1 ATTRIBUTES
=head2 scroller
-An L<Search::Elasticsearch::Scroll> object
+An L<Search::Elasticsearch::Scroll> object.
+
+=head2 items
+
+An arrayref of items to manually scroll over, instead of a scroller object.
=head2 type
@@ -112,6 +141,14 @@ Iterator call to fetch the next result set object.
Iterator call to fetch the total amount of objects available in result set.
+=head2 has_scroller
+
+Predicate for ES scroller presence.
+
+=head2 BUILDARGS
+
+Double checks construction of objects. You should never run this yourself.
+
=head1 AUTHORS
=over 4
@@ -2,7 +2,7 @@ use strict;
use warnings;
package MetaCPAN::Client::Role::Entity;
# ABSTRACT: A role for MetaCPAN entities
-$MetaCPAN::Client::Role::Entity::VERSION = '1.003000';
+$MetaCPAN::Client::Role::Entity::VERSION = '1.008000';
use Moo::Role;
has data => (
@@ -10,10 +10,22 @@ has data => (
required => 1,
);
+has client => (
+ is => 'ro',
+ lazy => 1,
+ builder => '_build_client',
+);
+
+sub _build_client {
+ require MetaCPAN::Client;
+ return MetaCPAN::Client->new();
+}
+
sub new_from_request {
- my ( $class, $request ) = @_;
+ my ( $class, $request, $client ) = @_;
return $class->new(
+ ( defined $client ? ( client => $client ) : () ),
data => {
map +( defined $request->{$_} ? ( $_ => $request->{$_} ) : () ),
@{ $class->_known_fields }
@@ -27,13 +39,15 @@ __END__
=pod
+=encoding UTF-8
+
=head1 NAME
MetaCPAN::Client::Role::Entity - A role for MetaCPAN entities
=head1 VERSION
-version 1.003000
+version 1.008000
=head1 DESCRIPTION
@@ -2,7 +2,7 @@ use strict;
use warnings;
package MetaCPAN::Client;
# ABSTRACT: A comprehensive, DWIM-featured client to the MetaCPAN API
-$MetaCPAN::Client::VERSION = '1.003000';
+$MetaCPAN::Client::VERSION = '1.008000';
use Moo;
use Carp;
@@ -12,6 +12,7 @@ use MetaCPAN::Client::Distribution;
use MetaCPAN::Client::Module;
use MetaCPAN::Client::File;
use MetaCPAN::Client::Favorite;
+use MetaCPAN::Client::Pod;
use MetaCPAN::Client::Rating;
use MetaCPAN::Client::Release;
use MetaCPAN::Client::ResultSet;
@@ -30,6 +31,8 @@ sub BUILDARGS {
$args{'request'} ||= MetaCPAN::Client::Request->new(
( ua => $args{'ua'} ) x !! $args{'ua'},
+ $args{domain} ? ( domain => $args{domain} ) : (),
+ $args{version} ? ( version => $args{version} ) : (),
);
return \%args;
@@ -69,6 +72,13 @@ sub file {
return $self->_get( 'file', $path );
}
+sub pod {
+ my $self = shift;
+ my $name = shift;
+
+ return MetaCPAN::Client::Pod->new({ name => $name });
+}
+
#
# $api->rating({ dist => "Moose" })
# is equal to http://api.metacpan.org/v0/favorite/_search?q=distribution:Moose
@@ -128,6 +138,36 @@ sub reverse_dependencies {
*rev_deps = *reverse_dependencies;
+sub recent {
+ my $self = shift;
+ my $size = shift || 100;
+
+ $size eq 'today'
+ and return $self->_recent(
+ size => 1000,
+ filter => _filter_today()
+ );
+
+ $size =~ /^[0-9]+$/
+ and return $self->_recent( size => $size );
+
+ croak "recent: invalid size value";
+}
+
+sub all {
+ my $self = shift;
+ my $type = shift;
+
+ my $match_all = { __MATCH_ALL__ => 1 };
+
+ $type eq 'authors' and return $self->author( $match_all );
+ $type eq 'distributions' and return $self->distribution( $match_all );
+ $type eq 'modules' and return $self->module( $match_all );
+ $type eq 'releases' and return $self->release( $match_all );
+
+ croak "all: unsupported type";
+}
+
###
sub _get {
@@ -144,7 +184,7 @@ sub _get {
or croak sprintf( 'Failed to fetch %s (%s)', ucfirst($type), $arg );
my $class = 'MetaCPAN::Client::' . ucfirst($type);
- return $class->new_from_request($response);
+ return $class->new_from_request($response, $self);
}
sub _search {
@@ -197,23 +237,70 @@ sub _reverse_deps {
$res = $self->fetch(
'/search/reverse_dependencies/'.$dist,
{
- query => { match_all => {} },
- filter => { term => { 'release.status' => 'latest' } },
size => 5000,
+ query => { match_all => {} },
+ filter => {
+ and => [
+ { term => { 'release.status' => 'latest' } },
+ { term => { 'authorized' => \1 } },
+ ]
+ },
}
);
+ 1;
} or do {
warn $@;
- return [];
+ return _empty_result_set('release'),
};
- return +[
- map { MetaCPAN::Client::Release->new_from_request($_->{'_source'}) }
- @{ $res->{'hits'}{'hits'} }
- ];
+ return MetaCPAN::Client::ResultSet->new(
+ items => $res->{'hits'}{'hits'},
+ type => 'release',
+ );
}
+sub _recent {
+ my $self = shift;
+ my @args = @_;
+
+ my $res;
+
+ eval {
+ $res = $self->fetch(
+ '/release/_search',
+ {
+ from => 0,
+ query => { match_all => {} },
+ @args,
+ sort => [ { 'date' => { order => "desc" } } ],
+ }
+ );
+ 1;
+
+ } or do {
+ warn $@;
+ return _empty_result_set('release');
+ };
+
+ return MetaCPAN::Client::ResultSet->new(
+ items => $res->{'hits'}{'hits'},
+ type => 'release',
+ );
+}
+
+sub _filter_today {
+ return { range => { date => { from => "now/1d+0h" } } };
+}
+
+sub _empty_result_set {
+ my $type = shift;
+
+ return MetaCPAN::Client::ResultSet->new(
+ items => [],
+ type => $type,
+ );
+}
1;
@@ -221,20 +308,22 @@ __END__
=pod
+=encoding UTF-8
+
=head1 NAME
MetaCPAN::Client - A comprehensive, DWIM-featured client to the MetaCPAN API
=head1 VERSION
-version 1.003000
+version 1.008000
=head1 SYNOPSIS
# simple usage
my $mcpan = MetaCPAN::Client->new();
my $author = $mcpan->author('XSAWYERX');
- my $dist = $mcpan->distribuion('MetaCPAN-Client');
+ my $dist = $mcpan->distribution('MetaCPAN-Client');
# advanced usage with cache (contributed by Kent Fredric)
use CHI;
@@ -289,7 +378,7 @@ under C<SEARCH SPEC>.
Return a L<MetaCPAN::Client::Author> object on a simple search (PAUSE ID), or
a L<MetaCPAN::Client::ResultSet> object propagated with
-L<MetaCPAN::Client::Author> objects on a complex (search spec based) search.
+L<MetaCPAN::Client::Author> objects on a complex (L<search spec based|/"SEARCH SPEC">) search.
=head2 module
@@ -302,12 +391,12 @@ under C<SEARCH SPEC>.
Return a L<MetaCPAN::Client::Module> object on a simple search (module name), or
a L<MetaCPAN::Client::ResultSet> object propagated with
-L<MetaCPAN::Client::Module> objects on a complex (search spec based) search.
+L<MetaCPAN::Client::Module> objects on a complex (L<search spec based|/"SEARCH SPEC">) search.
=head2 distribution
- my $dist = $mcpan->dist('MetaCPAN-Client');
- my $dist = $mcpan->dist($search_spec);
+ my $dist = $mcpan->distribution('MetaCPAN-Client');
+ my $dist = $mcpan->distribution($search_spec);
Finds a distribution by either its distribution name or by a search spec
defined by a hash reference. Since it is common to many other searches, it is
@@ -315,7 +404,7 @@ explained below under C<SEARCH SPEC>.
Return a L<MetaCPAN::Client::Distribution> object on a simple search
(distribution name), or a L<MetaCPAN::Client::ResultSet> object propagated with
-L<MetaCPAN::Client::Distribution> objects on a complex (search spec based)
+L<MetaCPAN::Client::Distribution> objects on a complex (L<search spec based|/"SEARCH SPEC">)
search.
=head2 file
@@ -341,22 +430,41 @@ below under C<SEARCH SPEC>.
Return a L<MetaCPAN::Client::Release> object on a simple search (release name),
or a L<MetaCPAN::Client::ResultSet> object propagated with
-L<MetaCPAN::Client::Release> objects on a complex (search spec based) search.
+L<MetaCPAN::Client::Release> objects on a complex (L<search spec based|/"SEARCH SPEC">) search.
=head2 reverse_dependencies
my $deps = $mcpan->reverse_dependencies('ElasticSearch');
-Return an array (ref) of L<MetaCPAN::Client::Release> matching all
-releases that are dependent on a given module.
+all L<MetaCPAN::Client::Release> objects of releases that are dependent
+on a given module, returned as L<MetaCPAN::Client::ResultSet>.
=head2 rev_deps
Alias to C<reverse_dependencies> described above.
+=head2 recent
+
+ my $recent = $mcpan->recent(10);
+ my $recent = $mcpan->recent('today');
+
+return the latest N releases, or all releases from today.
+
+returns a L<MetaCPAN::Client::ResultSet> of L<MetaCPAN::Client::Release>.
+
=head2 pod
-Not implemented yet.
+Get POD for given file/module name.
+returns a L<MetaCPAN::Client::POD> object, which supports various output
+formats (html, plain, x_pod & x_markdown).
+
+ my $pod = $mcpan->pod('Moo')->html;
+
+=head2 all
+
+Retrieve all matches for authors/modules/distributions or releases.
+
+ my $all_releases = $mcpan->all('releases')
=head2 BUILDARGS
@@ -367,9 +475,7 @@ Internal construction wrapper. Do not use.
The hash-based search spec is common to many searches. It is quite
feature-rich and allows to disambiguate different types of searches.
-=head2 Simple
-
-Simple searches just contain keys and values:
+Basic search specs just contain a hash of keys and values:
my $author = $mcpan->author( { name => 'Micha Nasriachi' } );
@@ -408,6 +514,20 @@ key:
]
} );
+If you want to do something even more complicated,
+You can also nest your queries, e.g.:
+
+ my $gmail_daves_or_cpan_sams = $mcpan->author( {
+ either => [
+ { all => [ { name => 'Dave *' },
+ { email => '*gmail.com' } ]
+ },
+ { all => [ { name => 'Sam *' },
+ { email => '*cpan.org' } ]
+ },
+ ],
+ } );
+
=head2 NOT
If you want to filter out some of the results of an either/all query
@@ -2,9 +2,9 @@ use 5.006;
use strict;
use warnings;
-# this test was generated with Dist::Zilla::Plugin::Test::Compile 2.040
+# this test was generated with Dist::Zilla::Plugin::Test::Compile 2.045
-use Test::More tests => 11 + ($ENV{AUTHOR_TESTING} ? 1 : 0);
+use Test::More tests => 12 + ($ENV{AUTHOR_TESTING} ? 1 : 0);
@@ -15,6 +15,7 @@ my @module_files = (
'MetaCPAN/Client/Favorite.pm',
'MetaCPAN/Client/File.pm',
'MetaCPAN/Client/Module.pm',
+ 'MetaCPAN/Client/Pod.pm',
'MetaCPAN/Client/Rating.pm',
'MetaCPAN/Client/Release.pm',
'MetaCPAN/Client/Request.pm',
@@ -0,0 +1,16 @@
+#!perl
+
+use strict;
+use warnings;
+use Test::More tests => 5;
+
+use t::lib::Functions;
+
+my $mc = mcpan();
+can_ok( $mc, 'pod' );
+
+my $pod = $mc->pod('MetaCPAN::API');
+isa_ok( $pod, 'MetaCPAN::Client::Pod' );
+can_ok( $pod, qw<html x_pod x_markdown plain name> );
+like( $pod->x_pod, qr/=head1/, 'got pod' );
+
@@ -3,7 +3,8 @@
use strict;
use warnings;
-use Test::More tests => 7;
+use Test::More tests => 9;
+use MetaCPAN::Client;
use MetaCPAN::Client::Request;
my $req = MetaCPAN::Client::Request->new( domain => 'mydomain', version => 'z' );
@@ -27,3 +28,6 @@ is_deeply(
'Correct UA args',
);
+my $client = MetaCPAN::Client->new( domain => 'foo', version => 'bar' );
+is ( $client->request->domain, 'foo', 'domain set in request' );
+is ( $client->request->version, 'bar', 'version set in request' );
@@ -7,11 +7,17 @@ use Test::More tests => 3;
use Test::Fatal;
use MetaCPAN::Client::ResultSet;
+{
+ package MetaCPAN::Client::Test::ScrollerZ;
+ use base 'Search::Elasticsearch::Scroll'; # < 5.10 FTW (except, no)
+ sub total {0}
+}
+
like(
exception {
MetaCPAN::Client::ResultSet->new(
type => 'failZZ',
- scroller => bless {}, 'Search::Elasticsearch::Scroll',
+ scroller => bless {}, 'MetaCPAN::Client::Test::ScrollerZ',
)
},
qr/Invalid type/,
@@ -0,0 +1,51 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+# ABSTRACT: Make sure passed value of UA gets used for things.
+
+use Test::Requires {
+ 'WWW::Mechanize::Cached' => 0,
+ 'HTTP::Tiny::Mech' => 0,
+};
+use Test::Fatal qw( exception );
+
+{
+
+ package TrapUA;
+ use Moo;
+ extends 'HTTP::Tiny::Mech';
+
+ sub mechua {
+ require WWW::Mechanize::Cached;
+ return WWW::Mechanize::Cached->new();
+ }
+}
+
+{
+ require HTTP::Tiny;
+ no warnings "redefine";
+ *HTTP::Tiny::request = sub {
+ my ( $self, @args ) = @_;
+ die "Illegal use of HTTP::Tiny" . pp( \@args );
+ };
+}
+use MetaCPAN::Client;
+
+my $e;
+is(
+ $e = exception {
+ my $client = MetaCPAN::Client->new( ua => TrapUA->new() );
+
+ my $a = $client->author('KENTNL');
+ my $releases = $a->releases;
+ },
+ undef,
+ "No illegal methods called"
+);
+
+if ($e) { diag explain $e }
+
+done_testing;
+