MetaCPAN-Client-2.031000000755001750001750 014520270707 14434 5ustar00mickeymickey000000000000README100644001750001750 3401514520270707 15420 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000NAME MetaCPAN::Client - A comprehensive, DWIM-featured client to the MetaCPAN API VERSION version 2.031000 SYNOPSIS # simple usage my $mcpan = MetaCPAN::Client->new(); my $author = $mcpan->author('XSAWYERX'); my $dist = $mcpan->distribution('MetaCPAN-Client'); # advanced usage with cache (contributed by Kent Fredric) use CHI; use WWW::Mechanize::Cached; use HTTP::Tiny::Mech; use MetaCPAN::Client; my $mcpan = MetaCPAN::Client->new( ua => HTTP::Tiny::Mech->new( mechua => WWW::Mechanize::Cached->new( cache => CHI->new( driver => 'File', root_dir => '/tmp/metacpan-cache', ), ), ), ); # now $mcpan caches results DESCRIPTION This is a hopefully-complete API-compliant client to MetaCPAN (https://metacpan.org) with DWIM capabilities, to make your life easier. ATTRIBUTES request Internal attribute representing the request object making the request to MetaCPAN and analyzing the results. You probably don't want to set this, nor should you have any usage of it. ua If provided, MetaCPAN::Client::Request will use the user agent object instead of the default, which is HTTP::Tiny. Then it can be used to fetch the user agent object used by MetaCPAN::Client::Request. domain If given, will be used to alter the API domain. debug If given, errors will include some low-level detailed message. METHODS author my $author = $mcpan->author('XSAWYERX'); my $author = $mcpan->author($search_spec); Finds an author by either its PAUSE ID or by a search spec defined by a hash reference. Since it is common to many other searches, it is explained below under SEARCH SPEC. Returns a MetaCPAN::Client::Author object on a simple search (PAUSE ID), or a MetaCPAN::Client::ResultSet object populated with MetaCPAN::Client::Author objects on a complex (search spec based) search. cover my $cover = $mcpan->cover('Moose-2.2007'); Returns a MetaCPAN::Client::Cover object. distribution 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 explained below under SEARCH SPEC. Returns a MetaCPAN::Client::Distribution object on a simple search (distribution name), or a MetaCPAN::Client::ResultSet object populated with MetaCPAN::Client::Distribution objects on a complex (search spec based) search. file Returns a MetaCPAN::Client::File object. favorite my $favorite = $mcpan->favorite({ distribution => 'Moose' }); Returns a MetaCPAN::Client::ResultSet object containing MetaCPAN::Client::Favorite results. rating my $rating = $mcpan->rating({ distribution => 'Moose' }); Returns a MetaCPAN::Client::ResultSet object containing MetaCPAN::Client::Rating results. release my $release = $mcpan->release('MetaCPAN-Client'); my $release = $mcpan->release($search_spec); Finds a release 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 explained below under SEARCH SPEC. Returns a MetaCPAN::Client::Release object on a simple search (release name), or a MetaCPAN::Client::ResultSet object populated with MetaCPAN::Client::Release objects on a complex (search spec based) search. mirror my $mirror = $mcpan->mirror('kr.freebsd.org'); Returns a MetaCPAN::Client::Mirror object. module my $module = $mcpan->module('MetaCPAN::Client'); my $module = $mcpan->module($search_spec); Finds a module by either its module name or by a search spec defined by a hash reference. Since it is common to many other searches, it is explained below under SEARCH SPEC. Returns a MetaCPAN::Client::Module object on a simple search (module name), or a MetaCPAN::Client::ResultSet object populated with MetaCPAN::Client::Module objects on a complex (search spec based) search. package my $package = $mcpan->package('MooseX::Types'); Returns a MetaCPAN::Client::Package object. permission my $permission = $mcpan->permission('MooseX::Types'); Returns a MetaCPAN::Client::Permission object. reverse_dependencies my $deps = $mcpan->reverse_dependencies('Search::Elasticsearch'); all MetaCPAN::Client::Release objects of releases that are directly dependent on a given module, returned as MetaCPAN::Client::ResultSet. rev_deps Alias to reverse_dependencies described above. autocomplete my $ac = $mcpan->autocomplete('Danc'); Call the search/autocomplete endpoint with a query string. Returns an array reference. autocomplete_suggest my $ac = $mcpan->autocomplete_suggest('Moo'); Call the search/autocomplete/suggest endpoint with a query string. Returns an array reference. 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 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; my $pod = $mcpan->pod('Moo', { url_prefix => $prefix })->html; download_url Retrieve information from the 'download_url' endpoint my $download_url = $mcpan->download_url($distro, [$version_or_range, $dev]); # request the last available version my $download_url = $mcpan->download_url('Moose'); # request an older version my $download_url = $mcpan->download_url('Moose', '1.01'); # using a range my $download_url = $mcpan->download_url('Moose', '<=1.01'); my $download_url = $mcpan->download_url('Moose', '>1.01,<=2.00'); Range operators are '== != <= >= < > !'. You can use a comma ',' to add multiple rules. # requesting dev release my $download_url = $mcpan->download_url('Moose', '>1.01', 1); Returns a MetaCPAN::Client::DownloadURL object all Retrieve all matches for authors/modules/distributions/favorites or releases. my $all_releases = $mcpan->all('releases') When called with a second parameter containing a hash ref, will support the following keys: fields See SEARCH PARAMS. my $all_releases = $mcpan->all('releases', { fields => [...] }) _source See SEARCH PARAMS. my $all_releases = $mcpan->all('releases', { _source => [...] }) es_filter Pass a raw Elasticsearch filter structure to reduce the number of elements returned by the query. my $some_releases = $mcpan->all('releases', { es_filter => {...} }) BUILDARGS Internal construction wrapper. Do not use. SEARCH PARAMS Most searches take params as an optional hash-ref argument. these params will be passed to the search action. In non-scrolled searches, 'fields' filter is the only supported parameter ATM. fields Filter the fields to reduce the amount of data pulled from MetaCPAN. can be passed as a csv list or an array ref. my $module = $mcpan->module('Moose', { fields => "version,author" }); my $module = $mcpan->module('Moose', { fields => [qw/version author/] }); _source Note: this param and its description are a bit too Elasticsearch specific. just like 'es_filter' - use only if you know what you're dealing with. Some fields are not indexed in Elasticsearch but stored as part of the entire document. These fields can still be read, but without the internal Elasticsearch optimizations and the server will internally read the whole document. Why do we even need those? because we don't index everything and some things we can't to begin with (like non-leaf fields that hold a structure) my $module = $mcpan->all('releases', { _source => "stat" }); scroller_time Note: please use with caution. This parameter will set the maximum lifetime of the Elasticsearch scroller on the server (default = '5m'). Normally you do not need to set this value (as tweaking this value can affect resources on the server). In case you do, you probably need to check the efficiency of your code/queries. (Feel free to reach out to us for assistance). my $module = $mcpan->all('releases', { scroller_time => '3m' }); scroller_size Note: please use with caution. This parameter will set the buffer size to be pulled from Elasticsearch when scrolling (default = 1000). This will affect query performance and memory usage, but you will still get an iterator back to fetch one object at a time. my $module = $mcpan->all('releases', { scroller_size => 500 }); sort Pass a raw Elasticsearch sort specification for the query. my $some_releases = $mcpan->all('releases', { sort => [{ date => { order => 'desc' } }] }) Note: this param and is a bit too specific to Elasticsearch. Just like "es_filter", only use this if you know what you're dealing with. SEARCH SPEC The hash-based search spec is common to many searches. It is quite feature-rich and allows you to disambiguate different types of searches. Basic search specs just contain a hash of keys and values: my $author = $mcpan->author( { name => 'Micha Nasriachi' } ); # the following is the same as ->author('MICKEY') my $author = $mcpan->author( { pauseid => 'MICKEY' } ); # find all people named Dave, not covering Davids # will return a resultset my $daves = $mcpan->author( { name => 'Dave *' } ); OR If you want to do a more complicated query that has an OR condition, such as "this or that", you can use the following syntax with the either key: # any author named "Dave" or "David" my $daves = $mcpan->author( { either => [ { name => 'Dave *' }, { name => 'David *' }, ] } ); AND If you want to do a more complicated query that has an AND condition, such as "this and that", you can use the following syntax with the all key: # any users named 'John' with a Gmail account my $johns = $mcpan->author( { all => [ { name => 'John *' }, { email => '*gmail.com' }, ] } ); Or, to get either the given version of a release, or the latest: my $releases = $mcpan->release( { all => [ { distribution => 'GraphViz2' }, ($version ? { version => $version } : { status => 'latest' }), ], } ); 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 following syntax with the not key: # any author named "Dave" or "David" my $daves = $mcpan->author( { either => [ { name => 'Dave *' }, { name => 'David *' }, ], not => [ { email => '*gmail.com' }, ], } ); DESIGN This module has three purposes: * Provide 100% of the MetaCPAN API This module will be updated regularly on every MetaCPAN API change, and intends to provide the user with as much of the API as possible, no shortcuts. If it's documented in the API, you should be able to do it. Because of this design decision, this module has an official MetaCPAN namespace with the blessing of the MetaCPAN developers. Notice this module currently only provides the beta API, not the old soon-to-be-deprecated API. * Be lightweight, to allow flexible usage While many modules would help make writing easier, it's important to take into account how they affect your compile-time, run-time, overall memory consumption, and CPU usage. By providing a slim interface implementation, more users are able to use this module, such as long-running processes (like daemons), CLI or GUI applications, cron jobs, and more. * DWIM While it's possible to access the methods defined by the API spec, there's still a matter of what you're really trying to achieve. For example, when searching for "Dave", you want to find both Dave Cross and Dave Rolsky (and any other Dave), but you also want to search for a PAUSE ID of DAVE, if one exists. This is where DWIM comes in. This module provides you with additional generic methods which will try to do what they think you want. Of course, this does not prevent you from manually using the API methods. You still have full control over that, if that's what you wish. You can (and should) read up on the general methods, which will explain how their DWIMish nature works, and what searches they run. AUTHORS * Sawyer X * Mickey Nasriachi COPYRIGHT AND LICENSE This software is copyright (c) 2016 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. LICENSE100644001750001750 4641314520270707 15552 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000This software is copyright (c) 2016 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. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2016 by Sawyer X. This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 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. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Perl Artistic License 1.0 --- This software is Copyright (c) 2016 by Sawyer X. This is free software, licensed under: The Perl Artistic License 1.0 The "Artistic License" Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder as specified below. "Copyright Holder" is whoever is named in the copyright or copyrights for the package. "You" is you, if you're thinking about copying or distributing this Package. "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as uunet.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) give non-standard executables non-standard names, and clearly document the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. You may embed this Package's interpreter within an executable of yours (by linking); this shall be construed as a mere form of aggregation, provided that the complete Standard Version of the interpreter is so embedded. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whoever generated them, and may be sold commercially, and may be aggregated with this Package. If such scripts or library files are aggregated with this Package via the so-called "undump" or "unexec" methods of producing a binary executable image, then distribution of such an image shall neither be construed as a distribution of this Package nor shall it fall under the restrictions of Paragraphs 3 and 4, provided that you do not represent such an executable image as a Standard Version of this Package. 7. C subroutines (or comparably compiled subroutines in other languages) supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language. 8. Aggregation of this Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package. 9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End Changes100644001750001750 3703614520270707 16041 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000Revision history for MetaCPAN-Client (previously MetaCPAN-API) 2.031000 31.10.23 * Fix reverse-dependencies distributions check (Mickey) * 'use Data::Printer' instead of shortened 'use DDP' (Mickey) 2.030000 22.08.22 * Set verify_SSL=>1 for default HTTP::Tiny user agent (Stig Palmquist, GH#113) * Updated docs (Dave Rolsky, GH#111) 2.029000 20.12.20 * Added checksum_sha256 & checksum_md5 fields support (stigo, GH#110) * Cleanup old files (Mickey) 2.028000 24.8.20 * Support specific versions in download_url (Nicolas R (atoomic), GH#107) 2.027000 11.8.20 * Run Travis tests with more Perls (Olaf Alders, GH#102) 2.026000 14.3.19 * Added (back, syntax modified for ES2.x) example script top20_favorites (Mickey) * Updated SYNOPSIS for Favorite (Mickey, Olaf Alders) * Fixed link to Search Spec (Renee Baecker, GH#101) * Fixed typo in error message (Johann Rolschewski, GH#100) 2.025000 22.4.18 * Added support for the new 'cover' index - cpancover.org info (Mickey) 2.024000 20.4.18 * Fix warning on a JSON::PP::Boolean check (Mickey) 2.023000 26.1.18 * Support the new 'deprecated' field in File and Release types (Mickey) 2.022000 3.1.18 * Allow user-defined target classes in ResultSet (Kent Fredric, Sawyer) * Added test for reverse dependencies (Sawyer) * Switched ref() checks to Ref::Util::is_ref (Mickey) 2.021000 18.11.17 * Scroller fix for page skipping (Thomas Sibley) * Sorting in scrolled searches (Thomas Sibley) * Type check cleanup (Thomas Sibley) 2.020000 17.11.17 * Added support for /search/autocomplete/suggest (Mickey) 2.019000 16.11.17 * Added 'package' type support for scrolled searches (Mickey) 2.018000 16.10.17 * Fix fetch URL (Mickey, GH#92) * Removed critic author test (Mickey) 2.017000 25.6.17 * reverse_dependencies: update link to new API endpoint (Mickey, GH#89) 2.016000 7.6.17 * Support CSV field list in 'all' requests (Mickey, GH#87) 2.015000 14.5.17 * Added 'main_module' field to the Release object (Mickey) * Updated doc (Matthew Horsfall, GH#85) 2.014000 12.5.17 * Fixed single-value case for expected arrayref (Mickey, GH#84) * Added support for new release/contributors endpoint (Mickey) 2.013001 12.5.17 * Updated endpoint name following API change (Mickey) 2.013000 9.5.17 * Added support for new 'packages' type (Mickey) 2.012000 27.4.17 * Fixed 'email' field handling in Author objects (Mickey, GH#83) 2.011000 18.4.17 * Added support for scroller time/size params (Mickey) * Removed warning of scroller deletion failure (Mickey, GH#81) 2.010000 3.4.17 * Added support for new 'permission' type (Mickey) 2.009001 29.3.17 * Use Test::Needs to force a minimum WWW::Mechanize::Cached version (Olaf Alders, GH#76) 2.009000 24.3.17 * Bump WWW::Mechanize::Cached version to 1.50 (Olaf Alders, GH#76) * Require LWP::Protocol::https in tests (Mickey, GH#79) * Added 'changes' method for Release objects (Mickey, GH#57) * Cleaner URLs - removed redundant slashes and 'v1' (Mickey) * Created a role for user-agent handling for reuse (Mickey) 2.008001 23.3.17 * Fixed a test (Mickey) 2.008000 22.3.17 * Added metacpan_url method to the entity objects (Mickey, #GH69) 2.007000 8.3.17 * Update tests for newer Perl versions, to run without '.' in @INC (Sawyer X, GH#72) 2.006000 24.2.17 * Support '_source' filtering (Mickey, GH#70) * Support debug-mode for detailed error messages (Mickey) 2.005000 13.2.17 * Added the ascii_name and perlmongers fields to the Author object (Dave Rolsky, GH #66) * Fixed Author->dir to actually return something (Dave Rolsky, GH #66) 2.004000 30.12.16 * Speed up own scroller (Mickey) * Fixed rev_deps (Mickey) 2.004000-TRIAL 24.12.16 * Removed dependency: Search::Elasticsearch in favor of an internal scroller (Mickey) * Added Types class for 'isa' checks (Mickey) 2.003000 19.12.16 * Escaped query to autocomplete (Mickey) * Removed dependency: Try::Tiny (Mickey) 2.002000 14.12.16 * Support 'autocomplete' endpoint (Mickey) 2.001000 08.12.16 * Distribution: added 'rt' & 'github' methods (Mickey) * Use Ref::Util for ref checks (Mickey) 2.000000 18.11.16 * Major version: v1 full support - removed support and default settings for v0 - corrected domain, base_url setting, using clientinfo - code/tests updates and cleanup (Mickey, Brad Lhotsky) * Pinned Search::Elasticsearch version to 2.03 (Mickey) * Use @Starter in dist.ini + cpanfile cleanup (Grinnz) 1.028003 23.10.16 * Removed AutoPrereqs from dist.ini (Mickey) 1.028002 23.10.16 * GH #53 a few small dist.ini tweaks (Karen Etheridge) * Even more dist.ini tweaks (Mickey, thanks to Grinnz) 1.028001 22.10.16 * GH #51 Adds eumm_version to dist.ini (Olaf Alders) * GH #52 Stop excluding cpanfile from being copied to build (Olaf Alders) 1.028000 21.10.16 * GH #50 Remove hard-deps for HTTP::Tiny::Mech and WWW::Mechanize::Cached (Paul Howarth) * dist.ini: don't automatically update cpanfile (Mickey) 1.027000 20.10.16 * GH #49 Convert values of JSON::PP::Boolean objects in output so they are not skipped when expeting scalars (Mickey) 1.026001 19.10.16 * Fixed version range for Search::Elasticsearch (Mickey) 1.026000 19.10.16 * Moved distini prereqs to cpanfile (Mickey) * Limit Search::Elasticsearch version to 2.02 (Mickey) * Updated docs (Thomas Sibley) 1.025000 30.8.16 * Added some version requirements to improve SSL over HTTP::Tiny (Mickey) * Added default values for distribution keys with no content (Mickey, per Tux request) 1.024000 28.08.16 * Try to fetch clientinfo from https://clientinfo.metacpan.org to get default production version (Mickey) 1.023000 27.08.16 * Added support for version by env METACPAN_VERSION (Mickey) * Added tests for version argument (Mickey) 1.022003 06.08.16 * Fixed a warning in $file->pod (Mickey) 1.022002 06.08.16 * Added LWP::Protocol::https as test dependency (Mickey) 1.022001 05.08.16 * check user provided UA for 'get' and 'post' methods (Mickey) * document updates (Mickey) 1.022000 04.08.16 * Rework type checking - enforce expected types, inc. single-valued array-ref unwrapping; doesn't break types that are expected to be array-refs (Mickey) 1.021000 27.07.16 * Fix result values in v1 - single valued arrayref in ES result will be turned to a scalar (Mickey) 1.020000 12.07.16 * Added support for Author->release_count & Author->links methods (Mickey) * Added support for url_prefix parameter for Pod (Mickey) 1.019000 06.07.16 * Added missing 'download_url' attribute to file/module result objects (Mickey) 1.018000 06.07.16 * Added support for download_url endpoint (Mickey) * Default domain set by providing 'version' - makes it easy to work with v1 (Mickey) 1.017000 28.06.16 * Fixed nodes list for Search::Elasticsearch (Mickey) * Added support for 'aggregations' (Mickey) 1.016000 27.06.16 * Added support for 'all' filters type 'files' (Mickey) * http -> https (Mickey) 1.015000 02.06.16 * Adding `source` method to MetaCPAN::Client::File (stevan) 1.014000 29.04.16 * Fix warning on missing fields param (Mickey, Sawyer X) * Switch to Search::Elasticsearch 2.0. (Sawyer X) * You can test MetaCPAN::API with a different domain using the environment variable "METACPAN_DOMAIN". (Mickey) 1.013000 25.04.15 * GH #34 Use Travis for CI (oalders) * GH #35 Improve Kwalitee + test improvements (oalders) 1.012000 09.04.15 * GH #33 added Mirror type and support for mirrors search in 'all' queries (mickeyn) * GH #33 support 'ratings' search in 'all' queries (mickeyn) * more example scripts: facets, top favorites, all authors blogs (mickeyn) * cleanup & doc updates (Gabor Szabo, mickeyn) 1.011000 27.01.15 * support 'favorites' type and 'facets' key param in 'all' queries (mickeyn) 1.010000 23.01.15 * support wildcard-only value in complex search (mickeyn) * support raw Elasticsearch filters in 'all' queries (mickeyn) 1.009000 11.01.15 * GH #25 (RT #99499): added support for 'fields' filtering (mickeyn, oalders) 1.008001 01.01.15 * Happy new year! * Correct Meta resources for the repo. * Correct link in POD for the Pod element. (Alex Vandiver) 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. * GH #2: Remove incorrect and unnecessary check for class names. * Provide "ua" attribute in the main object to override user agent. * Add some use-case examples (examples directory). * Add 'releases' method to Author (not official so no docs yet). * GH #4: Use example with hyphen. * Related to GH #4, use Data::Printer instead of shotened name "DDP". 1.002000 24.04.14 * Add 'not' support for complex queries * Add reverse_dependencies method 1.001001 15.04.14 * Fix the reading of scroller result when 'fields' param is passed. 1.001000 09.04.14 * Add support for nested either/all queries * Add tests for complex queries (two levels deep) * Correct documentation on complex queries * Update tests to work on older versions of perl 1.000001 03.04.14 * changed Elasticsearch (deprecated) to Search::Elasticsearch (official) 1.000000 02.04.14 ** Completely rewritten ** MetaCPAN::API has been completely rewritten as MetaCPAN::Client. Other than the different name (to match MetaCPAN itself), the following changes had been made: * MetaCPAN::Client is officially part of MetaCPAN * Semantic Versioning (semver) scheme * Moo as object system * All entities are now objects * Using Elasticsearch.pm for complex queries * Rich syntax for nested queries (AND/OR) * Simple queries return entity objects * Complex queries return resultset objects (with iterator) * Support for scrolled searches * Inline support for Elasticsearch facets * Documentation, tests - all cleaned, rewritten 0.43 05.04.12 * Add example in POD of advanced usage with cache by Kent Fredric. (Gist: https://gist.github.com/1291928) * Sort keys in param join - more predictable result. 0.42 08.01.12 * Corrected documentation in MetaCPAN::API::Source. * Updated Dist::Zilla configuration, added more tests. 0.41 07.01.12 * Use Test::TinyMocker 0.02 syntax in tests to avoid test fails. 0.40 06.01.12 * Fixed JSON encoding so ElasticSearch won't fail. (Christian Walde) * Documentation fixes (Logan - logie17). 0.34 02.10.11 * Added MetaCPAN::API::Source (Renee Baecker). * Fix of HTTP::Tiny content-type in options (Renee Baecker). * Typo fix (Olaf Alders, reported by @doherty). 0.33 24.08.11 * No functional changes. * Skip t::lib in dzil. 0.32 04.08.11 * Use a default agent string for requests. * Use a default agent string for tests. 0.31 02.08.11 * URL updates (thanks to Olaf Alders, OALDERS). * Small mismatched quote in POD example. 0.30 30.07.11 * Add POST query searches (RT #69814, GH #2). (original code by Tim Bunce, thank you!) * More tests. 0.20 28.07.11 * Add complex (manual) searches to author()/release() + docs. * Add file() as a synonym to module(). * Respect content-type. * Allow setting additional params to fetch(). * Allow "pauseid" in author via key. * Better check for content-type. 0.11 24.07.11 * Correct the POD example and tests. * Update to use a different API path. 0.10 24.07.11 * Almost complete rewrite. * Make use of the new beta API. * Remove old API support. * Remove DWIM methods for now. * Include lots of tests. 0.02 13.02.11 (First stable release!) * Add docs (Sawyer X). 0.01_03 10.02.11 * Immutable base_url (Olaf Alders, Sawyer X). 0.01_02 10.02.11 * Make base_url 'rw' (Olaf Alders). * Update module search URL (Olaf Alders). * Refactoring using _http_req method (Sawyer X). * Remove render_result method (Sawyer X). * Remove unnecessary print (Sawyer X). 0.01_01 05.02.11 * Module, Dist, POD, Author and CPANRatings are supported 100%. * Still needs more refactoring, and some methods aren't used yet. META.yml100644001750001750 3022114520270707 16004 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000--- abstract: 'A comprehensive, DWIM-featured client to the MetaCPAN API' author: - 'Sawyer X ' - 'Mickey Nasriachi ' build_requires: ExtUtils::MakeMaker: '0' File::Spec: '0' LWP::Protocol::https: '0' Test::Fatal: '0' Test::More: '0' Test::Needs: '0.002005' base: '0' blib: '1.01' configure_requires: ExtUtils::MakeMaker: '7.1101' dynamic_config: 0 generated_by: 'Dist::Zilla version 6.030, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: MetaCPAN-Client no_index: directory: - eg - examples - inc - share - t - xt provides: MetaCPAN::Client: file: lib/MetaCPAN/Client.pm version: '2.031000' MetaCPAN::Client::Author: file: lib/MetaCPAN/Client/Author.pm version: '2.031000' MetaCPAN::Client::Cover: file: lib/MetaCPAN/Client/Cover.pm version: '2.031000' MetaCPAN::Client::Distribution: file: lib/MetaCPAN/Client/Distribution.pm version: '2.031000' MetaCPAN::Client::DownloadURL: file: lib/MetaCPAN/Client/DownloadURL.pm version: '2.031000' MetaCPAN::Client::Favorite: file: lib/MetaCPAN/Client/Favorite.pm version: '2.031000' MetaCPAN::Client::File: file: lib/MetaCPAN/Client/File.pm version: '2.031000' MetaCPAN::Client::Mirror: file: lib/MetaCPAN/Client/Mirror.pm version: '2.031000' MetaCPAN::Client::Module: file: lib/MetaCPAN/Client/Module.pm version: '2.031000' MetaCPAN::Client::Package: file: lib/MetaCPAN/Client/Package.pm version: '2.031000' MetaCPAN::Client::Permission: file: lib/MetaCPAN/Client/Permission.pm version: '2.031000' MetaCPAN::Client::Pod: file: lib/MetaCPAN/Client/Pod.pm version: '2.031000' MetaCPAN::Client::Rating: file: lib/MetaCPAN/Client/Rating.pm version: '2.031000' MetaCPAN::Client::Release: file: lib/MetaCPAN/Client/Release.pm version: '2.031000' MetaCPAN::Client::Request: file: lib/MetaCPAN/Client/Request.pm version: '2.031000' MetaCPAN::Client::ResultSet: file: lib/MetaCPAN/Client/ResultSet.pm version: '2.031000' MetaCPAN::Client::Role::Entity: file: lib/MetaCPAN/Client/Role/Entity.pm version: '2.031000' MetaCPAN::Client::Role::HasUA: file: lib/MetaCPAN/Client/Role/HasUA.pm version: '2.031000' MetaCPAN::Client::Scroll: file: lib/MetaCPAN/Client/Scroll.pm version: '2.031000' MetaCPAN::Client::Types: file: lib/MetaCPAN/Client/Types.pm version: '2.031000' requires: Carp: '0' HTTP::Tiny: '0.056' IO::Socket::SSL: '1.42' JSON::MaybeXS: '0' JSON::PP: '0' Moo: '0' Moo::Role: '0' Net::SSLeay: '1.49' Ref::Util: '0' Safe::Isa: '0' Type::Tiny: '0' URI::Escape: '0' perl: '5.010' strict: '0' warnings: '0' resources: IRC: irc://irc.perl.org/#metacpan WebIRC: https://chat.mibbit.com/#metacpan@irc.perl.org bugtracker: https://github.com/metacpan/metacpan-client/issues repository: https://github.com/metacpan/metacpan-client.git version: '2.031000' x_Dist_Zilla: perl: version: '5.034000' plugins: - class: Dist::Zilla::Plugin::MetaYAML name: '@Starter/MetaYAML' version: '6.030' - class: Dist::Zilla::Plugin::MetaJSON name: '@Starter/MetaJSON' version: '6.030' - class: Dist::Zilla::Plugin::License name: '@Starter/License' version: '6.030' - class: Dist::Zilla::Plugin::ReadmeAnyFromPod config: Dist::Zilla::Role::FileWatcher: version: '0.006' name: '@Starter/ReadmeAnyFromPod' version: '0.163250' - class: Dist::Zilla::Plugin::PodSyntaxTests name: '@Starter/PodSyntaxTests' version: '6.030' - class: Dist::Zilla::Plugin::Test::ReportPrereqs name: '@Starter/Test::ReportPrereqs' version: '0.029' - class: Dist::Zilla::Plugin::Test::Compile config: Dist::Zilla::Plugin::Test::Compile: bail_out_on_fail: '0' fail_on_warning: author fake_home: 0 filename: xt/author/00-compile.t module_finder: - ':InstallModules' needs_display: 0 phase: develop script_finder: - ':PerlExecFiles' skips: [] switch: [] name: '@Starter/Test::Compile' version: '2.058' - class: Dist::Zilla::Plugin::MakeMaker config: Dist::Zilla::Role::TestRunner: default_jobs: 1 name: '@Starter/MakeMaker' version: '6.030' - class: Dist::Zilla::Plugin::Manifest name: '@Starter/Manifest' version: '6.030' - class: Dist::Zilla::Plugin::PruneCruft name: '@Starter/PruneCruft' version: '6.030' - class: Dist::Zilla::Plugin::ManifestSkip name: '@Starter/ManifestSkip' version: '6.030' - class: Dist::Zilla::Plugin::RunExtraTests config: Dist::Zilla::Role::TestRunner: default_jobs: 1 name: '@Starter/RunExtraTests' version: '0.029' - class: Dist::Zilla::Plugin::TestRelease name: '@Starter/TestRelease' version: '6.030' - class: Dist::Zilla::Plugin::ConfirmRelease name: '@Starter/ConfirmRelease' version: '6.030' - class: Dist::Zilla::Plugin::UploadToCPAN name: '@Starter/UploadToCPAN' version: '6.030' - class: Dist::Zilla::Plugin::MetaConfig name: '@Starter/MetaConfig' version: '6.030' - class: Dist::Zilla::Plugin::MetaNoIndex name: '@Starter/MetaNoIndex' version: '6.030' - class: Dist::Zilla::Plugin::MetaProvides::Package config: Dist::Zilla::Plugin::MetaProvides::Package: finder_objects: - class: Dist::Zilla::Plugin::FinderCode name: '@Starter/MetaProvides::Package/AUTOVIV/:InstallModulesPM' version: '6.030' include_underscores: 0 Dist::Zilla::Role::MetaProvider::Provider: $Dist::Zilla::Role::MetaProvider::Provider::VERSION: '2.002004' inherit_missing: '1' inherit_version: '1' meta_noindex: '1' Dist::Zilla::Role::ModuleMetadata: Module::Metadata: '1.000037' version: '0.006' name: '@Starter/MetaProvides::Package' version: '2.004003' - class: Dist::Zilla::Plugin::ShareDir name: '@Starter/ShareDir' version: '6.030' - class: Dist::Zilla::Plugin::ExecDir name: '@Starter/ExecDir' version: '6.030' - class: Dist::Zilla::Plugin::PodCoverageTests name: PodCoverageTests version: '6.030' - class: Dist::Zilla::Plugin::Git::GatherDir config: Dist::Zilla::Plugin::GatherDir: exclude_filename: [] exclude_match: [] follow_symlinks: 0 include_dotfiles: 0 prefix: '' prune_directory: [] root: . Dist::Zilla::Plugin::Git::GatherDir: include_untracked: 0 name: Git::GatherDir version: '2.048' - class: Dist::Zilla::Plugin::Prereqs::FromCPANfile name: Prereqs::FromCPANfile version: '0.08' - class: Dist::Zilla::Plugin::PodWeaver config: Dist::Zilla::Plugin::PodWeaver: finder: - ':InstallModules' - ':PerlExecFiles' plugins: - class: Pod::Weaver::Plugin::EnsurePod5 name: '@CorePrep/EnsurePod5' version: '4.019' - class: Pod::Weaver::Plugin::H1Nester name: '@CorePrep/H1Nester' version: '4.019' - class: Pod::Weaver::Plugin::SingleEncoding name: '@Default/SingleEncoding' version: '4.019' - class: Pod::Weaver::Section::Name name: '@Default/Name' version: '4.019' - class: Pod::Weaver::Section::Version name: '@Default/Version' version: '4.019' - class: Pod::Weaver::Section::Region name: '@Default/prelude' version: '4.019' - class: Pod::Weaver::Section::Generic name: SYNOPSIS version: '4.019' - class: Pod::Weaver::Section::Generic name: DESCRIPTION version: '4.019' - class: Pod::Weaver::Section::Generic name: OVERVIEW version: '4.019' - class: Pod::Weaver::Section::Collect name: ATTRIBUTES version: '4.019' - class: Pod::Weaver::Section::Collect name: METHODS version: '4.019' - class: Pod::Weaver::Section::Collect name: FUNCTIONS version: '4.019' - class: Pod::Weaver::Section::Leftovers name: '@Default/Leftovers' version: '4.019' - class: Pod::Weaver::Section::Region name: '@Default/postlude' version: '4.019' - class: Pod::Weaver::Section::Authors name: '@Default/Authors' version: '4.019' - class: Pod::Weaver::Section::Legal name: '@Default/Legal' version: '4.019' name: PodWeaver version: '4.010' - class: Dist::Zilla::Plugin::MinimumPerlFast name: MinimumPerlFast version: '0.005' - class: Dist::Zilla::Plugin::ReadmeAnyFromPod config: Dist::Zilla::Role::FileWatcher: version: '0.006' name: pod.root version: '0.163250' - class: Dist::Zilla::Plugin::CheckChangeLog name: CheckChangeLog version: '0.05' - class: Dist::Zilla::Plugin::PkgVersion name: PkgVersion version: '6.030' - class: Dist::Zilla::Plugin::MetaResources name: MetaResources version: '6.030' - class: Dist::Zilla::Plugin::Git::Tag config: Dist::Zilla::Plugin::Git::Tag: branch: ~ changelog: Changes signed: 0 tag: v2.031000 tag_format: v%V tag_message: v%V Dist::Zilla::Role::Git::Repo: git_version: 2.34.1 repo_root: . Dist::Zilla::Role::Git::StringFormatter: time_zone: local name: Git::Tag version: '2.048' - class: Dist::Zilla::Plugin::Git::Push config: Dist::Zilla::Plugin::Git::Push: push_to: - origin remotes_must_exist: 1 Dist::Zilla::Role::Git::Repo: git_version: 2.34.1 repo_root: . name: Git::Push version: '2.048' - class: Dist::Zilla::Plugin::FinderCode name: ':InstallModules' version: '6.030' - class: Dist::Zilla::Plugin::FinderCode name: ':IncModules' version: '6.030' - class: Dist::Zilla::Plugin::FinderCode name: ':TestFiles' version: '6.030' - class: Dist::Zilla::Plugin::FinderCode name: ':ExtraTestFiles' version: '6.030' - class: Dist::Zilla::Plugin::FinderCode name: ':ExecFiles' version: '6.030' - class: Dist::Zilla::Plugin::FinderCode name: ':PerlExecFiles' version: '6.030' - class: Dist::Zilla::Plugin::FinderCode name: ':ShareFiles' version: '6.030' - class: Dist::Zilla::Plugin::FinderCode name: ':MainModule' version: '6.030' - class: Dist::Zilla::Plugin::FinderCode name: ':AllFiles' version: '6.030' - class: Dist::Zilla::Plugin::FinderCode name: ':NoFiles' version: '6.030' - class: Dist::Zilla::Plugin::FinderCode name: '@Starter/MetaProvides::Package/AUTOVIV/:InstallModulesPM' version: '6.030' zilla: class: Dist::Zilla::Dist::Builder config: is_trial: '0' version: '6.030' x_generated_by_perl: v5.34.0 x_serialization_backend: 'YAML::Tiny version 1.73' x_spdx_expression: 'Artistic-1.0-Perl OR GPL-1.0-or-later' MANIFEST100644001750001750 377114520270707 15656 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.030. Changes LICENSE MANIFEST MANIFEST.SKIP META.json META.yml Makefile.PL README cpanfile dist.ini examples/agg.pl examples/author-country.pl examples/author.pl examples/author_releases.pl examples/authors_blogs.pl examples/autocomplete.pl examples/autocomplete_suggest.pl examples/changes.pl examples/complex-either-and.pl examples/complex-either-not.pl examples/complex-nested-either-and.pl examples/complex.pl examples/contributors.pl examples/cover.pl examples/distribution.pl examples/download_url.pl examples/es_filter.pl examples/fields-filter.pl examples/metacpan_url.pl examples/mirror.pl examples/module.pl examples/package.pl examples/permission.pl examples/pod.pl examples/rating.pl examples/recent.pl examples/recent_today.pl examples/release.pl examples/rev_deps-recursive.pl examples/rev_deps.pl examples/top20_favorites.pl examples/totals.pl lib/MetaCPAN/Client.pm lib/MetaCPAN/Client/Author.pm lib/MetaCPAN/Client/Cover.pm lib/MetaCPAN/Client/Distribution.pm lib/MetaCPAN/Client/DownloadURL.pm lib/MetaCPAN/Client/Favorite.pm lib/MetaCPAN/Client/File.pm lib/MetaCPAN/Client/Mirror.pm lib/MetaCPAN/Client/Module.pm lib/MetaCPAN/Client/Package.pm lib/MetaCPAN/Client/Permission.pm lib/MetaCPAN/Client/Pod.pm lib/MetaCPAN/Client/Rating.pm lib/MetaCPAN/Client/Release.pm lib/MetaCPAN/Client/Request.pm lib/MetaCPAN/Client/ResultSet.pm lib/MetaCPAN/Client/Role/Entity.pm lib/MetaCPAN/Client/Role/HasUA.pm lib/MetaCPAN/Client/Scroll.pm lib/MetaCPAN/Client/Types.pm t/00-report-prereqs.dd t/00-report-prereqs.t t/api/_get.t t/api/_get_or_search.t t/api/_search.t t/api/author.t t/api/cover.t t/api/distribution.t t/api/download_url.t t/api/favorite.t t/api/file.t t/api/module.t t/api/package.t t/api/permission.t t/api/pod.t t/api/rating.t t/api/release.t t/api/reverse-dependencies.t t/entity.t t/lib/Functions.pm t/request.t t/result_custom.t t/resultset.t t/scroll.t t/ua_trap.t xt/author/00-compile.t xt/author/pod-coverage.t xt/author/pod-syntax.t cpanfile100644001750001750 215714520270707 16226 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000use strict; use warnings; requires "Carp" => "0"; requires "HTTP::Tiny" => "0.056"; requires "IO::Socket::SSL" => "1.42"; requires "JSON::MaybeXS" => "0"; requires "JSON::PP" => "0"; requires "Moo" => "0"; requires "Moo::Role" => "0"; requires "Net::SSLeay" => "1.49"; requires "Ref::Util" => "0"; requires "Safe::Isa" => "0"; requires "Type::Tiny" => "0"; requires "URI::Escape"; requires "perl" => "5.010"; requires "strict" => "0"; requires "warnings" => "0"; on 'test' => sub { requires "Test::Fatal" => "0"; requires "Test::More" => "0"; requires "Test::Needs" => "0.002005"; requires "base" => "0"; requires "blib" => "1.01"; requires "LWP::Protocol::https" => "0"; recommends "HTTP::Tiny::Mech" => "1.001002"; recommends "WWW::Mechanize::Cached" => "1.54"; }; on 'develop' => sub { requires "HTTP::Tiny::Mech" => "1.001002"; requires "LWP::Protocol::https" => "0"; requires "WWW::Mechanize::Cached" => "1.54"; }; dist.ini100644001750001750 151314520270707 16161 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000name = MetaCPAN-Client author = Sawyer X author = Mickey Nasriachi license = Perl_5 copyright_holder = Sawyer X copyright_year = 2016 version = 2.031000 [@Starter] -remove = GatherDir MakeMaker.eumm_version = 7.1101 [PodCoverageTests] [Git::GatherDir] [Prereqs::FromCPANfile] [PodWeaver] [MinimumPerlFast] [ReadmeAnyFromPod / pod.root] filename = README.pod type = pod location = root [CheckChangeLog] [PkgVersion] [MetaResources] bugtracker.web = https://github.com/metacpan/metacpan-client/issues repository.url = https://github.com/metacpan/metacpan-client.git repository.web = https://github.com/metacpan/metacpan-client repository.type = git x_IRC = irc://irc.perl.org/#metacpan x_WebIRC = https://chat.mibbit.com/#metacpan@irc.perl.org [Git::Tag] [Git::Push] META.json100644001750001750 4626314520270707 16171 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000{ "abstract" : "A comprehensive, DWIM-featured client to the MetaCPAN API", "author" : [ "Sawyer X ", "Mickey Nasriachi " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.030, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "MetaCPAN-Client", "no_index" : { "directory" : [ "eg", "examples", "inc", "share", "t", "xt" ] }, "prereqs" : { "configure" : { "requires" : { "ExtUtils::MakeMaker" : "7.1101" } }, "develop" : { "requires" : { "File::Spec" : "0", "HTTP::Tiny::Mech" : "1.001002", "IO::Handle" : "0", "IPC::Open3" : "0", "LWP::Protocol::https" : "0", "Pod::Coverage::TrustPod" : "0", "Test::More" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08", "WWW::Mechanize::Cached" : "1.54" } }, "runtime" : { "requires" : { "Carp" : "0", "HTTP::Tiny" : "0.056", "IO::Socket::SSL" : "1.42", "JSON::MaybeXS" : "0", "JSON::PP" : "0", "Moo" : "0", "Moo::Role" : "0", "Net::SSLeay" : "1.49", "Ref::Util" : "0", "Safe::Isa" : "0", "Type::Tiny" : "0", "URI::Escape" : "0", "perl" : "5.010", "strict" : "0", "warnings" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900", "HTTP::Tiny::Mech" : "1.001002", "WWW::Mechanize::Cached" : "1.54" }, "requires" : { "ExtUtils::MakeMaker" : "0", "File::Spec" : "0", "LWP::Protocol::https" : "0", "Test::Fatal" : "0", "Test::More" : "0", "Test::Needs" : "0.002005", "base" : "0", "blib" : "1.01" } } }, "provides" : { "MetaCPAN::Client" : { "file" : "lib/MetaCPAN/Client.pm", "version" : "2.031000" }, "MetaCPAN::Client::Author" : { "file" : "lib/MetaCPAN/Client/Author.pm", "version" : "2.031000" }, "MetaCPAN::Client::Cover" : { "file" : "lib/MetaCPAN/Client/Cover.pm", "version" : "2.031000" }, "MetaCPAN::Client::Distribution" : { "file" : "lib/MetaCPAN/Client/Distribution.pm", "version" : "2.031000" }, "MetaCPAN::Client::DownloadURL" : { "file" : "lib/MetaCPAN/Client/DownloadURL.pm", "version" : "2.031000" }, "MetaCPAN::Client::Favorite" : { "file" : "lib/MetaCPAN/Client/Favorite.pm", "version" : "2.031000" }, "MetaCPAN::Client::File" : { "file" : "lib/MetaCPAN/Client/File.pm", "version" : "2.031000" }, "MetaCPAN::Client::Mirror" : { "file" : "lib/MetaCPAN/Client/Mirror.pm", "version" : "2.031000" }, "MetaCPAN::Client::Module" : { "file" : "lib/MetaCPAN/Client/Module.pm", "version" : "2.031000" }, "MetaCPAN::Client::Package" : { "file" : "lib/MetaCPAN/Client/Package.pm", "version" : "2.031000" }, "MetaCPAN::Client::Permission" : { "file" : "lib/MetaCPAN/Client/Permission.pm", "version" : "2.031000" }, "MetaCPAN::Client::Pod" : { "file" : "lib/MetaCPAN/Client/Pod.pm", "version" : "2.031000" }, "MetaCPAN::Client::Rating" : { "file" : "lib/MetaCPAN/Client/Rating.pm", "version" : "2.031000" }, "MetaCPAN::Client::Release" : { "file" : "lib/MetaCPAN/Client/Release.pm", "version" : "2.031000" }, "MetaCPAN::Client::Request" : { "file" : "lib/MetaCPAN/Client/Request.pm", "version" : "2.031000" }, "MetaCPAN::Client::ResultSet" : { "file" : "lib/MetaCPAN/Client/ResultSet.pm", "version" : "2.031000" }, "MetaCPAN::Client::Role::Entity" : { "file" : "lib/MetaCPAN/Client/Role/Entity.pm", "version" : "2.031000" }, "MetaCPAN::Client::Role::HasUA" : { "file" : "lib/MetaCPAN/Client/Role/HasUA.pm", "version" : "2.031000" }, "MetaCPAN::Client::Scroll" : { "file" : "lib/MetaCPAN/Client/Scroll.pm", "version" : "2.031000" }, "MetaCPAN::Client::Types" : { "file" : "lib/MetaCPAN/Client/Types.pm", "version" : "2.031000" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/metacpan/metacpan-client/issues" }, "repository" : { "type" : "git", "url" : "https://github.com/metacpan/metacpan-client.git", "web" : "https://github.com/metacpan/metacpan-client" }, "x_IRC" : "irc://irc.perl.org/#metacpan", "x_WebIRC" : "https://chat.mibbit.com/#metacpan@irc.perl.org" }, "version" : "2.031000", "x_Dist_Zilla" : { "perl" : { "version" : "5.034000" }, "plugins" : [ { "class" : "Dist::Zilla::Plugin::MetaYAML", "name" : "@Starter/MetaYAML", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::MetaJSON", "name" : "@Starter/MetaJSON", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::License", "name" : "@Starter/License", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod", "config" : { "Dist::Zilla::Role::FileWatcher" : { "version" : "0.006" } }, "name" : "@Starter/ReadmeAnyFromPod", "version" : "0.163250" }, { "class" : "Dist::Zilla::Plugin::PodSyntaxTests", "name" : "@Starter/PodSyntaxTests", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::Test::ReportPrereqs", "name" : "@Starter/Test::ReportPrereqs", "version" : "0.029" }, { "class" : "Dist::Zilla::Plugin::Test::Compile", "config" : { "Dist::Zilla::Plugin::Test::Compile" : { "bail_out_on_fail" : 0, "fail_on_warning" : "author", "fake_home" : 0, "filename" : "xt/author/00-compile.t", "module_finder" : [ ":InstallModules" ], "needs_display" : 0, "phase" : "develop", "script_finder" : [ ":PerlExecFiles" ], "skips" : [], "switch" : [] } }, "name" : "@Starter/Test::Compile", "version" : "2.058" }, { "class" : "Dist::Zilla::Plugin::MakeMaker", "config" : { "Dist::Zilla::Role::TestRunner" : { "default_jobs" : 1 } }, "name" : "@Starter/MakeMaker", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::Manifest", "name" : "@Starter/Manifest", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::PruneCruft", "name" : "@Starter/PruneCruft", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::ManifestSkip", "name" : "@Starter/ManifestSkip", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::RunExtraTests", "config" : { "Dist::Zilla::Role::TestRunner" : { "default_jobs" : 1 } }, "name" : "@Starter/RunExtraTests", "version" : "0.029" }, { "class" : "Dist::Zilla::Plugin::TestRelease", "name" : "@Starter/TestRelease", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::ConfirmRelease", "name" : "@Starter/ConfirmRelease", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::UploadToCPAN", "name" : "@Starter/UploadToCPAN", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::MetaConfig", "name" : "@Starter/MetaConfig", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::MetaNoIndex", "name" : "@Starter/MetaNoIndex", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::MetaProvides::Package", "config" : { "Dist::Zilla::Plugin::MetaProvides::Package" : { "finder_objects" : [ { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : "@Starter/MetaProvides::Package/AUTOVIV/:InstallModulesPM", "version" : "6.030" } ], "include_underscores" : 0 }, "Dist::Zilla::Role::MetaProvider::Provider" : { "$Dist::Zilla::Role::MetaProvider::Provider::VERSION" : "2.002004", "inherit_missing" : 1, "inherit_version" : 1, "meta_noindex" : 1 }, "Dist::Zilla::Role::ModuleMetadata" : { "Module::Metadata" : "1.000037", "version" : "0.006" } }, "name" : "@Starter/MetaProvides::Package", "version" : "2.004003" }, { "class" : "Dist::Zilla::Plugin::ShareDir", "name" : "@Starter/ShareDir", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::ExecDir", "name" : "@Starter/ExecDir", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::PodCoverageTests", "name" : "PodCoverageTests", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::Git::GatherDir", "config" : { "Dist::Zilla::Plugin::GatherDir" : { "exclude_filename" : [], "exclude_match" : [], "follow_symlinks" : 0, "include_dotfiles" : 0, "prefix" : "", "prune_directory" : [], "root" : "." }, "Dist::Zilla::Plugin::Git::GatherDir" : { "include_untracked" : 0 } }, "name" : "Git::GatherDir", "version" : "2.048" }, { "class" : "Dist::Zilla::Plugin::Prereqs::FromCPANfile", "name" : "Prereqs::FromCPANfile", "version" : "0.08" }, { "class" : "Dist::Zilla::Plugin::PodWeaver", "config" : { "Dist::Zilla::Plugin::PodWeaver" : { "finder" : [ ":InstallModules", ":PerlExecFiles" ], "plugins" : [ { "class" : "Pod::Weaver::Plugin::EnsurePod5", "name" : "@CorePrep/EnsurePod5", "version" : "4.019" }, { "class" : "Pod::Weaver::Plugin::H1Nester", "name" : "@CorePrep/H1Nester", "version" : "4.019" }, { "class" : "Pod::Weaver::Plugin::SingleEncoding", "name" : "@Default/SingleEncoding", "version" : "4.019" }, { "class" : "Pod::Weaver::Section::Name", "name" : "@Default/Name", "version" : "4.019" }, { "class" : "Pod::Weaver::Section::Version", "name" : "@Default/Version", "version" : "4.019" }, { "class" : "Pod::Weaver::Section::Region", "name" : "@Default/prelude", "version" : "4.019" }, { "class" : "Pod::Weaver::Section::Generic", "name" : "SYNOPSIS", "version" : "4.019" }, { "class" : "Pod::Weaver::Section::Generic", "name" : "DESCRIPTION", "version" : "4.019" }, { "class" : "Pod::Weaver::Section::Generic", "name" : "OVERVIEW", "version" : "4.019" }, { "class" : "Pod::Weaver::Section::Collect", "name" : "ATTRIBUTES", "version" : "4.019" }, { "class" : "Pod::Weaver::Section::Collect", "name" : "METHODS", "version" : "4.019" }, { "class" : "Pod::Weaver::Section::Collect", "name" : "FUNCTIONS", "version" : "4.019" }, { "class" : "Pod::Weaver::Section::Leftovers", "name" : "@Default/Leftovers", "version" : "4.019" }, { "class" : "Pod::Weaver::Section::Region", "name" : "@Default/postlude", "version" : "4.019" }, { "class" : "Pod::Weaver::Section::Authors", "name" : "@Default/Authors", "version" : "4.019" }, { "class" : "Pod::Weaver::Section::Legal", "name" : "@Default/Legal", "version" : "4.019" } ] } }, "name" : "PodWeaver", "version" : "4.010" }, { "class" : "Dist::Zilla::Plugin::MinimumPerlFast", "name" : "MinimumPerlFast", "version" : "0.005" }, { "class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod", "config" : { "Dist::Zilla::Role::FileWatcher" : { "version" : "0.006" } }, "name" : "pod.root", "version" : "0.163250" }, { "class" : "Dist::Zilla::Plugin::CheckChangeLog", "name" : "CheckChangeLog", "version" : "0.05" }, { "class" : "Dist::Zilla::Plugin::PkgVersion", "name" : "PkgVersion", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::MetaResources", "name" : "MetaResources", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::Git::Tag", "config" : { "Dist::Zilla::Plugin::Git::Tag" : { "branch" : null, "changelog" : "Changes", "signed" : 0, "tag" : "v2.031000", "tag_format" : "v%V", "tag_message" : "v%V" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.34.1", "repo_root" : "." }, "Dist::Zilla::Role::Git::StringFormatter" : { "time_zone" : "local" } }, "name" : "Git::Tag", "version" : "2.048" }, { "class" : "Dist::Zilla::Plugin::Git::Push", "config" : { "Dist::Zilla::Plugin::Git::Push" : { "push_to" : [ "origin" ], "remotes_must_exist" : 1 }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.34.1", "repo_root" : "." } }, "name" : "Git::Push", "version" : "2.048" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":InstallModules", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":IncModules", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":TestFiles", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ExtraTestFiles", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ExecFiles", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":PerlExecFiles", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ShareFiles", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":MainModule", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":AllFiles", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":NoFiles", "version" : "6.030" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : "@Starter/MetaProvides::Package/AUTOVIV/:InstallModulesPM", "version" : "6.030" } ], "zilla" : { "class" : "Dist::Zilla::Dist::Builder", "config" : { "is_trial" : 0 }, "version" : "6.030" } }, "x_generated_by_perl" : "v5.34.0", "x_serialization_backend" : "Cpanel::JSON::XS version 4.27", "x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later" } t000755001750001750 014520270707 14620 5ustar00mickeymickey000000000000MetaCPAN-Client-2.031000entity.t100644001750001750 240514520270707 16462 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t#!perl use strict; use warnings; use Test::More tests => 6; use Test::Fatal; { package MetaCPAN::Client::FakeEntityEmpty; use Moo; with 'MetaCPAN::Client::Role::Entity'; sub BUILDARGS { my ( $class, %args ) = @_; return \%args; } } { package MetaCPAN::Client::FakeEntityFull; use Moo; with 'MetaCPAN::Client::Role::Entity'; sub _known_fields { +{ scalar => ['this'], arrayref => [], hashref => [], } } } ok( exception { MetaCPAN::Client::FakeEntityEmpty->new }, 'data is missing, causing exception', ); is( exception { MetaCPAN::Client::FakeEntityEmpty->new( data => {} ) }, undef, 'data available, not causing exception', ); like( exception { MetaCPAN::Client::FakeEntityEmpty->new_from_request( {} ) }, qr/.*Can't locate.*_known_fields/, 'Subroutine _known_fields missing', ); is( exception { MetaCPAN::Client::FakeEntityFull->new( data => {} ) }, undef, 'data available, not causing exception', ); my $fe = MetaCPAN::Client::FakeEntityFull->new_from_request( { that => 'this', this => 'that' } ); isa_ok( $fe, 'MetaCPAN::Client::FakeEntityFull' ); is_deeply( $fe->{'data'}, { this => 'that' }, 'Correct data' ); scroll.t100644001750001750 207014520270707 16442 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t#!perl use strict; use warnings; use Test::More tests => 6; use Ref::Util qw< is_hashref >; use HTTP::Tiny; use MetaCPAN::Client::Scroll; use MetaCPAN::Client::Release; my $scroller = MetaCPAN::Client::Scroll->new( ua => HTTP::Tiny->new, base_url => 'https://fastapi.metacpan.org/v1/', type => 'release', body => { query => { term => { distribution => 'MetaCPAN-Client' } } }, size => 50, ); isa_ok( $scroller, 'MetaCPAN::Client::Scroll' ); can_ok( $scroller, qw< aggregations base_url body _buffer BUILDARGS DEMOLISH _fetch_next _id next size time total type ua > ); my $next = $scroller->next; ok( is_hashref($next), 'next doc returns a hashref' ); my $rel = MetaCPAN::Client::Release->new_from_request( $next->{'_source'} ); isa_ok( $rel, 'MetaCPAN::Client::Release' ); is( $rel->distribution, 'MetaCPAN-Client', 'release object can be created from next doc' ); my $got = 1; # we call ->next once above while ( my $n = $scroller->next ) { $got++ } is( $got, $scroller->total, 'can read all matching docs' ); 1; Makefile.PL100644001750001750 400714520270707 16470 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.030. use strict; use warnings; use 5.010; use ExtUtils::MakeMaker 7.1101; my %WriteMakefileArgs = ( "ABSTRACT" => "A comprehensive, DWIM-featured client to the MetaCPAN API", "AUTHOR" => "Sawyer X , Mickey Nasriachi ", "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => "7.1101" }, "DISTNAME" => "MetaCPAN-Client", "LICENSE" => "perl", "MIN_PERL_VERSION" => "5.010", "NAME" => "MetaCPAN::Client", "PREREQ_PM" => { "Carp" => 0, "HTTP::Tiny" => "0.056", "IO::Socket::SSL" => "1.42", "JSON::MaybeXS" => 0, "JSON::PP" => 0, "Moo" => 0, "Moo::Role" => 0, "Net::SSLeay" => "1.49", "Ref::Util" => 0, "Safe::Isa" => 0, "Type::Tiny" => 0, "URI::Escape" => 0, "strict" => 0, "warnings" => 0 }, "TEST_REQUIRES" => { "ExtUtils::MakeMaker" => 0, "File::Spec" => 0, "LWP::Protocol::https" => 0, "Test::Fatal" => 0, "Test::More" => 0, "Test::Needs" => "0.002005", "base" => 0, "blib" => "1.01" }, "VERSION" => "2.031000", "test" => { "TESTS" => "t/*.t t/api/*.t" } ); my %FallbackPrereqs = ( "Carp" => 0, "ExtUtils::MakeMaker" => 0, "File::Spec" => 0, "HTTP::Tiny" => "0.056", "IO::Socket::SSL" => "1.42", "JSON::MaybeXS" => 0, "JSON::PP" => 0, "LWP::Protocol::https" => 0, "Moo" => 0, "Moo::Role" => 0, "Net::SSLeay" => "1.49", "Ref::Util" => 0, "Safe::Isa" => 0, "Test::Fatal" => 0, "Test::More" => 0, "Test::Needs" => "0.002005", "Type::Tiny" => 0, "URI::Escape" => 0, "base" => 0, "blib" => "1.01", "strict" => 0, "warnings" => 0 ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); api000755001750001750 014520270707 15371 5ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/tpod.t100644001750001750 47514520270707 16466 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/api#!perl use strict; use warnings; use Test::More tests => 5; use lib '.'; 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 ); like( $pod->x_pod, qr/=head1/, 'got pod' ); request.t100644001750001750 145314520270707 16640 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t#!perl use strict; use warnings; use Test::More tests => 7; use MetaCPAN::Client; use MetaCPAN::Client::Request; my $req = MetaCPAN::Client::Request->new( domain => 'https://mydomain' ); isa_ok( $req, 'MetaCPAN::Client::Request' ); can_ok( $req, qw, ); is( $req->domain, 'https://mydomain', 'Correct domain' ); is( $req->base_url, 'https://mydomain', 'Correct base_url' ); isa_ok( $req->ua, 'HTTP::Tiny' ); my $ver = $MetaCPAN::Client::VERSION || 'xx'; is_deeply( $req->ua_args, [ agent => "MetaCPAN::Client/$ver", verify_SSL => 1 ], 'Correct UA args', ); my $client = MetaCPAN::Client->new( domain => 'foo' ); is ( $client->request->domain, 'foo', 'domain set in request' ); ua_trap.t100644001750001750 166114520270707 16604 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/tuse strict; use warnings; use Test::More; # ABSTRACT: Make sure passed value of UA gets used for things. use Test::Needs { 'WWW::Mechanize::Cached' => 1.54, 'HTTP::Tiny::Mech' => 1.001002, }; use Test::Fatal qw( exception ); { package TrapUA; our $VERSION = '0.01'; 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; _get.t100644001750001750 257014520270707 16640 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/api#!perl use strict; use warnings; use Test::More tests => 13; use Test::Fatal; use lib '.'; use t::lib::Functions; { no warnings qw; *MetaCPAN::Client::Author::new_from_request = sub { my ( $self, $res ) = @_; ::isa_ok( $self, 'MetaCPAN::Client::Author' ); ::is_deeply( $res, { hello => 'world' }, 'Correct response' ); return 'ok'; }; my $count = 0; *MetaCPAN::Client::fetch = sub { my ( $self, $path ) = @_; ::isa_ok( $self, 'MetaCPAN::Client' ); ::is( $path, 'author/myarg', 'Correct path' ); $count++ == 0 and return; return { hello => 'world' }; }; } my $mc = mcpan(); can_ok( $mc, '_get' ); like( exception { $mc->_get() }, qr/^_get takes type and search string as parameters/, 'Failed with no params', ); like( exception { $mc->_get('wah') }, qr/^_get takes type and search string as parameters/, 'Failed with one param', ); like( exception { $mc->_get('wah', 'wah', 'wah') }, qr/^_get takes type and search string as parameters/, 'Failed with more than two params', ); # call fetch and fail like( exception { $mc->_get( 'author', 'myarg' ) }, qr/^Failed to fetch Author \(myarg\)/, 'Correct failure', ); # call fetch and succeed my $res = $mc->_get( 'author', 'myarg' ); is( $res, 'ok', 'Correct result' ); file.t100644001750001750 130114520270707 16630 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/api#!perl use strict; use warnings; use Test::More tests => 11; use Test::Fatal; use lib '.'; use t::lib::Functions; my $mc = mcpan(); can_ok( $mc, 'file' ); my $file = $mc->file('DOY/Moose-2.0001/lib/Moose.pm'); isa_ok( $file, 'MetaCPAN::Client::File' ); can_ok( $file, qw ); is( $file->author, 'DOY', 'Correct author' ); is( $file->distribution, 'Moose', 'Correct distribution' ); is( $file->name, 'Moose.pm', 'Correct name' ); is( $file->path, 'lib/Moose.pm', 'Correct path' ); is( $file->release, 'Moose-2.0001', 'Correct release' ); is( $file->version, '2.0001', 'Correct version' ); like( $file->source, qr/^\s*package Moose\;/, 'Correct source' ); MANIFEST.SKIP100644001750001750 5114520270707 16347 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000cpanfile.snapshot local MetaCPAN-Client* cover.t100644001750001750 106414520270707 17035 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/api#!perl use strict; use warnings; use Test::More tests => 8; use Ref::Util qw< is_hashref is_ref >; use lib '.'; use t::lib::Functions; my $mc = mcpan(); can_ok( $mc, 'cover' ); my $cover = $mc->cover('Moose-2.2007'); isa_ok( $cover, 'MetaCPAN::Client::Cover' ); can_ok( $cover, qw< distribution release version criteria > ); ok( !is_ref($cover->distribution), "distribution is not a ref"); ok( !is_ref($cover->release), "release is not a ref"); ok( !is_ref($cover->version), "version is not a ref"); ok( is_hashref($cover->criteria), "criteria is a hashref"); resultset.t100644001750001750 131614520270707 17200 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t#!perl use strict; use warnings; use Test::More tests => 3; use Test::Fatal; use MetaCPAN::Client::ResultSet; { package MetaCPAN::Client::Test::ScrollerZ; use base 'MetaCPAN::Client::Scroll'; # < 5.10 FTW (except, no) sub total {0} } like( exception { MetaCPAN::Client::ResultSet->new( type => 'failZZ', scroller => bless {}, 'MetaCPAN::Client::Test::ScrollerZ', ) }, qr/Invalid type/, 'Invalid type fail', ); my $rs = MetaCPAN::Client::ResultSet->new( type => 'author', scroller => bless {}, 'MetaCPAN::Client::Scroll', ); isa_ok( $rs, 'MetaCPAN::Client::ResultSet' ); can_ok( $rs, qw ); author.t100644001750001750 450414520270707 17223 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/api#!perl use strict; use warnings; use Test::More; use Test::Fatal; use Ref::Util qw< is_arrayref >; use lib '.'; use t::lib::Functions; my $mc = mcpan(); can_ok( $mc, 'author' ); my $author = $mc->author('XSAWYERX'); isa_ok( $author, 'MetaCPAN::Client::Author' ); can_ok( $author, 'pauseid' ); is( $author->pauseid, 'XSAWYERX', 'Correct author' ); my $most_daves; { my $daves = $mc->author( { either => [ { name => 'Dave *' }, { name => 'David *' }, ] } ); isa_ok( $daves, 'MetaCPAN::Client::ResultSet' ); can_ok( $daves, 'total' ); ok( $daves->total > 200, 'Lots of Daves' ); $most_daves = $daves->total; } { my $daves = $mc->author( { either => [ { name => 'Dave *' }, { name => 'David *' }, ], not => [ { name => 'Dave S*' }, { name => 'David S*' }, ], } ); isa_ok( $daves, 'MetaCPAN::Client::ResultSet' ); can_ok( $daves, 'total' ); ok( $daves->total < $most_daves, 'Definitely less Daves' ); } { my $daves = $mc->author( { either => [ { all => [ { name => 'Dave *' }, { email => '*gmail.com' }, ], }, { all => [ { name => 'David *' }, { email => '*gmail.com' }, ], }, ] } ); isa_ok( $daves, 'MetaCPAN::Client::ResultSet' ); can_ok( $daves, 'total' ); ok( $daves->total <= $most_daves, 'Definitely not more Daves' ); while ( my $dave = $daves->next ) { my @emails = is_arrayref $dave->email ? @{ $dave->email } : $dave->email; ok( grep( +( $_ =~ /gmail\.com$/ ), @emails ), 'This Dave has a Gmail account', ); } } my $johns = $mc->author( { all => [ { name => 'John *' }, { email => '*gmail.com' }, ] } ); isa_ok( $johns, 'MetaCPAN::Client::ResultSet' ); can_ok( $johns, 'total' ); ok( $johns->total > 0, 'Got some Johns' ); while ( my $john = $johns->next ) { my @emails = is_arrayref $john->email ? @{ $john->email } : $john->email; ok( grep( +( $_ =~ /gmail\.com$/ ), @emails ), 'This John has a Gmail account', ); } done_testing; module.t100644001750001750 122714520270707 17205 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/api#!perl use strict; use warnings; use Test::More tests => 10; use Test::Fatal; use lib '.'; use t::lib::Functions; my $mc = mcpan(); can_ok( $mc, 'module' ); my $module = $mc->module('MetaCPAN::API'); isa_ok( $module, 'MetaCPAN::Client::Module' ); can_ok( $module, qw ); is( $module->distribution, 'MetaCPAN-API', 'Correct distribution' ); is( $module->name, 'API.pm', 'Correct name' ); is( $module->path, 'lib/MetaCPAN/API.pm', 'Correct path' ); my $rs = $mc->module( { path => 'lib/MetaCPAN' } ); isa_ok( $rs, 'MetaCPAN::Client::ResultSet' ); can_ok( $rs, 'total' ); ok( $rs->total > 0, 'More than a single result in result set' ); rating.t100644001750001750 104414520270707 17201 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/api#!perl use strict; use warnings; use Test::More tests => 7; use Test::Fatal; use lib '.'; use t::lib::Functions; my $mc = mcpan(); can_ok( $mc, 'rating' ); my $rs = $mc->rating( { distribution => 'Moose' } ); isa_ok( $rs, 'MetaCPAN::Client::ResultSet' ); can_ok( $rs, 'next' ); my $rating = $rs->next; isa_ok( $rating, 'MetaCPAN::Client::Rating' ); can_ok( $rating, 'distribution' ); is( $rating->distribution, 'Moose', 'Correct distribution' ); __END__ can_ok( $rs, 'name' ); is( $rating->name, 'MetaCPAN-Client', 'Correct distribution' ); examples000755001750001750 014520270707 16173 5ustar00mickeymickey000000000000MetaCPAN-Client-2.031000agg.pl100644001750001750 60714520270707 17411 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/agg.pl use strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $author = MetaCPAN::Client->new()->all( 'authors', { aggregations => { aggs => { terms => { field => "country" } } } } ); p $author->aggregations; pod.pl100644001750001750 21114520270707 17424 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/pod.pl use strict; use warnings; use MetaCPAN::Client; my $pod = MetaCPAN::Client->new->pod('Moo'); print $pod->html; _search.t100644001750001750 334614520270707 17330 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/api#!perl use strict; use warnings; use Test::More tests => 19; use Test::Fatal; use lib '.'; use t::lib::Functions; { no warnings qw; my $count = 0; *MetaCPAN::Client::ssearch = sub { my ( $self, $type, $args, $params ) = @_; ::isa_ok( $self, 'MetaCPAN::Client' ); ::is( $type, 'author', 'Correct type' ); ::is_deeply( $args, { hello => 'world' }, 'Correct args' ); if ( $count++ == 0 ) { ::is_deeply( $params, {}, 'Correct empty params' ); } else { ::is_deeply( $params, { a => 'b' }, 'Correct params' ); } return { a => 'ok' }; }; *MetaCPAN::Client::ResultSet::new = sub { my ( $self, %args ) = @_; ::isa_ok( $self, 'MetaCPAN::Client::ResultSet' ); ::is_deeply( \%args, { scroller => { a => 'ok' }, type => 'author', }, 'Correct args to ::ResultSet', ); return 'yoyo'; }; } my $mc = mcpan(); can_ok( $mc, '_search' ); like( exception { $mc->_search('author') }, qr/^_search takes a hash ref as query/, 'Failed with no query', ); like( exception { $mc->_search( 'author', { hello => 'world' }, 'fail' ) }, qr/^_search takes a hash ref as query parameters/, 'Failed with no query parameters', ); like( exception { $mc->_search( 'authorz', { hello => 'world' }, { a => 'b' } ) }, qr/^search type is not supported/, 'Unsupported search type', ); is( $mc->_search( 'author', { hello => 'world' } ), 'yoyo', 'Works with no query parameters', ); is( $mc->_search( 'author', { hello => 'world' }, { a => 'b' } ), 'yoyo', 'Correct _search call', ); package.t100644001750001750 127514520270707 17316 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/api#!perl use strict; use warnings; use Test::More tests => 10; use Ref::Util qw< is_arrayref is_ref >; use lib '.'; use t::lib::Functions; my $mc = mcpan(); can_ok( $mc, 'package' ); my $pack = $mc->package('MooseX::Types'); isa_ok( $pack, 'MetaCPAN::Client::Package' ); can_ok( $pack, qw< module_name file distribution version author > ); ok( !is_ref($pack->module_name), "module_name is not a ref"); ok( !is_ref($pack->file), "file is not a ref"); ok( !is_ref($pack->distribution), "distribution is not a ref"); ok( !is_ref($pack->version), "version is not a ref"); ok( !is_ref($pack->dist_version), "version is not a ref"); ok( !is_ref($pack->author), "author is not a ref"); release.t100644001750001750 100214520270707 17327 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/api#!perl use strict; use warnings; use Test::More tests => 7; use Test::Fatal; use lib '.'; use t::lib::Functions; my $mc = mcpan(); can_ok( $mc, 'release' ); my $release = $mc->release('MetaCPAN-API'); isa_ok( $release, 'MetaCPAN::Client::Release' ); can_ok( $release, qw ); is( $release->distribution, 'MetaCPAN-API', 'Correct distribution' ); like($release->checksum_sha256, qr/^[a-f0-9]{64}$/, 'Has a sha256 hexdigest'); like($release->checksum_md5, qr/^[a-f0-9]{32}$/, 'Has a md5 hexdigest'); favorite.t100644001750001750 75614520270707 17525 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/api#!perl use strict; use warnings; use Test::More tests => 2 + 4 * 2; use Test::Fatal; use lib '.'; use t::lib::Functions; my $mc = mcpan(); can_ok( $mc, 'favorite' ); foreach my $option ( { author => 'XSAWYERX' }, { dist => 'MetaCPAN-Client' } ) { my $rs = $mc->favorite($option); isa_ok( $rs, 'MetaCPAN::Client::ResultSet' ); can_ok( $rs, qw ); is( $rs->type, 'favorite', 'Correct resultset type' ); isa_ok( $rs->scroller, 'MetaCPAN::Client::Scroll' ); } cover.pl100644001750001750 27514520270707 17772 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples# examples/cover.pl use strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $mcpan = MetaCPAN::Client->new(); my $coverage = $mcpan->cover('Moose-2.2007'); p $coverage; 1; result_custom.t100644001750001750 202414520270707 20053 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t#!perl use strict; use warnings; use Test::More tests => 5; use Test::Fatal qw( exception ); use MetaCPAN::Client; use MetaCPAN::Client::ResultSet; { package Test::Result; use Moo; with 'MetaCPAN::Client::Role::Entity'; sub new_from_request { my ( $class, $request, $client ) = @_; return $class->new( ( defined $client ? ( client => $client ) : () ), data => $request, ); } sub _known_fields { +{} } } my $client = MetaCPAN::Client->new(); my $scroll = $client->ssearch( 'author', { pauseid => 'KENTNL' } ); my $rs = MetaCPAN::Client::ResultSet->new( class => 'Test::Result', scroller => $scroll, ); isa_ok( $rs, 'MetaCPAN::Client::ResultSet' ); can_ok( $rs, qw ); my $item; is( exception { $item = $rs->next; 1 }, undef, "no fail on next" ); isa_ok( $item, 'Test::Result' ); my $ex; isnt( $ex = exception { MetaCPAN::Client::ResultSet->new( scroller => $scroll ) }, undef, 'Must fail is neither class or type are passed' ); author.pl100644001750001750 64314520270707 20155 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/author.pl use strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $author = MetaCPAN::Client->new()->author('XSAWYERX'); my %output = ( NAME => $author->name, EMAILS => $author->email, COUNTRY => $author->country, CITY => $author->city, PROFILE => $author->profile, LINKS => $author->links, RELEASE_COUNTS => $author->release_count, ); p %output; mirror.pl100644001750001750 25114520270707 20160 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/mirror.pl use strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $mirrors = MetaCPAN::Client->new->mirror('eutelia.it'); p $mirrors; module.pl100644001750001750 62114520270707 20134 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/module.pl use strict; use warnings; use Data::Printer; 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; rating.pl100644001750001750 27314520270707 20136 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/rating.pl use strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $rating = MetaCPAN::Client->new->rating({ distribution => "Moose" }); p $rating->next; recent.pl100644001750001750 54114520270707 20130 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # 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; } totals.pl100644001750001750 77614520270707 20170 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examplesuse 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; permission.t100644001750001750 101414520270707 20102 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/api#!perl use strict; use warnings; use Test::More tests => 7; use Ref::Util qw< is_arrayref is_ref >; use lib '.'; use t::lib::Functions; my $mc = mcpan(); can_ok( $mc, 'permission' ); my $perm = $mc->permission('MooseX::Types'); isa_ok( $perm, 'MetaCPAN::Client::Permission' ); can_ok( $perm, qw< module_name owner co_maintainers > ); ok( !is_ref($perm->module_name), "module_name is not a ref"); ok( !is_ref($perm->owner), "owner is not a ref"); ok( is_arrayref($perm->co_maintainers), "co_maintainers is an arrayref"); lib000755001750001750 014520270707 15366 5ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/tFunctions.pm100644001750001750 47314520270707 20020 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/libuse strict; use warnings; use MetaCPAN::Client; use Test::More; my $version = $MetaCPAN::Client::VERSION || 'xx'; sub mcpan { my $mc = MetaCPAN::Client->new( ua_args => [ agent => "MetaCPAN::Client-testing/$version" ], @_, ); isa_ok( $mc, 'MetaCPAN::Client' ); return $mc; } 1; changes.pl100644001750001750 25514520270707 20262 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examplesuse strict; use warnings; use MetaCPAN::Client; my $dist = shift || 'Moose'; my $mcpan = MetaCPAN::Client->new(); my $rel = $mcpan->release($dist); print $rel->changes; complex.pl100644001750001750 55514520270707 20324 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/complex.pl use strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $authors = MetaCPAN::Client->new->author({ either => [ { name => 'Dave *' }, { name => 'David *' }, ] }); my %output = ( TOTAL => $authors->total, NAMES => [ map { $authors->next->name } 0 .. 9 ], ); p %output; package.pl100644001750001750 44114520270707 20242 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples# examples/package.pl use strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $mcpan = MetaCPAN::Client->new(); my $pack = $mcpan->package('MooseX::Types'); p $pack; 1; __END__ Alternatively: my $module = $mcpan->module('MooseX::Types'); my $pack = $module->package; release.pl100644001750001750 51114520270707 20265 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examplesuse strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $release = MetaCPAN::Client->new->release('Devel-NYTProf'); my %output = ( AUTHOR => $release->author, DATE => $release->date, STATUS => $release->status, VERSION => $release->version, TESTS => $release->tests, ); p %output; rev_deps.pl100644001750001750 62414520270707 20461 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # 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($module); my @output; 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; distribution.t100644001750001750 104714520270707 20437 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/api#!perl use strict; use warnings; use Test::More tests => 9; use Test::Fatal; use Ref::Util qw< is_hashref >; use lib '.'; use t::lib::Functions; my $mc = mcpan(); can_ok( $mc, 'distribution' ); my $dist = $mc->distribution('Business-ISBN'); isa_ok( $dist, 'MetaCPAN::Client::Distribution' ); can_ok( $dist, 'name' ); is( $dist->name, 'Business-ISBN', 'Correct distribution' ); can_ok( $dist, 'rt' ); ok( is_hashref( $dist->rt ), 'rt returns a hashref' ); can_ok( $dist, 'github' ); ok( is_hashref( $dist->github ), 'github returns a hashref' ); download_url.t100644001750001750 305014520270707 20405 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/api#!perl use strict; use warnings; use Test::More tests => 17; use Test::Fatal; use lib '.'; use t::lib::Functions; my $mc = mcpan(); can_ok( $mc, 'rating' ); { my $rs = $mc->download_url( 'Moose' ); isa_ok( $rs, 'MetaCPAN::Client::DownloadURL' ); can_ok( $rs, 'date' ); can_ok( $rs, 'download_url' ); can_ok( $rs, 'status' ); can_ok( $rs, 'version' ); } { note "request an older version"; # URL for 1.01 should not change over time, let's check it my $rs = $mc->download_url( 'Moose', 1.01 ); isa_ok( $rs, 'MetaCPAN::Client::DownloadURL' ); is $rs->version(), '1.01'; is $rs->download_url(), q[https://cpan.metacpan.org/authors/id/F/FL/FLORA/Moose-1.01.tar.gz], 'download_url for Moose-1.01'; is $rs->checksum_sha256(), q[f4424f4d709907dea8bc9de2a37b9d3fef4f87775a8c102f432c48a1fdf8067b], 'sha256 for Moose-1.0.1.tar.gz'; is $rs->checksum_md5(), q[f13f9c203d099f5dc6117f59bda96340], 'md5 for Moose-1.0.1.tar.gz'; } { note "request a range"; my $rs = $mc->download_url( 'Moose', '>1.01,<=2.00' ); isa_ok( $rs, 'MetaCPAN::Client::DownloadURL' ); is $rs->version(), '1.07'; is $rs->download_url(), q[https://cpan.metacpan.org/authors/id/F/FL/FLORA/Moose-1.07.tar.gz], 'download_url for Moose-1.07'; } { note "request a devel version with range"; my $rs = $mc->download_url( 'Try::Tiny', '>0.21,<0.27', 1 ); isa_ok( $rs, 'MetaCPAN::Client::DownloadURL' ); is $rs->version(), q[0.22], 'Try::Tiny >0.21,<0.27 dev=1'; } 00-report-prereqs.t100644001750001750 1360114520270707 20375 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t#!perl use strict; use warnings; # This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.029 use Test::More tests => 1; use ExtUtils::MakeMaker; use File::Spec; # from $version::LAX my $lax_version_re = qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )? | (?:\.[0-9]+) (?:_[0-9]+)? ) | (?: v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )? | (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)? ) )/x; # hide optional CPAN::Meta modules from prereq scanner # and check if they are available my $cpan_meta = "CPAN::Meta"; my $cpan_meta_pre = "CPAN::Meta::Prereqs"; my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic # Verify requirements? my $DO_VERIFY_PREREQS = 1; sub _max { my $max = shift; $max = ( $_ > $max ) ? $_ : $max for @_; return $max; } sub _merge_prereqs { my ($collector, $prereqs) = @_; # CPAN::Meta::Prereqs object if (ref $collector eq $cpan_meta_pre) { return $collector->with_merged_prereqs( CPAN::Meta::Prereqs->new( $prereqs ) ); } # Raw hashrefs for my $phase ( keys %$prereqs ) { for my $type ( keys %{ $prereqs->{$phase} } ) { for my $module ( keys %{ $prereqs->{$phase}{$type} } ) { $collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module}; } } } return $collector; } my @include = qw( ); my @exclude = qw( ); # Add static prereqs to the included modules list my $static_prereqs = do './t/00-report-prereqs.dd'; # Merge all prereqs (either with ::Prereqs or a hashref) my $full_prereqs = _merge_prereqs( ( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ), $static_prereqs ); # Add dynamic prereqs to the included modules list (if we can) my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; my $cpan_meta_error; if ( $source && $HAS_CPAN_META && (my $meta = eval { CPAN::Meta->load_file($source) } ) ) { $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs); } else { $cpan_meta_error = $@; # capture error from CPAN::Meta->load_file($source) $source = 'static metadata'; } my @full_reports; my @dep_errors; my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs; # Add static includes into a fake section for my $mod (@include) { $req_hash->{other}{modules}{$mod} = 0; } for my $phase ( qw(configure build test runtime develop other) ) { next unless $req_hash->{$phase}; next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING}); for my $type ( qw(requires recommends suggests conflicts modules) ) { next unless $req_hash->{$phase}{$type}; my $title = ucfirst($phase).' '.ucfirst($type); my @reports = [qw/Module Want Have/]; for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) { next if grep { $_ eq $mod } @exclude; my $want = $req_hash->{$phase}{$type}{$mod}; $want = "undef" unless defined $want; $want = "any" if !$want && $want == 0; if ($mod eq 'perl') { push @reports, ['perl', $want, $]]; next; } my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required"; my $file = $mod; $file =~ s{::}{/}g; $file .= ".pm"; my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC; if ($prefix) { my $have = MM->parse_version( File::Spec->catfile($prefix, $file) ); $have = "undef" unless defined $have; push @reports, [$mod, $want, $have]; if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) { if ( $have !~ /\A$lax_version_re\z/ ) { push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)"; } elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) { push @dep_errors, "$mod version '$have' is not in required range '$want'"; } } } else { push @reports, [$mod, $want, "missing"]; if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) { push @dep_errors, "$mod is not installed ($req_string)"; } } } if ( @reports ) { push @full_reports, "=== $title ===\n\n"; my $ml = _max( map { length $_->[0] } @reports ); my $wl = _max( map { length $_->[1] } @reports ); my $hl = _max( map { length $_->[2] } @reports ); if ($type eq 'modules') { splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports; } else { splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports; } push @full_reports, "\n"; } } } if ( @full_reports ) { diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports; } if ( $cpan_meta_error || @dep_errors ) { diag "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n"; } if ( $cpan_meta_error ) { my ($orig_source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; diag "\nCPAN::Meta->load_file('$orig_source') failed with: $cpan_meta_error\n"; } if ( @dep_errors ) { diag join("\n", "\nThe following REQUIRED prerequisites were not satisfied:\n", @dep_errors, "\n" ); } pass('Reported prereqs'); # vim: ts=4 sts=4 sw=4 et: es_filter.pl100644001750001750 131314520270707 20642 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examplesuse strict; use warnings; use MetaCPAN::Client; # use raw ES filter on a { match_all => {} } query # find 'latest' status releases with at least 700 failing tests # which consist at least 50% of their overall number of tests. my $release = MetaCPAN::Client->new->all( 'releases', { es_filter => { and => [ { range => { 'tests.fail' => { gte => 700 } } }, { term => { 'status' => 'latest' } } ] }, } ); while ( my $r = $release->next ) { my $fail = $r->tests->{fail}; my $all = 0; $all += $_ for @{ $r->tests }{qw/pass fail na unknown/}; ($fail / $all) >= 0.5 and printf "%4d/%4d: %s\n", $fail, $all, $r->name; } author000755001750001750 014520270707 16312 5ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/xtpod-syntax.t100644001750001750 25214520270707 20724 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/xt/author#!perl # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use strict; use warnings; use Test::More; use Test::Pod 1.41; all_pod_files_ok(); 00-report-prereqs.dd100644001750001750 546114520270707 20506 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/tdo { my $x = { 'configure' => { 'requires' => { 'ExtUtils::MakeMaker' => '7.1101' } }, 'develop' => { 'requires' => { 'File::Spec' => '0', 'HTTP::Tiny::Mech' => '1.001002', 'IO::Handle' => '0', 'IPC::Open3' => '0', 'LWP::Protocol::https' => '0', 'Pod::Coverage::TrustPod' => '0', 'Test::More' => '0', 'Test::Pod' => '1.41', 'Test::Pod::Coverage' => '1.08', 'WWW::Mechanize::Cached' => '1.54' } }, 'runtime' => { 'requires' => { 'Carp' => '0', 'HTTP::Tiny' => '0.056', 'IO::Socket::SSL' => '1.42', 'JSON::MaybeXS' => '0', 'JSON::PP' => '0', 'Moo' => '0', 'Moo::Role' => '0', 'Net::SSLeay' => '1.49', 'Ref::Util' => '0', 'Safe::Isa' => '0', 'Type::Tiny' => '0', 'URI::Escape' => '0', 'perl' => '5.010', 'strict' => '0', 'warnings' => '0' } }, 'test' => { 'recommends' => { 'CPAN::Meta' => '2.120900', 'HTTP::Tiny::Mech' => '1.001002', 'WWW::Mechanize::Cached' => '1.54' }, 'requires' => { 'ExtUtils::MakeMaker' => '0', 'File::Spec' => '0', 'LWP::Protocol::https' => '0', 'Test::Fatal' => '0', 'Test::More' => '0', 'Test::Needs' => '0.002005', 'base' => '0', 'blib' => '1.01' } } }; $x; }00-compile.t100644001750001750 376414520270707 20516 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/xt/authoruse 5.006; use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::Compile 2.058 use Test::More; plan tests => 21; my @module_files = ( 'MetaCPAN/Client.pm', 'MetaCPAN/Client/Author.pm', 'MetaCPAN/Client/Cover.pm', 'MetaCPAN/Client/Distribution.pm', 'MetaCPAN/Client/DownloadURL.pm', 'MetaCPAN/Client/Favorite.pm', 'MetaCPAN/Client/File.pm', 'MetaCPAN/Client/Mirror.pm', 'MetaCPAN/Client/Module.pm', 'MetaCPAN/Client/Package.pm', 'MetaCPAN/Client/Permission.pm', 'MetaCPAN/Client/Pod.pm', 'MetaCPAN/Client/Rating.pm', 'MetaCPAN/Client/Release.pm', 'MetaCPAN/Client/Request.pm', 'MetaCPAN/Client/ResultSet.pm', 'MetaCPAN/Client/Role/Entity.pm', 'MetaCPAN/Client/Role/HasUA.pm', 'MetaCPAN/Client/Scroll.pm', 'MetaCPAN/Client/Types.pm' ); # no fake home requested my @switches = ( -d 'blib' ? '-Mblib' : '-Ilib', ); use File::Spec; use IPC::Open3; use IO::Handle; open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!"; my @warnings; for my $lib (@module_files) { # see L my $stderr = IO::Handle->new; diag('Running: ', join(', ', map { my $str = $_; $str =~ s/'/\\'/g; q{'} . $str . q{'} } $^X, @switches, '-e', "require q[$lib]")) if $ENV{PERL_COMPILE_TEST_DEBUG}; my $pid = open3($stdin, '>&STDERR', $stderr, $^X, @switches, '-e', "require q[$lib]"); binmode $stderr, ':crlf' if $^O eq 'MSWin32'; my @_warnings = <$stderr>; waitpid($pid, 0); is($?, 0, "$lib loaded ok"); shift @_warnings if @_warnings and $_warnings[0] =~ /^Using .*\bblib/ and not eval { +require blib; blib->VERSION('1.01') }; if (@_warnings) { warn @_warnings; push @warnings, @_warnings; } } is(scalar(@warnings), 0, 'no warnings found') or diag 'got warnings: ', ( Test::More->can('explain') ? Test::More::explain(\@warnings) : join("\n", '', @warnings) ); permission.pl100644001750001750 45214520270707 21041 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples# examples/permission.pl use strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $mcpan = MetaCPAN::Client->new(); my $perm = $mcpan->permission('MooseX::Types'); p $perm; 1; __END__ Alternatively: my $module = $mcpan->module('MooseX::Types'); my $perm = $module->permission; MetaCPAN000755001750001750 014520270707 16453 5ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/libClient.pm100644001750001750 5574614520270707 20430 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPANuse strict; use warnings; package MetaCPAN::Client; # ABSTRACT: A comprehensive, DWIM-featured client to the MetaCPAN API $MetaCPAN::Client::VERSION = '2.031000'; use Moo; use Carp; use Ref::Util qw< is_arrayref is_hashref is_ref >; use URI::Escape qw< uri_escape_utf8 >; use MetaCPAN::Client::Request; use MetaCPAN::Client::Author; use MetaCPAN::Client::Distribution; use MetaCPAN::Client::DownloadURL; 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::Mirror; use MetaCPAN::Client::Package; use MetaCPAN::Client::Permission; use MetaCPAN::Client::ResultSet; use MetaCPAN::Client::Cover; has request => ( is => 'ro', handles => [qw], ); my @supported_searches = qw< author distribution favorite module rating release mirror file permission package cover >; sub BUILDARGS { my ( $class, %args ) = @_; $args{'request'} ||= MetaCPAN::Client::Request->new( ( ua => $args{ua} )x!! $args{ua}, ( domain => $args{domain} )x!! $args{domain}, ( debug => $args{debug} )x!! $args{debug}, ); return \%args; } sub author { my $self = shift; my $arg = shift; my $params = shift; return $self->_get_or_search( 'author', $arg, $params ); } sub module { my $self = shift; my $arg = shift; my $params = shift; return $self->_get_or_search( 'module', $arg, $params ); } sub distribution { my $self = shift; my $arg = shift; my $params = shift; return $self->_get_or_search( 'distribution', $arg, $params ); } sub file { my $self = shift; my $arg = shift; my $params = shift; return $self->_get_or_search( 'file', $arg, $params ); } sub package { my $self = shift; my $arg = shift; my $params = shift; return $self->_get_or_search( 'package', $arg, $params ); } sub permission { my $self = shift; my $arg = shift; my $params = shift; return $self->_get_or_search( 'permission', $arg, $params ); } sub cover { my $self = shift; my $arg = shift; my $params = shift; return $self->_get_or_search( 'cover', $arg, $params ); } sub pod { my $self = shift; my $name = shift; my $params = shift || {}; return MetaCPAN::Client::Pod->new({ request => $self->request, name => $name, %$params }); } sub favorite { my $self = shift; my $args = shift; my $params = shift; is_hashref($args) or croak 'favorite takes a hash ref as parameter'; return $self->_search( 'favorite', $args, $params ); } sub rating { my $self = shift; my $args = shift; my $params = shift; is_hashref($args) or croak 'rating takes a hash ref as parameter'; return $self->_search( 'rating', $args, $params ); } sub release { my $self = shift; my $arg = shift; my $params = shift; return $self->_get_or_search( 'release', $arg, $params ); } sub mirror { my $self = shift; my $arg = shift; my $params = shift; return $self->_get_or_search( 'mirror', $arg, $params ); } sub reverse_dependencies { my $self = shift; my $dist = shift; $dist =~ s/::/-/g; return $self->_reverse_deps($dist); } *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 $params = shift; # This endpoint used to support only pluralized types (mostly) and convert # to singular types before redispatching. Now it accepts both plural and # unplural forms directly and relies on the underlying methods it # dispatches to to check types (using the global supported types array). $type =~ s/s$//; $params and !is_hashref($params) and croak "all: params must be a hashref"; if ( $params->{fields} and !is_arrayref($params->{fields}) ) { $params->{fields} = [ split /,/ => $params->{fields} ]; } return $self->$type( { __MATCH_ALL__ => 1 }, $params ); } sub download_url { my $self = shift; my $module = shift; my $version_or_range = shift; my $dev = shift; my $uri = $module; my @extra; if ( defined $version_or_range ) { my @valid_ranges = qw{ == != <= >= < > ! }; my $is_using_range; foreach my $range ( @valid_ranges ) { if ( index( $version_or_range, $range ) >= 0 ) { $is_using_range = 1; last; } } # by default use the '==' operator when no range set $version_or_range = '==' . $version_or_range unless $is_using_range; # version=>0.21,<0.27,!=0.26&dev=1 push @extra, 'version=' .uri_escape_utf8($version_or_range); } if ( defined $dev ) { push @extra, 'dev=' . uri_escape_utf8($dev); } $uri .= '?'.join('&', @extra) if scalar @extra; return $self->_get( 'download_url', $uri ); } sub autocomplete { my $self = shift; my $q = shift; my $res; eval { $res = $self->fetch( '/search/autocomplete?q=' . uri_escape_utf8($q) ); 1; } or do { warn $@; return []; }; return [ map { $_->{fields} } @{ $res->{hits}{hits} } ]; } sub autocomplete_suggest { my $self = shift; my $q = shift; my $res; eval { $res = $self->fetch( '/search/autocomplete/suggest?q=' . uri_escape_utf8($q) ); 1; } or do { warn $@; return []; }; return $res->{suggestions}; } ### sub _get { my $self = shift; ( scalar(@_) == 2 or ( scalar(@_) == 3 and ( !defined $_[2] or is_hashref($_[2]) ) ) ) or croak '_get takes type and search string as parameters (and an optional params hash)'; my $type = shift; my $arg = shift; my $params = shift; my $fields_filter = $self->_read_fields( $params ); my $response = $self->fetch( sprintf("%s/%s%s", $type ,$arg, $fields_filter||'') ); is_hashref($response) or croak sprintf( 'Failed to fetch %s (%s)', ucfirst($type), $arg ); $type = 'DownloadURL' if $type eq 'download_url'; my $class = 'MetaCPAN::Client::' . ucfirst($type); return $class->new_from_request($response, $self); } sub _read_fields { my $self = shift; my $params = shift; $params or return; my $fields = delete $params->{fields}; $fields or return; if ( is_arrayref($fields) ) { grep { ref $_ } @$fields and croak "fields array should not contain any refs."; return sprintf( "?fields=%s", join q{,} => @$fields ); } elsif ( !ref $fields ) { return "?fields=$fields"; } croak "invalid param: fields"; } sub _search { my $self = shift; my $type = shift; my $args = shift; my $params = shift; is_hashref($args) or croak '_search takes a hash ref as query'; ! defined $params or is_hashref($params) or croak '_search takes a hash ref as query parameters'; $params ||= {}; grep { $_ eq $type } @supported_searches or croak 'search type is not supported'; my $scroller = $self->ssearch($type, $args, $params); return MetaCPAN::Client::ResultSet->new( scroller => $scroller, type => $type, ); } sub _get_or_search { my $self = shift; my $type = shift; my $arg = shift; my $params = shift; is_hashref($arg) and return $self->_search( $type, $arg, $params ); defined $arg and !is_ref($arg) and return $self->_get($type, $arg, $params); croak "$type: invalid args (takes scalar value or search parameters hashref)"; } sub _reverse_deps { my $self = shift; my $dist = shift; my $res; eval { $res = $self->fetch( "/reverse_dependencies/dist/$dist", { size => 5000, query => { match_all => {} }, filter => { and => [ { term => { 'status' => 'latest' } }, { term => { 'authorized' => 1 } }, ] }, } ); 1; } or do { warn $@; return _empty_result_set('release'), }; return MetaCPAN::Client::ResultSet->new( items => $res->{'data'}, 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; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client - A comprehensive, DWIM-featured client to the MetaCPAN API =head1 VERSION version 2.031000 =head1 SYNOPSIS # simple usage my $mcpan = MetaCPAN::Client->new(); my $author = $mcpan->author('XSAWYERX'); my $dist = $mcpan->distribution('MetaCPAN-Client'); # advanced usage with cache (contributed by Kent Fredric) use CHI; use WWW::Mechanize::Cached; use HTTP::Tiny::Mech; use MetaCPAN::Client; my $mcpan = MetaCPAN::Client->new( ua => HTTP::Tiny::Mech->new( mechua => WWW::Mechanize::Cached->new( cache => CHI->new( driver => 'File', root_dir => '/tmp/metacpan-cache', ), ), ), ); # now $mcpan caches results =head1 DESCRIPTION This is a hopefully-complete API-compliant client to MetaCPAN (L) with DWIM capabilities, to make your life easier. =head1 ATTRIBUTES =head2 request Internal attribute representing the request object making the request to MetaCPAN and analyzing the results. You probably don't want to set this, nor should you have any usage of it. =head2 ua If provided, L will use the user agent object instead of the default, which is L. Then it can be used to fetch the user agent object used by L. =head2 domain If given, will be used to alter the API domain. =head2 debug If given, errors will include some low-level detailed message. =head1 METHODS =head2 author my $author = $mcpan->author('XSAWYERX'); my $author = $mcpan->author($search_spec); Finds an author by either its PAUSE ID or by a search spec defined by a hash reference. Since it is common to many other searches, it is explained below under C. Returns a L object on a simple search (PAUSE ID), or a L object populated with L objects on a complex (L) search. =head2 cover my $cover = $mcpan->cover('Moose-2.2007'); Returns a L object. =head2 distribution 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 explained below under C. Returns a L object on a simple search (distribution name), or a L object populated with L objects on a complex (L) search. =head2 file Returns a L object. =head2 favorite my $favorite = $mcpan->favorite({ distribution => 'Moose' }); Returns a L object containing L results. =head2 rating my $rating = $mcpan->rating({ distribution => 'Moose' }); Returns a L object containing L results. =head2 release my $release = $mcpan->release('MetaCPAN-Client'); my $release = $mcpan->release($search_spec); Finds a release 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 explained below under C. Returns a L object on a simple search (release name), or a L object populated with L objects on a complex (L) search. =head2 mirror my $mirror = $mcpan->mirror('kr.freebsd.org'); Returns a L object. =head2 module my $module = $mcpan->module('MetaCPAN::Client'); my $module = $mcpan->module($search_spec); Finds a module by either its module name or by a search spec defined by a hash reference. Since it is common to many other searches, it is explained below under C. Returns a L object on a simple search (module name), or a L object populated with L objects on a complex (L) search. =head2 package my $package = $mcpan->package('MooseX::Types'); Returns a L object. =head2 permission my $permission = $mcpan->permission('MooseX::Types'); Returns a L object. =head2 reverse_dependencies my $deps = $mcpan->reverse_dependencies('Search::Elasticsearch'); all L objects of releases that are directly dependent on a given module, returned as L. =head2 rev_deps Alias to C described above. =head2 autocomplete my $ac = $mcpan->autocomplete('Danc'); Call the search/autocomplete endpoint with a query string. Returns an array reference. =head2 autocomplete_suggest my $ac = $mcpan->autocomplete_suggest('Moo'); Call the search/autocomplete/suggest endpoint with a query string. Returns an array reference. =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 of L. =head2 pod Get POD for given file/module name. returns a L object, which supports various output formats (html, plain, x_pod & x_markdown). my $pod = $mcpan->pod('Moo')->html; my $pod = $mcpan->pod('Moo', { url_prefix => $prefix })->html; =head2 download_url Retrieve information from the 'download_url' endpoint my $download_url = $mcpan->download_url($distro, [$version_or_range, $dev]); # request the last available version my $download_url = $mcpan->download_url('Moose'); # request an older version my $download_url = $mcpan->download_url('Moose', '1.01'); # using a range my $download_url = $mcpan->download_url('Moose', '<=1.01'); my $download_url = $mcpan->download_url('Moose', '>1.01,<=2.00'); Range operators are '== != <= >= < > !'. You can use a comma ',' to add multiple rules. # requesting dev release my $download_url = $mcpan->download_url('Moose', '>1.01', 1); Returns a L object =head2 all Retrieve all matches for authors/modules/distributions/favorites or releases. my $all_releases = $mcpan->all('releases') When called with a second parameter containing a hash ref, will support the following keys: =head3 fields See SEARCH PARAMS. my $all_releases = $mcpan->all('releases', { fields => [...] }) =head3 _source See SEARCH PARAMS. my $all_releases = $mcpan->all('releases', { _source => [...] }) =head3 es_filter Pass a raw Elasticsearch filter structure to reduce the number of elements returned by the query. my $some_releases = $mcpan->all('releases', { es_filter => {...} }) =head2 BUILDARGS Internal construction wrapper. Do not use. =head1 SEARCH PARAMS Most searches take params as an optional hash-ref argument. these params will be passed to the search action. In non-scrolled searches, 'fields' filter is the only supported parameter ATM. =head2 fields Filter the fields to reduce the amount of data pulled from MetaCPAN. can be passed as a csv list or an array ref. my $module = $mcpan->module('Moose', { fields => "version,author" }); my $module = $mcpan->module('Moose', { fields => [qw/version author/] }); =head2 _source Note: this param and its description are a bit too Elasticsearch specific. just like 'es_filter' - use only if you know what you're dealing with. Some fields are not indexed in Elasticsearch but stored as part of the entire document. These fields can still be read, but without the internal Elasticsearch optimizations and the server will internally read the whole document. Why do we even need those? because we don't index everything and some things we can't to begin with (like non-leaf fields that hold a structure) my $module = $mcpan->all('releases', { _source => "stat" }); =head2 scroller_time Note: please use with caution. This parameter will set the maximum lifetime of the Elasticsearch scroller on the server (default = '5m'). Normally you do not need to set this value (as tweaking this value can affect resources on the server). In case you do, you probably need to check the efficiency of your code/queries. (Feel free to reach out to us for assistance). my $module = $mcpan->all('releases', { scroller_time => '3m' }); =head2 scroller_size Note: please use with caution. This parameter will set the buffer size to be pulled from Elasticsearch when scrolling (default = 1000). This will affect query performance and memory usage, but you will still get an iterator back to fetch one object at a time. my $module = $mcpan->all('releases', { scroller_size => 500 }); =head3 sort Pass a raw Elasticsearch sort specification for the query. my $some_releases = $mcpan->all('releases', { sort => [{ date => { order => 'desc' } }] }) Note: this param and is a bit too specific to Elasticsearch. Just like L, only use this if you know what you're dealing with. =head1 SEARCH SPEC The hash-based search spec is common to many searches. It is quite feature-rich and allows you to disambiguate different types of searches. Basic search specs just contain a hash of keys and values: my $author = $mcpan->author( { name => 'Micha Nasriachi' } ); # the following is the same as ->author('MICKEY') my $author = $mcpan->author( { pauseid => 'MICKEY' } ); # find all people named Dave, not covering Davids # will return a resultset my $daves = $mcpan->author( { name => 'Dave *' } ); =head2 OR If you want to do a more complicated query that has an I condition, such as "this or that", you can use the following syntax with the C key: # any author named "Dave" or "David" my $daves = $mcpan->author( { either => [ { name => 'Dave *' }, { name => 'David *' }, ] } ); =head2 AND If you want to do a more complicated query that has an I condition, such as "this and that", you can use the following syntax with the C key: # any users named 'John' with a Gmail account my $johns = $mcpan->author( { all => [ { name => 'John *' }, { email => '*gmail.com' }, ] } ); Or, to get either the given version of a release, or the latest: my $releases = $mcpan->release( { all => [ { distribution => 'GraphViz2' }, ($version ? { version => $version } : { status => 'latest' }), ], } ); 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 adding a I filter condition, such as "not these", you can use the following syntax with the C key: # any author named "Dave" or "David" my $daves = $mcpan->author( { either => [ { name => 'Dave *' }, { name => 'David *' }, ], not => [ { email => '*gmail.com' }, ], } ); =head1 DESIGN This module has three purposes: =over 4 =item * Provide 100% of the MetaCPAN API This module will be updated regularly on every MetaCPAN API change, and intends to provide the user with as much of the API as possible, no shortcuts. If it's documented in the API, you should be able to do it. Because of this design decision, this module has an official MetaCPAN namespace with the blessing of the MetaCPAN developers. Notice this module currently only provides the beta API, not the old soon-to-be-deprecated API. =item * Be lightweight, to allow flexible usage While many modules would help make writing easier, it's important to take into account how they affect your compile-time, run-time, overall memory consumption, and CPU usage. By providing a slim interface implementation, more users are able to use this module, such as long-running processes (like daemons), CLI or GUI applications, cron jobs, and more. =item * DWIM While it's possible to access the methods defined by the API spec, there's still a matter of what you're really trying to achieve. For example, when searching for I<"Dave">, you want to find both I and I (and any other I), but you also want to search for a PAUSE ID of I, if one exists. This is where DWIM comes in. This module provides you with additional generic methods which will try to do what they think you want. Of course, this does not prevent you from manually using the API methods. You still have full control over that, if that's what you wish. You can (and should) read up on the general methods, which will explain how their DWIMish nature works, and what searches they run. =back =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 _get_or_search.t100644001750001750 214014520270707 20656 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/api#!perl use strict; use warnings; use Test::More tests => 10; use Test::Fatal; use lib '.'; use t::lib::Functions; { no warnings qw; *MetaCPAN::Client::_search = sub { my ( $self, $type, $arg, $params ) = @_; ::isa_ok( $self, 'MetaCPAN::Client' ); ::is( $type, 'type', 'Correct type' ); ::is_deeply( $arg, { hello => 'world' }, 'Correct arg' ); ::is_deeply( $params, { this => 'that' }, 'Correct params' ); }; *MetaCPAN::Client::_get = sub { my ( $self, $type, $arg ) = @_; ::isa_ok( $self, 'MetaCPAN::Client' ); ::is( $type, 'typeB', 'Correct type in _get' ); ::is( $arg, 'argb', 'Correct arg in _get' ); }; } my $mc = mcpan(); can_ok( $mc, '_get_or_search' ); # if arg is hash, it should call _search with it $mc->_get_or_search( 'type', { hello => 'world' }, { this => 'that' } ); # if not, check for arg and call _get $mc->_get_or_search( 'typeB', 'argb' ); # make arg fail check like( exception { $mc->_get_or_search( 'type', sub {1} ) }, qr/^type: invalid args/, 'Failed execution', ); pod-coverage.t100644001750001750 36514520270707 21176 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/xt/author#!perl # This file was automatically generated by Dist::Zilla::Plugin::PodCoverageTests. use strict; use warnings; use Test::Pod::Coverage 1.08; use Pod::Coverage::TrustPod; all_pod_coverage_ok({ coverage_class => 'Pod::Coverage::TrustPod' }); autocomplete.pl100644001750001750 26614520270707 21355 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/autocomplete.pl use strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $mcpan = MetaCPAN::Client->new(); my $ac = $mcpan->autocomplete("Danc"); p $ac; contributors.pl100644001750001750 45714520270707 21413 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examplesuse strict; use warnings; use MetaCPAN::Client; use Data::Printer; my $mcpan = MetaCPAN::Client->new(); my $release = $mcpan->release({ all => [ { distribution => 'Moose' }, { version => '2.2005' }, ] })->next; my $contributors = $release->contributors; p $contributors; 1; distribution.pl100644001750001750 43714520270707 21373 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/distribution.pl use strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $mcpan = MetaCPAN::Client->new(); my $dist = $mcpan->distribution('Moose'); my %output = ( NAME => $dist->name, BUGS => $dist->bugs, RIVER => $dist->river, ); p %output; download_url.pl100644001750001750 61214520270707 21340 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/download_url.pl use strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $mcpan = MetaCPAN::Client->new(); my $download_url = $mcpan->download_url('Moose'); my %output = ( VERSION => $download_url->version, STATUS => $download_url->status, DATE => $download_url->date, DOWNLOAD_URL => $download_url->download_url, ); p %output; metacpan_url.pl100644001750001750 120114520270707 21334 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examplesuse strict; use warnings; use MetaCPAN::Client; my $mcpan = MetaCPAN::Client->new(); my $auth = $mcpan->author('HAARG'); my $mod = $mcpan->module('Moo'); my $file = $mcpan->file('HAARG/Moo-2.003001/lib/Moo.pm'); my $dist = $mcpan->distribution('Moo'); my $rel = $mcpan->release({ all => [ { distribution => 'Moo' }, { version => '2.002005' }, ] }); printf "AUTHOR : %s\n", $auth->metacpan_url; printf "RELEASE : %s\n", $rel->next->metacpan_url; printf "MODULE : %s\n", $mod->metacpan_url; printf "FILE : %s\n", $file->metacpan_url; printf "DISTRIBUTION : %s\n", $dist->metacpan_url; 1; recent_today.pl100644001750001750 52014520270707 21325 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examplesuse 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; } authors_blogs.pl100644001750001750 100414520270707 21536 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examplesuse strict; use warnings; use MetaCPAN::Client; use Ref::Util qw< is_arrayref >; my $mcpan = MetaCPAN::Client->new; my $all_authors = $mcpan->all('authors'); AUTHOR: while ( my $author = $all_authors->next ) { BLOG: for my $blog ( @{ $author->blog || [] } ) { $blog and exists $blog->{url} or next BLOG; my $url = $blog->{url}; my $blogs_csv = is_arrayref($url) ? join q{,} => @$url : $url; printf "%-10s: %s\n", $author->pauseid, $blogs_csv; } } fields-filter.pl100644001750001750 36214520270707 21402 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/fields-filter.pl use strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $module = MetaCPAN::Client->new->module('Moose', { fields => [ qw/ author version / ] }); p $module; author-country.pl100644001750001750 56714520270707 21663 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/author-country.pl use strict; use warnings; use Data::Printer; use MetaCPAN::Client; my @countries = qw; my %cc2authors; for my $cc ( @countries ) { my $authors = MetaCPAN::Client->new->author({ country => $cc }); $cc2authors{$cc} = $authors->total; } p %cc2authors; Client000755001750001750 014520270707 17671 5ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPANPod.pm100644001750001750 430014520270707 21106 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::Pod; # ABSTRACT: A Pod object $MetaCPAN::Client::Pod::VERSION = '2.031000'; use Moo; use Carp; use MetaCPAN::Client::Types qw< Str >; has request => ( is => 'ro', handles => [qw], required => 1, ); has name => ( is => 'ro', required => 1 ); has url_prefix => ( is => 'ro', isa => Str, ); 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 $url = 'pod/' . $self->name . '?content-type=text/' . $ctype; $self->url_prefix and $url .= '&url_prefix=' . $self->url_prefix; return $self->request->fetch($url); } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::Pod - A Pod object =head1 VERSION version 2.031000 =head1 SYNOPSIS use strict; use warnings; use MetaCPAN::Client; my $pod = MetaCPAN::Client->new->pod('Moo'); print $pod->html; =head1 DESCRIPTION A MetaCPAN pod entity object. =head1 ATTRIBUTES =head2 request A L object (created in L) =head2 name The name of the module (probably always the value passed to the pod() method) =head2 url_prefix Prefix to be passed through the url_prefix query parameter to the 'pod' endpoint =head2 x_pod The raw pod extracted from the file. =head2 html Formatted as an HTML chunk (No ...) =head2 x_markdown Converted to Markdown. =head2 plain Formatted as plain text. Get the plaintext version of the documentation $pod = MetaCPAN::Client->new->pod( "MetaCPAN::Client" ); print $pod->plain; =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 author_releases.pl100644001750001750 40714520270707 22036 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examplesuse strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $mcpan = MetaCPAN::Client->new; my $author = $mcpan->author('XSAWYERX'); my $releases = $author->releases; p $releases->total; while ( my $rel = $releases->next ) { p $rel->name; } top20_favorites.pl100644001750001750 102614520270707 21715 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/top20_favorites use strict; use warnings; use MetaCPAN::Client; my $top20_fav = MetaCPAN::Client->new()->all( 'favorites', { aggregations => { aggs => { terms => { field => "distribution", size => 20, } } } } ); for my $bucket ( @{ $top20_fav->aggregations->{aggs}{buckets} } ) { printf "%s : %s\n", $bucket->{doc_count}, $bucket->{key} } File.pm100644001750001750 1362414520270707 21274 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::File; # ABSTRACT: A File data object $MetaCPAN::Client::File::VERSION = '2.031000'; use Moo; use Carp; with 'MetaCPAN::Client::Role::Entity'; my %known_fields = ( scalar => [qw< abstract author authorized binary date deprecated description directory distribution documentation download_url id indexed level maturity mime name path release sloc slop status version version_numified >], arrayref => [qw< module pod_lines >], hashref => [qw< stat >], ); my @known_fields = map { @{ $known_fields{$_} } } qw< scalar arrayref hashref >; foreach my $field (@known_fields) { has $field => ( is => 'ro', lazy => 1, default => sub { my $self = shift; return $self->data->{$field}; }, ); } sub _known_fields { return \%known_fields } sub pod { my $self = shift; my $ctype = shift || "plain"; $ctype = lc($ctype); grep { $ctype eq $_ } qw or croak "wrong content-type for POD requested"; my $name = $self->module->[0]{name}; return unless $name; require MetaCPAN::Client::Request; return MetaCPAN::Client::Request->new->fetch( "pod/${name}?content-type=text/${ctype}" ); } sub source { my $self = shift; my $author = $self->author; my $release = $self->release; my $path = $self->path; require MetaCPAN::Client::Request; return MetaCPAN::Client::Request->new->fetch( "source/${author}/${release}/${path}" ); } sub metacpan_url { my $self = shift; sprintf("https://metacpan.org/source/%s/%s/%s", $self->author, $self->release, $self->path ); } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::File - A File data object =head1 VERSION version 2.031000 =head1 DESCRIPTION A MetaCPAN file entity object. =head1 ATTRIBUTES =head2 status Returns a release status like C, C, or C. =head2 date An ISO8601 datetime string like C<2016-11-19T12:41:46> indicating when the file was uploaded. =head2 author The author's PAUSE id. =head2 maturity This will be either C or C. =head2 directory A boolean indicating whether or not the path represents a directory. =head2 indexed A boolean indicating whether or not the content is indexed. =head2 documentation The name of the module for which this file contains docs. This may be C =head2 id The file's internal MetaCPAN id. =head2 authorized A boolean indicating whether or not this file was part of an authorized upload. =head2 version The distribution version that contains this file. =head2 version_numified The numified version of the distribution that contains the file. =head2 release The release that contains this file, which will be something like C. =head2 binary A boolean indicating whether or not this file contains binary content. =head2 name The File's name, without any directory path included. =head2 path The file's path I, relative to the root of the archive. =head2 abstract If the file contains POD with a C section, then this attribute will include the abstract portion of the name. =head2 deprecated The deprecated field value for this file. =head2 description If the file contains POD with a C section, then this attribute will contain that description. =head2 distribution The name of the distribution that contains the file. =head2 level A 0-indexed indication of how many directories deep this file is, relative to the archive root. =head2 sloc If the file contains code, this will contain the number of lines of code in the file. =head2 slop If the file contains POD, this will contain the number of lines of POD in the file. =head2 mime The file's mime type. =head2 module If the file contains module indexed by PAUSE, then this attribute contains an arrayref of hashrefs, one for each module. The hashrefs have the following keys: =over 4 =item * name The module name. =item * indexed Whether or not the file is indexed by MetaCPAN. =item * authorized Whether or not the module is part of an authorized upload. =item * version The version of the module that this file contains. =item * version_numified The numified version of the module that this file contains. =item * associated_pod A path you can use with the C<< MetaCPAN::Client->file >> method to get the file that contains POD for this module. In most cases, that will be the same file as that one that contains this C data. =back =head2 pod_lines An arrayref. =head2 stat A hashref containing C all information about the file. The keys are: =over 4 =item * mtime The Unix epoch of the file's last modified time. =item * mode The file's mode (as an integer, not an octal representation). =item * size The file's size in bytes. =back =head2 download_url A URL for the distribution archive that contains this file. =head1 METHODS =head2 pod my $pod = $module->pod(); # default = plain my $pod = $module->pod($type); Returns the POD content for the module/file. Takes a type as argument. Supported types: B, B, B, B. =head2 source my $source = $module->source(); Returns the source code for the file. =head2 metacpan_url Returns a link to the file source page on MetaCPAN. =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 Cover.pm100644001750001750 313014520270707 21442 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::Cover; # ABSTRACT: A Cover data object $MetaCPAN::Client::Cover::VERSION = '2.031000'; use Moo; with 'MetaCPAN::Client::Role::Entity'; my %known_fields = ( scalar => [qw< distribution release version >], arrayref => [], hashref => [qw< criteria >], ); my @known_fields = map { @{ $known_fields{$_} } } qw< scalar arrayref hashref >; foreach my $field (@known_fields) { has $field => ( is => 'ro', lazy => 1, default => sub { my $self = shift; return $self->data->{$field}; }, ); } sub _known_fields { return \%known_fields } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::Cover - A Cover data object =head1 VERSION version 2.031000 =head1 SYNOPSIS my $cover = $mcpan->cover('Moose-2.2007'); =head1 DESCRIPTION A MetaCPAN cover entity object. =head1 ATTRIBUTES =head2 distribution Returns the name of the distribution. =head2 release Returns the name of the release. =head2 version Returns the version of the release. =head2 criteria Returns a hashref with the coverage stats for the release. Will contain one or more of the following keys: 'branch', 'condition', 'statement', 'subroutine', 'total' =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 Types.pm100644001750001750 244414520270707 21477 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::Types; # ABSTRACT: type checking helper class $MetaCPAN::Client::Types::VERSION = '2.031000'; use Type::Tiny (); use Types::Standard (); use Ref::Util qw< is_ref >; use parent 'Exporter'; our @EXPORT_OK = qw< Str Int Time ArrayRef HashRef Bool >; sub Str { Types::Standard::Str } sub Int { Types::Standard::Int } sub ArrayRef { Types::Standard::ArrayRef } sub HashRef { Types::Standard::HashRef } sub Bool { Types::Standard::Bool } sub Time { return Type::Tiny->new( name => "Time", constraint => sub { !is_ref($_) and /^[1-9][0-9]*(?:s|m|h)$/ }, message => sub { "$_ is not in time format (e.g. '5m')" }, ); } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::Types - type checking helper class =head1 VERSION version 2.031000 =head1 METHODS =head2 ArrayRef =head2 Bool =head2 HashRef =head2 Int =head2 Str =head2 Time =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 reverse-dependencies.t100644001750001750 156614520270707 22025 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/t/apiuse strict; use warnings; use Test::More; use Test::Fatal; use Ref::Util qw< is_hashref >; use lib '.'; use t::lib::Functions; my $mc = mcpan(); can_ok( $mc, qw< reverse_dependencies rev_deps > ); my $module = 'MetaCPAN::Client'; my $rs = $mc->reverse_dependencies($module); isa_ok( $rs, 'MetaCPAN::Client::ResultSet' ); my @revdeps; while ( my $release = $rs->next ) { is( ref $release, 'MetaCPAN::Client::Release', 'ResultSet->next with ' . $release->distribution . ' is ok', ), push @revdeps, $release->distribution; } ok( @revdeps > 2, 'revdep count for MetaCPAN::Client seems ok' ); foreach my $dep (@revdeps) { my $ok = eval { $mc->distribution($dep)->name; 1; }; is( $ok, 1, "$dep is a valid reverse dependency" ); } # Counting here would be fragile since it depends on dependency changes done_testing(); Author.pm100644001750001750 1441114520270707 21652 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::Author; # ABSTRACT: An Author data object $MetaCPAN::Client::Author::VERSION = '2.031000'; use Moo; use Ref::Util qw< is_arrayref >; with 'MetaCPAN::Client::Role::Entity'; my %known_fields = ( scalar => [qw< city country gravatar_url name ascii_name pauseid region updated user >], arrayref => [qw< donation email perlmongers profile website >], hashref => [qw< blog extra links release_count >], ); sub BUILDARGS { my ( $class, %args ) = @_; my $email = $args{'email'} || []; $args{'email'} = [ $email ] unless is_arrayref($email); return \%args; } my @known_fields = map { @{ $known_fields{$_} } } qw< scalar arrayref hashref >; foreach my $field ( @known_fields ) { has $field => ( is => 'ro', lazy => 1, default => sub { my $self = shift; return $self->data->{$field}; }, ); } sub _known_fields { \%known_fields } sub releases { my $self = shift; my $id = $self->pauseid; return $self->client->release({ all => [ { author => $id }, { status => 'latest' }, ] }); } sub dir { $_[0]->links->{cpan_directory} } sub metacpan_url { "https://metacpan.org/author/" . $_[0]->pauseid } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::Author - An Author data object =head1 VERSION version 2.031000 =head1 SYNOPSIS my $author = $mcpan->author('MICKEY'); =head1 DESCRIPTION a MetaCPAN author entity object. =head1 ATTRIBUTES =head2 pauseid The author's pause id, which is a string like C or C. =head2 name The author's full name, if they've provided this in their MetaCPAN profile. This may contain Unicode characters. =head2 ascii_name An ASCII-only version of the author's full name, if they've provided this in their MetaCPAN profile. =head2 city The author's city, if they've provided this in their MetaCPAN profile. =head2 region The author's region, if they've provided this in their MetaCPAN profile. =head2 country The author's country, if they've provided this in their MetaCPAN profile. =head2 updated An ISO8601 datetime string like C<2016-11-19T12:41:46> indicating when the author last updated their MetaCPAN profile. This is always provided in UTC. =head2 dir The author's CPAN directory, which is something like C. =head2 gravatar_url The author's gravatar.com user URL, if they have one. This URL is generated using PAUSEID@cpan.org. =head2 user The user's internal MetaCPAN id. =head2 donation This is an arrayref containing zero or more hashrefs. Each hashref contains two keys, C and C. The known names are currently C, C, and C. The id will be an appropriate id or URL for the thing in question. This may be empty if the author has not provided this information in their MetaCPAN profile. For example: [ { "name" => "paypal", "id" => "brian.d.foy@gmail.com" }, { "name" => "wishlist", "id" => "http://amzn.com/w/4O7IX9ZNQJR" }, ], =head2 email This is an arrayref containing zero or more email addresses that the author has added to their MetaCPAN profile. Note that this does I include the C email address that all CPAN authors have. =head2 website This is an arrayref of website URLs provided by the author in their MetaCPAN profile. =head2 profile This is an arrayref containing zero or more hashrefs. Each hashref contains two keys, C and C. The names are things like C or C. The id will be an appropriate id for the site in question. For example: [ { name => "amazon", id => "B002MRC39U" }, { name => "stackoverflow", id => "brian-d-foy" }, ] This may be empty if the author has not provided this information in their MetaCPAN profile. =head2 perlmongers This is an arrayref containing zero or more hashrefs. Each hashref contains two keys, C and C. The names are things like C. This may be empty if the author has not provided this information in their MetaCPAN profile. =head2 links This is a hashref where the keys are a link type, and the values are URLs. The currently known keys are: =over 4 =item * cpan_directory The author's CPAN directory. =item * backpan_directory The author's BackCPAN directory. =item * cpantesters_reports The author's CPAN Testers Reports page. =item * cpantesters_matrix The author's CPAN Testers matrix page. =item * cpants The author's CPANTS page. =item * metacpan_explorer A link to the MetaCPAN explorer site pre-populated with a request for the author's profile. =back =head2 blog This is an arrayref containing zer or more hashrefs. Each hashref contains two keys, C and C. For example: { url => "http://blogs.perl.org/users/brian_d_foy/", feed => "http://blogs.perl.org/users/brian_d_foy/atom.xml", } =head2 release_count This is a hashref containing counts for various types of releases. The known keys are: =over 4 =item * cpan The total number of distribution uplaods the author currently has on CPAN. =item * latest The total number of unique distributions the author currently has on CPAN. =item * backpan-only The number of distribution uploads currently only available via BackPAN. =back =head2 extra Returns a hashref. The contents of this are entirely arbitrary and will vary by author. =head1 METHODS =head2 BUILDARGS Ensures format of the input. =head2 releases my $releases = $author->releases(); This method returns a L of L objects. It includes all of the author's releases with the C status. =head2 metacpan_url Returns a link to the author's page on MetaCPAN. =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 Mirror.pm100644001750001750 421214520270707 21640 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::Mirror; # ABSTRACT: A Mirror data object $MetaCPAN::Client::Mirror::VERSION = '2.031000'; use Moo; use Carp; with 'MetaCPAN::Client::Role::Entity'; my %known_fields = ( scalar => [qw< aka_name A_or_CNAME ccode city continent country dnsrr freq ftp http inceptdate name note org region reitredate rsync src tz >], arrayref => [qw< contact location >], hashref => [qw<>], ); my @known_fields = map { @{ $known_fields{$_} } } qw< scalar arrayref hashref >; foreach my $field (@known_fields) { has $field => ( is => 'ro', lazy => 1, default => sub { my $self = shift; return $self->data->{$field}; }, ); } sub _known_fields { return \%known_fields } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::Mirror - A Mirror data object =head1 VERSION version 2.031000 =head1 SYNOPSIS my $mirror = $mcpan->mirror('eutelia.it'); =head1 DESCRIPTION A MetaCPAN mirror entity object. =head1 ATTRIBUTES =head2 name The name of the mirror, which is what you passed =head2 org The organization that maintains the mirror. =head2 ftp An FTP url for the mirror. =head2 rsync An rsync url for the mirror. =head2 src =head2 city The city where the mirror is located. =head2 country The name of the country where the mirror is located. =head2 ccode The ISO country code for the mirror's country. =head2 aka_name =head2 tz =head2 note =head2 dnsrr =head2 region =head2 inceptdate =head2 freq =head2 continent =head2 http =head2 reitredate =head2 A_or_CNAME =head2 contact Array-Ref. =head2 location Array-Ref. =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 Module.pm100644001750001750 275614520270707 21626 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::Module; # ABSTRACT: A Module data object $MetaCPAN::Client::Module::VERSION = '2.031000'; use Moo; extends 'MetaCPAN::Client::File'; sub metacpan_url { my $self = shift; sprintf("https://metacpan.org/pod/release/%s/%s/%s", $self->author, $self->release, $self->path ); } sub package { my $self = shift; return $self->client->package( $self->documentation ); } sub permission { my $self = shift; return $self->client->permission( $self->documentation ); } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::Module - A Module data object =head1 VERSION version 2.031000 =head1 SYNOPSIS my $module = MetaCPAN::Client->new->module('Moo'); =head1 DESCRIPTION A MetaCPAN module entity object. This is currently the exact same as L. =head1 ATTRIBUTES Whatever L has. =head1 METHODS =head2 metacpan_url Returns a link to the module page on MetaCPAN. =head2 package Returns an L object for the module. =head2 permission Returns an L object for the module. =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 Rating.pm100644001750001750 316214520270707 21615 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::Rating; # ABSTRACT: A Rating data object $MetaCPAN::Client::Rating::VERSION = '2.031000'; use Moo; with 'MetaCPAN::Client::Role::Entity'; my %known_fields = ( scalar => [qw< author date details distribution helpful rating release user >], arrayref => [], hashref => [], ); my @known_fields = map { @{ $known_fields{$_} } } qw< scalar arrayref hashref >; foreach my $field (@known_fields) { has $field => ( is => 'ro', lazy => 1, default => sub { my $self = shift; return $self->data->{$field}; }, ); } sub _known_fields { return \%known_fields } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::Rating - A Rating data object =head1 VERSION version 2.031000 =head1 SYNOPSIS my $ratings = $mcpan->rating({ distribution => "Moo" }); while ( my $rat = $ratings->next ) { ... } =head1 DESCRIPTION A MetaCPAN rating entity object. =head1 ATTRIBUTES =head2 date An ISO8601 datetime string like C<2016-11-19T12:41:46> indicating when the rating was created. =head2 release =head2 author =head2 details =head2 rating =head2 distribution =head2 helpful =head2 user =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 Scroll.pm100644001750001750 1006414520270707 21646 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::Scroll; # ABSTRACT: A MetaCPAN::Client scroller $MetaCPAN::Client::Scroll::VERSION = '2.031000'; use Moo; use Carp; use Ref::Util qw< is_hashref >; use JSON::MaybeXS qw< decode_json encode_json >; use MetaCPAN::Client::Types qw< Str Int Time ArrayRef HashRef Bool >; has ua => ( is => 'ro', required => 1, ); has size => ( is => 'ro', isa => Str, ); has time => ( is => 'ro', isa => Time, ); has base_url => ( is => 'ro', isa => Str, required => 1, ); has type => ( is => 'ro', isa => Str, required => 1, ); has body => ( is => 'ro', isa => HashRef, required => 1, ); has _id => ( is => 'ro', isa => Str, ); has _buffer => ( is => 'ro', isa => ArrayRef, default => sub { [] }, ); has _done => ( is => 'rw', isa => Bool, default => sub { 0 }, ); has total => ( is => 'ro', isa => Int, ); has aggregations => ( is => 'ro', isa => HashRef, default => sub { +{} }, ); sub BUILDARGS { my ( $class, %args ) = @_; $args{time} //= '5m'; $args{size} //= '100'; my ( $ua, $base_url, $type, $body, $time, $size ) = @args{qw< ua base_url type body time size >}; # fetch scroller from server my $res = $ua->post( sprintf( '%s/%s/_search?scroll=%s&size=%s', $base_url, $type, $time, $size ), { content => encode_json $body } ); if ( $res->{status} != 200 ) { my $msg = "failed to create a scrolled search"; $args{debug} and $msg .= "\n(" . $res->{content} . ")"; croak $msg; } my $content = decode_json $res->{content}; # read response content --> object params $args{_id} = $content->{_scroll_id}; $args{total} = $content->{hits}{total}; $args{_buffer} = $content->{hits}{hits}; $args{aggregations} = $content->{aggregations} if $content->{aggregations} and is_hashref( $content->{aggregations} ); return \%args; } sub next { my $self = shift; my $buffer = $self->_buffer; # We're exhausted and will do no more. return if $self->_done; # Refill the buffer if it's empty. @$buffer = @{ $self->_fetch_next } unless @$buffer; # Grab the next result from the buffer. If there's no result, then that's # all, folks! my $next = shift @$buffer; $self->_done(1) unless $next; return $next; } sub _fetch_next { my $self = shift; my $res = $self->ua->post( sprintf( '%s/_search/scroll?scroll=%s&size=%s', $self->base_url, $self->time, $self->size ), { content => $self->_id } ); croak "failed to fetch next scrolled batch" unless $res->{status} == 200; my $content = decode_json $res->{content}; return $content->{hits}{hits}; } sub DEMOLISH { my $self = shift; $self->ua->delete( sprintf( '%s/_search/scroll?scroll=%s', $self->base_url, $self->time ), { content => $self->_id } ); } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::Scroll - A MetaCPAN::Client scroller =head1 VERSION version 2.031000 =head1 METHODS =head2 next get next matched document. =head2 BUILDARGS =head2 DEMOLISH =head1 ATTRIBUTES =head2 aggregations The returned aggregations structure from agg requests. =head2 base_url The base URL for sending server requests. =head2 body The request body. =head2 size The number of docs to pull from each shard per request. =head2 time The lifetime of the scroller on the server. =head2 total The total number of matches. =head2 type The Elasticsearch type to query. =head2 ua The user agent object for running requests. =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 complex-either-and.pl100644001750001750 64314520270707 22340 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/complex-either-and.pl use strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $authors = MetaCPAN::Client->new->author({ either => [ { name => 'Dave *' }, { name => 'David *' }, ], all => { email => '*gmail.com' }, }); my %output = ( TOTAL => $authors->total, NAMES => [ map { $authors->next->name } 0 .. 9 ], ); p %output; complex-either-not.pl100644001750001750 64614520270707 22401 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/complex-either-not.pl use strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $authors = MetaCPAN::Client->new->author({ either => [ { name => 'Dave *' }, { name => 'David *' }, ], not => [ { name => 'Dave C*' }, { name => 'David M*' }, ] }); print "\n"; my %out = ( TOTAL => $authors->total ); p %out; rev_deps-recursive.pl100644001750001750 130714520270707 22505 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/rev_deps-recursive.pl use strict; use warnings; use Term::ANSIColor; use MetaCPAN::Client; $|=1; my $dist = shift || 'Hijk'; my $mcpan = MetaCPAN::Client->new; print "\n\n", colored( "* $dist", 'green' ), "\n"; dig( $dist, 0 ); sub dig { my $dist = shift; my $level = shift; my $res = $mcpan->reverse_dependencies($dist); while ( my $item = $res->next ) { if ( $level ) { printf "%s%s\n", colored( '....' x $level, 'yellow' ), $item->distribution; } else { printf "\n>> %s\n", colored( $item->distribution, 'blue' ); } dig( $item->distribution, $level + 1 ); } } Package.pm100644001750001750 331414520270707 21723 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::Package; # ABSTRACT: A package data object (02packages.details entry) $MetaCPAN::Client::Package::VERSION = '2.031000'; use Moo; with 'MetaCPAN::Client::Role::Entity'; my %known_fields = ( scalar => [qw< author distribution dist_version file module_name version >], arrayref => [qw<>], hashref => [], ); my @known_fields = map { @{ $known_fields{$_} } } qw< scalar arrayref hashref >; foreach my $field (@known_fields) { has $field => ( is => 'ro', lazy => 1, default => sub { my $self = shift; return $self->data->{$field}; }, ); } sub _known_fields { return \%known_fields } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::Package - A package data object (02packages.details entry) =head1 VERSION version 2.031000 =head1 SYNOPSIS my $package = $mcpan->package('MooseX::Types'); =head1 DESCRIPTION A MetaCPAN package (02packages.details) entity object. =head1 ATTRIBUTES =head2 module_name Returns the name of the module. =head2 file The file path in CPAN for the module (latest release) =head2 distribution The distribution in which the module exist =head2 version The (latest) version of the module =head2 dist_version The (latest) version of the distribution =head2 author The pauseid of the release author =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 Release.pm100644001750001750 1313514520270707 21772 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::Release; # ABSTRACT: A Release data object $MetaCPAN::Client::Release::VERSION = '2.031000'; use Moo; use Ref::Util qw< is_hashref >; use JSON::MaybeXS qw< decode_json >; with 'MetaCPAN::Client::Role::Entity', 'MetaCPAN::Client::Role::HasUA'; my %known_fields = ( scalar => [qw< abstract archive author authorized checksum_md5 checksum_sha256 date deprecated distribution download_url first id maturity main_module name status version version_numified >], arrayref => [qw< dependency license provides >], hashref => [qw< metadata resources stat tests >], ); my @known_fields = map { @{ $known_fields{$_} } } qw< scalar arrayref hashref >; foreach my $field (@known_fields) { has $field => ( is => 'ro', lazy => 1, default => sub { my $self = shift; return $self->data->{$field}; }, ); } sub _known_fields { return \%known_fields } sub changes { my $self = shift; my $url = sprintf "https://fastapi.metacpan.org/changes/%s/%s", $self->author, $self->name; my $res = $self->ua->get($url); return unless is_hashref($res); my $content = decode_json $res->{'content'}; return $content->{'content'}; } sub metacpan_url { my $self = shift; sprintf( "https://metacpan.org/release/%s/%s", $self->author, $self->name ) } sub contributors { my $self = shift; my $url = sprintf( "https://fastapi.metacpan.org/release/contributors/%s/%s", $self->author, $self->name ); my $res = $self->ua->get($url); return unless is_hashref($res); my $content = decode_json $res->{'content'}; return $content->{'contributors'}; } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::Release - A Release data object =head1 VERSION version 2.031000 =head1 SYNOPSIS my $release = $mcpan->release('Moose'); =head1 DESCRIPTION A MetaCPAN release entity object. =head1 ATTRIBUTES =head2 status The release's status, C, C, or C. =head2 name The release's name, something like C. =head2 date An ISO8601 datetime string like C<2016-11-19T12:41:46> indicating when the release was uploaded. =head2 author The PAUSE ID of the author who uploaded the release. =head2 maturity This will be either C or C. =head2 main_module The release's main module name. =head2 id The release's internal MetaCPAN id. =head2 authorized A boolean indicating whether or not this was an authorized release. =head2 download_url A URL for this release's distribution archive file. =head2 checksum_sha256 The sha256 hexdigest for this release's distribution archive file. =head2 checksum_md5 The md5 hexdigest for this release's distribution archive file. =head2 first A boolean indicating whether or not this is the first release of this distribution. =head2 archive The filename of the archive file for this release. =head2 version The release's version. =head2 version_numified The numified form of the release's version. =head2 deprecated The deprecated field value for this release. =head2 distribution The name of the distribution to which this release belongs. Something like C =head2 abstract The abstract from this release's metadata. =head2 dependency This is an arrayref of hashrefs. Each hashref contains the following keys: =over 4 =item * phase The phase to which this dependency belongs. This will be one of C, C, C, C, or C. =item * relationship This will be one of C, C, or C. =item * module The name of the module which is depended on. =item * version The required version of the dependency. This may be C<0>, indicating that any version is acceptable. =back =head2 license An arrayref containing the license(s) under which this release has been made available. These licenses are represented by strings like C or C. =head2 provides This an arrayref containing a list of all the modules provided by this distribution. =head2 metadata This is a hashref containing metadata provided by the distribution. The exact contents of this hashref will vary across CPAN, but should largely conform to the spec defined by L. =head2 resources The resources portion of the release's metadata, returned as a hashref. =head2 stat A hashref containing C all information about the release's archive file. The keys are: =over 4 =item * mtime The Unix epoch of the file's last modified time. =item * mode The file's mode (as an integer, not an octal representation). =item * size The file's size in bytes. =back =head2 tests Returns a hashref of information about CPAN testers results for this release. The keys are C, C, C, and C. The values are the count of that particular result on CPAN Testers for this release. =head1 METHODS =head2 changes Returns the Changes text for the release. =head2 metacpan_url Returns a link to the release page on MetaCPAN. =head2 contributors Returns a structure with release contributors info. =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 Request.pm100644001750001750 1666214520270707 22052 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::Request; # ABSTRACT: Object used for making requests to MetaCPAN $MetaCPAN::Client::Request::VERSION = '2.031000'; use Moo; use Carp; use JSON::MaybeXS qw; use Ref::Util qw< is_arrayref is_hashref is_ref >; use MetaCPAN::Client::Scroll; use MetaCPAN::Client::Types qw< HashRef Int >; with 'MetaCPAN::Client::Role::HasUA'; has _clientinfo => ( is => 'ro', isa => HashRef, lazy => 1, builder => '_build_clientinfo', ); has domain => ( is => 'ro', default => sub { $ENV{METACPAN_DOMAIN} and return $ENV{METACPAN_DOMAIN}; $_[0]->_clientinfo->{production}{domain}; }, ); has base_url => ( is => 'ro', lazy => 1, default => sub { $ENV{METACPAN_DOMAIN} and return $ENV{METACPAN_DOMAIN}; $_[0]->_clientinfo->{production}{url}; }, ); has _is_agg => ( is => 'ro', default => 0, writer => '_set_is_agg' ); has debug => ( is => 'ro', isa => Int, default => 0, ); sub BUILDARGS { my ( $self, %args ) = @_; $args{domain} and $args{base_url} = $args{domain}; return \%args; } sub _build_clientinfo { my $self = shift; my $info; eval { $info = $self->ua->get( 'https://clientinfo.metacpan.org' ); $info = decode_json( $info->{content} ); is_hashref($info) and exists $info->{production} or die; 1; } or $info = +{ production => { url => 'https://fastapi.metacpan.org', # last known production url domain => 'https://fastapi.metacpan.org', # last known production domain } }; return $info; } sub fetch { my $self = shift; my $url = shift or croak 'fetch must be called with a URL parameter'; my $params = shift || {}; $url =~ s{^/}{}; my $req_url = sprintf '%s/%s', $self->base_url, $url; my $ua = $self->ua; my $result = keys %{$params} ? $ua->post( $req_url, { content => encode_json $params } ) : $ua->get($req_url); return $self->_decode_result( $result, $req_url ); } sub ssearch { my $self = shift; my $type = shift; my $args = shift; my $params = shift; my $time = delete $params->{'scroller_time'} || '5m'; my $size = delete $params->{'scroller_size'} || 1000; my $scroller = MetaCPAN::Client::Scroll->new( ua => $self->ua, size => $size, time => $time, base_url => $self->base_url, type => $type, body => $self->_build_body($args, $params), debug => $self->debug, ); return $scroller; } sub _decode_result { my $self = shift; my $result = shift; my $url = shift or croak 'Second argument of a URL must be provided'; is_hashref($result) or croak 'First argument must be hashref'; my $success = $result->{'success'}; defined $success or croak 'Missing success in return value'; $success or croak "Failed to fetch '$url': " . $result->{'reason'}; my $content = $result->{'content'} or croak 'Missing content in return value'; $url =~ m|/pod/| and return $content; $url =~ m|/source/| and return $content; my $decoded_result; eval { $decoded_result = decode_json $content; 1; } or do { croak "Couldn't decode '$content': $@"; }; return $decoded_result; } sub _build_body { my $self = shift; my $args = shift; my $params = shift; my $query = $args->{__MATCH_ALL__} ? { match_all => {} } : _build_query_rec($args); return +{ query => $query, $self->_read_filters($params), $self->_read_fields($params), $self->_read_aggregations($params), $self->_read_sort($params) }; } my %key2es = ( all => 'must', either => 'should', not => 'must_not', ); sub _read_fields { my $self = shift; my $params = shift; my $fields = delete $params->{fields}; my $_source = delete $params->{_source}; my @ret; if ( $fields ) { is_arrayref($fields) or croak "fields must be an arrayref"; push @ret => ( fields => $fields ); } if ( $_source ) { is_arrayref($_source) or !is_ref($_source) or croak "_source must be an arrayref or a string"; push @ret => ( _source => $_source ); } return @ret; } sub _read_aggregations { my $self = shift; my $params = shift; my $aggregations = delete $params->{aggregations}; is_ref($aggregations) or return (); $self->_set_is_agg(1); return ( aggregations => $aggregations ); } sub _read_filters { my $self = shift; my $params = shift; my $filter = delete $params->{es_filter}; is_ref($filter) or return (); return ( filter => $filter ); } sub _read_sort { my $self = shift; my $params = shift; my $sort = delete $params->{sort}; is_ref($sort) or return (); return ( sort => $sort ); } sub _build_query_rec { my $args = shift; is_hashref($args) or croak 'query args must be a hash'; my %query = (); my $basic_element = 1; KEY: for my $k ( qw/ all either not / ) { my $v = delete $args->{$k} || next KEY; is_hashref($v) and $v = [ $v ]; is_arrayref($v) or croak "invalid value for key $k"; undef $basic_element; $query{'bool'}{ $key2es{$k} } = [ map +( _build_query_rec($_) ), @$v ]; $k eq 'either' and $query{'bool'}{'minimum_should_match'} = 1; } $basic_element and %query = %{ _build_query_element($args) }; return \%query; } sub _build_query_element { my $args = shift; scalar keys %{$args} == 1 or croak 'Wrong number of keys in query element'; my ($key) = keys %{$args}; my $val = $args->{$key}; !is_ref($val) and $val =~ /[\w\*]/ or croak 'Wrong type of query arguments'; my $wildcard = $val =~ /[*?]/; my $qtype = $wildcard ? 'wildcard' : 'term'; return +{ $qtype => $args }; } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::Request - Object used for making requests to MetaCPAN =head1 VERSION version 2.031000 =head1 ATTRIBUTES =head2 domain $mcpan = MetaCPAN::Client->new( domain => 'localhost' ); What domain to use for all requests. Default: B. =head2 base_url my $mcpan = MetaCPAN::Client->new( base_url => 'https://localhost:9999/v2', ); Instead of overriding the C, you should override the C. The C will be set appropriately automatically. Default: I. =head2 debug debug-mode for more detailed error messages. =head1 METHODS =head2 BUILDARGS =head2 fetch my $result = $mcpan->fetch('/release/Moose'); # with parameters my $more = $mcpan->fetch( '/release/Moose', { param => 'value' }, ); Fetches a path from MetaCPAN (post or get), and returns the decoded result. =head2 ssearch Calls an Elasticsearch query and returns an L scroller object. =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 Favorite.pm100644001750001750 370514520270707 22153 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::Favorite; # ABSTRACT: A Favorite data object $MetaCPAN::Client::Favorite::VERSION = '2.031000'; use Moo; with 'MetaCPAN::Client::Role::Entity'; my %known_fields = ( scalar => [qw< date user release id author distribution >], arrayref => [], hashref => [] ); my @known_fields = map { @{ $known_fields{$_} } } qw< scalar arrayref hashref >; foreach my $field (@known_fields) { has $field => ( is => 'ro', lazy => 1, default => sub { my $self = shift; return $self->data->{$field}; }, ); } sub _known_fields { return \%known_fields } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::Favorite - A Favorite data object =head1 VERSION version 2.031000 =head1 SYNOPSIS # Query favorites for a given distribution: my $favorites = $mcpan->favorite( { distribution => 'Moose' } ); # Total number of matches ("how many favorites does the dist have?"): print $favorites->total; # Iterate over the favorite matches while ( my $fav = $favorites->next ) { ... } =head1 DESCRIPTION A MetaCPAN favorite entity object. =head1 ATTRIBUTES =head2 date An ISO8601 datetime string like C<2016-11-19T12:41:46> indicating when the favorite was created. =head2 user The user ID (B PAUSE ID) of the person who favorited the thing in question. =head2 release The release that was favorited. =head2 id The favorite ID. =head2 author The PAUSE ID of the author whose release was favorited. =head2 distribution The distribution that was favorited. =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 autocomplete_suggest.pl100644001750001750 27614520270707 23117 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # examples/autocomplete.pl use strict; use warnings; use Data::Printer; use MetaCPAN::Client; my $mcpan = MetaCPAN::Client->new(); my $ac = $mcpan->autocomplete_suggest("Moos"); p $ac; ResultSet.pm100644001750001750 740714520270707 22331 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::ResultSet; # ABSTRACT: A Result Set $MetaCPAN::Client::ResultSet::VERSION = '2.031000'; use Moo; use Carp; use MetaCPAN::Client::Types qw< ArrayRef >; has type => ( is => 'ro', isa => sub { croak 'Invalid type' unless grep { $_ eq $_[0] } qw; }, lazy => 1, ); # in case we're returning from a scrolled search has scroller => ( is => 'ro', isa => sub { use Safe::Isa; $_[0]->$_isa('MetaCPAN::Client::Scroll') or croak 'scroller must be an MetaCPAN::Client::Scroll object'; }, predicate => 'has_scroller', ); # in case we're returning from a fetch has items => ( is => 'ro', isa => ArrayRef, ); has total => ( is => 'ro', default => sub { my $self = shift; return $self->has_scroller ? $self->scroller->total : scalar @{ $self->items }; }, ); has 'class' => ( is => 'ro', lazy => 1, builder => '_build_class', ); sub BUILDARGS { my ( $class, %args ) = @_; 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'; exists $args{type} or exists $args{class} or croak 'Must pass either type or target class to ResultSet'; exists $args{type} and exists $args{class} and croak 'Must pass either type or target class to ResultSet, not both'; return \%args; } sub BUILD { my ( $self ) = @_; $self->class; # vifify and validate } sub next { my $self = shift; my $result = $self->has_scroller ? $self->scroller->next : shift @{ $self->items }; defined $result or return; return $self->class->new_from_request( $result->{'_source'} || $result->{'fields'} || $result ); } sub aggregations { my $self = shift; return $self->has_scroller ? $self->scroller->aggregations : {}; } sub _build_class { my $self = shift; return 'MetaCPAN::Client::' . ucfirst $self->type; } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::ResultSet - A Result Set =head1 VERSION version 2.031000 =head1 DESCRIPTION Object representing a result from Elastic Search. This is used for the complex (as in L) queries to MetaCPAN. It provides easy access to the scroller and aggregations. =head1 ATTRIBUTES =head2 scroller An L object. =head2 items An arrayref of items to manually scroll over, instead of a scroller object. =head2 type The entity of the result set. Available types: =over 4 =item * author =item * distribution =item * module =item * release =item * favorite =item * file =back =head2 aggregations The aggregations available in the Elastic Search response. =head1 METHODS =head2 next Iterator call to fetch the next result set object. =head2 total 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. =head2 BUILD Validates the object. You should never run this yourself. =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 Permission.pm100644001750001750 273414520270707 22525 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::Permission; # ABSTRACT: A Permission data object $MetaCPAN::Client::Permission::VERSION = '2.031000'; use Moo; with 'MetaCPAN::Client::Role::Entity'; my %known_fields = ( scalar => [qw< module_name owner >], arrayref => [qw< co_maintainers >], hashref => [], ); my @known_fields = map { @{ $known_fields{$_} } } qw< scalar arrayref hashref >; foreach my $field (@known_fields) { has $field => ( is => 'ro', lazy => 1, default => sub { my $self = shift; return $self->data->{$field}; }, ); } sub _known_fields { return \%known_fields } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::Permission - A Permission data object =head1 VERSION version 2.031000 =head1 SYNOPSIS my $permission = $mcpan->permission('MooseX::Types'); =head1 DESCRIPTION A MetaCPAN permission entity object. =head1 ATTRIBUTES =head2 module_name Returns the name of the module. =head2 owner The module owner (first-come permissions). =head2 co_maintainers Other maintainers with permissions to this module. =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 Role000755001750001750 014520270707 20572 5ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/ClientHasUA.pm100644001750001750 453714520270707 22242 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Client/Roleuse strict; use warnings; package MetaCPAN::Client::Role::HasUA; # ABSTRACT: Role for supporting user-agent attribute $MetaCPAN::Client::Role::HasUA::VERSION = '2.031000'; use Moo::Role; use Carp; use HTTP::Tiny; has _user_ua => ( init_arg => 'ua', is => 'ro', predicate => '_has_user_ua', ); has ua => ( init_arg => undef, is => 'ro', lazy => 1, builder => '_build_ua', ); has ua_args => ( is => 'ro', default => sub { [ agent => 'MetaCPAN::Client/'.($MetaCPAN::Client::VERSION||'xx'), verify_SSL => 1 ] }, ); sub _build_ua { my $self = shift; # This level of indirection is so that if a user has not specified a custom UA # MetaCPAN::Client will have its own UA's # # But if the user **has** specified a custom UA, that UA is used for both. if ( $self->_has_user_ua ) { my $ua = $self->_user_ua; croak "cannot use given ua (must support 'get' and 'post' methods)" unless $ua->can("get") and $ua->can("post"); return $self->_user_ua; } return HTTP::Tiny->new( @{ $self->ua_args } ); } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::Role::HasUA - Role for supporting user-agent attribute =head1 VERSION version 2.031000 =head1 ATTRIBUTES =head2 ua my $mcpan = MetaCPAN::Client->new( ua => HTTP::Tiny->new(...) ); The user agent object for running requests. It must provide an interface that matches L. Explicitly: =over 4 =item * Implement post() Method C must be available that accepts a request URL and a hashref of options. =item * Implement get() Method C must be available that accepts a request URL. =item * Return result hashref Must return a result hashref which has key C and key C. =back Default: L, =head2 ua_args my $mcpan = MetaCPAN::Client->new( ua_args => [ agent => 'MyAgent' ], ); Arguments sent to the user agent. Default: user agent string: B. =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 DownloadURL.pm100644001750001750 326214520270707 22524 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::DownloadURL; # ABSTRACT: A Download URL data object $MetaCPAN::Client::DownloadURL::VERSION = '2.031000'; use Moo; with 'MetaCPAN::Client::Role::Entity'; my %known_fields = ( scalar => [qw< checksum_md5 checksum_sha256 date download_url status version >], arrayref => [], hashref => [], ); my @known_fields = map { @{ $known_fields{$_} } } qw< scalar arrayref hashref >; foreach my $field (@known_fields) { has $field => ( is => 'ro', lazy => 1, default => sub { my $self = shift; return $self->data->{$field}; }, ); } sub _known_fields { return \%known_fields } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::DownloadURL - A Download URL data object =head1 VERSION version 2.031000 =head1 SYNOPSIS my $download_url = $mcpan->download_url('Moose'); =head1 DESCRIPTION A MetaCPAN download_url entity object. =head1 ATTRIBUTES =head2 checksum_sha256 The sha256 hexdigest for the file. =head2 checksum_md5 The md5 hexdigest for the file. =head2 date Returns the date of the release that this URL refers to. =head2 download_url The actual download URL. =head2 status The release status, which is something like C or C =head2 version The version number for the distribution. =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 Entity.pm100644001750001750 600714520270707 22547 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Client/Roleuse strict; use warnings; package MetaCPAN::Client::Role::Entity; # ABSTRACT: A role for MetaCPAN entities $MetaCPAN::Client::Role::Entity::VERSION = '2.031000'; use Moo::Role; use JSON::PP; use Ref::Util qw< is_ref is_arrayref is_hashref >; has data => ( is => 'ro', required => 1, ); has client => ( is => 'ro', lazy => 1, builder => '_build_client', ); sub _build_client { require MetaCPAN::Client; return MetaCPAN::Client->new(); } sub BUILDARGS { my ( $class, %args ) = @_; my $known_fields = $class->_known_fields; for my $k ( @{ $known_fields->{scalar} } ) { $args{data}{$k} = $args{data}{$k}->[0] if is_arrayref( $args{data}{$k} ) and @{$args{data}{$k}} == 1; if ( JSON::PP::is_bool($args{data}{$k}) ) { $args{data}{$k} = !!$args{data}{$k}; } elsif ( is_ref( $args{data}{$k} ) ) { delete $args{data}{$k}; } } for my $k ( @{ $known_fields->{arrayref} } ) { # fix the case when we expect an array ref but get a scalar because # the result array had one element and we received a scalar if ( defined($args{data}{$k}) and !is_ref($args{data}{$k}) ) { $args{data}{$k} = [ $args{data}{$k} ] } delete $args{data}{$k} unless is_arrayref( $args{data}{$k} ); # warn? } for my $k ( @{ $known_fields->{hashref} } ) { delete $args{data}{$k} unless is_hashref( $args{data}{$k} ); # warn? } return \%args; } sub new_from_request { my ( $class, $request, $client ) = @_; my $known_fields = $class->_known_fields; return $class->new( ( defined $client ? ( client => $client ) : () ), data => { map +( defined $request->{$_} ? ( $_ => $request->{$_} ) : () ), map +( @{ $known_fields->{$_} } ), qw< scalar arrayref hashref > } ); } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::Role::Entity - A role for MetaCPAN entities =head1 VERSION version 2.031000 =head1 DESCRIPTION This is a role to be consumed by all L entities. It provides common attributes and methods. =head1 ATTRIBUTES =head2 data Hash reference containing all the entity data. Entities are usually generated using C which sets the C attribute appropriately by picking the relevant information. Required. =head1 METHODS =head2 new_from_request Create a new entity object using a request hash. The hash represents the information returned from a MetaCPAN request. This also sets the data attribute. =head2 BUILDARGS Perform type checks & conversion for the incoming data. =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 Distribution.pm100644001750001750 605714520270707 23056 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/lib/MetaCPAN/Clientuse strict; use warnings; package MetaCPAN::Client::Distribution; # ABSTRACT: A Distribution data object $MetaCPAN::Client::Distribution::VERSION = '2.031000'; use Moo; with 'MetaCPAN::Client::Role::Entity'; my %known_fields = ( scalar => [qw< name >], arrayref => [], hashref => [qw< bugs river >] ); my %__known_fields_ex = ( map { my $k = $_; $k => +{ map { $_ => 1 } @{ $known_fields{$k} } } } keys %known_fields ); my @known_fields = map { @{ $known_fields{$_} } } keys %known_fields; foreach my $field ( @known_fields ) { has $field => ( is => 'ro', lazy => 1, default => sub { my $self = shift; return ( exists $self->data->{$field} ? $self->data->{$field} : exists $__known_fields_ex{hashref}{$field} ? {} : exists $__known_fields_ex{arrayref}{$field} ? [] : exists $__known_fields_ex{scalar}{$field} ? '' : undef ); }, ); } sub _known_fields { return \%known_fields } sub rt { $_[0]->bugs->{rt} || {} } sub github { $_[0]->bugs->{github} || {} } sub metacpan_url { "https://metacpan.org/release/" . $_[0]->name } 1; __END__ =pod =encoding UTF-8 =head1 NAME MetaCPAN::Client::Distribution - A Distribution data object =head1 VERSION version 2.031000 =head1 SYNOPSIS my $dist = $mcpan->distribution('MetaCPAN-Client'); =head1 DESCRIPTION A MetaCPAN distribution entity object. =head1 ATTRIBUTES =head2 name The distribution's name. =head2 bugs A hashref containing information about bugs reported in various issue trackers. The top-level keys are issue tracker names like C or C. Each value is itself a hashref containing information about the bugs in that tracker. The keys vary between trackers, but this will always contain a C key, which is a URL for the tracker. There may also be keys containing counts such as C, C, etc. =head2 river A hashref containing L<"CPAN River"|http://neilb.org/2015/04/20/river-of-cpan.html> information about the distro. The hashref contains the following keys: =over 4 =item * bucket A positive or zero integer. The higher the number the farther upstream this distribution is. =item * immediate The number of distributions that directly depend on this one. =item * total The number of distributions that depend on this one, directly or indirectly. =back =head1 METHODS =head2 rt Returns the hashref of data for the rt bug tracker. This defaults to an empty hashref. =head2 github Returns the hashref of data for the github bug tracker. This defaults to an empty hashref. =head2 metacpan_url Returns a link to the distribution page on MetaCPAN. =head1 AUTHORS =over 4 =item * Sawyer X =item * Mickey Nasriachi =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2016 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 complex-nested-either-and.pl100644001750001750 102714520270707 23635 0ustar00mickeymickey000000000000MetaCPAN-Client-2.031000/examples # 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;