Parse-CPAN-Packages-2.40/0000755000175100010010000000000012465247250011661 5ustar Parse-CPAN-Packages-2.40/t/0000755000175100010010000000000012465247244012127 5ustar Parse-CPAN-Packages-2.40/t/mirror/0000755000175100010010000000000012465247244013441 5ustar Parse-CPAN-Packages-2.40/t/mirror/modules/0000755000175100010010000000000012465247244015111 5ustar Parse-CPAN-Packages-2.40/t/mirror/modules/02packages.details.txt.gz0000755000175100000000000000101011505635062022523 0ustar root >UA02packages.details.txt.fooMs0zĖ@3P˜xMm1\H@BgR͞lv1Q52xY4p&WM/Ueg0BmAS'B7dѲђ35Za)zm-oJwՂT >L޹6(IV6=z/u4me<цA9pVj{Aj5:KoF9OWOᴩdRY_!4}H/X6dt!*8.$tܺQ= )!?,K]A3JJ R\*oGyyMB,ާp?q$pMcͻф^$|ݕӯۧ Or_+a2iKW9?XDh,=:cOdyŸ< .ԃ _U9.k*XىŘXsɝV?VͭUgRe}OParse-CPAN-Packages-2.40/t/mirror/authors/0000755000175100010010000000000012465247244015126 5ustar Parse-CPAN-Packages-2.40/t/mirror/authors/id/0000755000175100010010000000000012465247244015522 5ustar Parse-CPAN-Packages-2.40/t/mirror/authors/id/K/0000755000175100010010000000000012465247244015714 5ustar Parse-CPAN-Packages-2.40/t/mirror/authors/id/K/KA/0000755000175100010010000000000012465247244016207 5ustar Parse-CPAN-Packages-2.40/t/mirror/authors/id/K/KA/KANE/0000755000175100010010000000000012465247244016725 5ustar Parse-CPAN-Packages-2.40/t/mirror/authors/id/K/KA/KANE/Acme-Comment-1.02.tar.gz0000755000175100000000000000032511627150347023600 0ustar root\N 0a/6w,vVMo+,Cv 'ʃ-R,98^qzw2)mvZR QdI?Jz]fyfV}30,RZKC9 vg?1zTo E?` iUj(Parse-CPAN-Packages-2.40/t/simple.t0000755000175100010010000001145512057127254013611 0ustar #!/usr/bin/perl use strict; use Test::InDistDir; use Test::More; use File::Slurp 'read_file'; run(); done_testing; sub run { use_ok( "Parse::CPAN::Packages" ); default_features(); my $raw_data = read_file( "t/02packages.details.txt" ); my $gz_data = read_file( "t/02packages.details.txt.gz", binmode => ':raw' ); creation_check( "t/02packages.details.txt.gz", "gzip file is parsed" ); creation_check( $raw_data, "text contents are parsed" ); creation_check( $gz_data, "gzip contents are parsed" ); dist_contents( "t/mirror/modules/02packages.details.txt.gz", "mirror file with implicit mirror directory" ); dist_contents( filename => "t/02packages.details.txt", mirror_dir => "t/mirror", "mirror file with explicit mirror dir" ); return; } sub default_features { my ( $p, @packages ) = creation_check( "t/02packages.details.txt", "text file is parsed" ); is( $p->file, '02packages.details.txt', 'file' ); is( $p->url, 'http://www.perl.com/CPAN/modules/02packages.details.txt', 'url' ); is( $p->description, 'Package names found in directory $CPAN/authors/id/', 'description' ); is( $p->columns, 'package name, version, path', 'columns' ); is( $p->intended_for, 'Automated fetch routines, namespace documentation.', 'intended for' ); is( $p->written_by, 'Id: mldistwatch 479 2004-01-04 13:29:05Z k ', 'written by' ); is( $p->line_count, 23609, 'line count' ); is( $p->last_updated, 'Fri, 13 Feb 2004 13:50:21 GMT', 'last updated' ); my $m = $p->package( "Acme::Colour" ); is( $m->package, "Acme::Colour" ); is( $m->version, "1.00" ); my $d = $m->distribution; is( $d->prefix, "L/LB/LBROCARD/Acme-Colour-1.00.tar.gz" ); is( $d->dist, "Acme-Colour" ); is( $d->version, "1.00" ); is( $d->maturity, "released" ); is( $d->filename, "Acme-Colour-1.00.tar.gz" ); is( $d->cpanid, "LBROCARD" ); is( $d->distvname, "Acme-Colour-1.00" ); is( $p->package( "accessors::chained" )->distribution->dist, "accessors", "accessors::chained lives in accessors" ); is( $p->package( "accessors::classic" )->distribution->dist, "accessors", "as does accessors::classic" ); is( $p->package( "accessors::chained" )->distribution, $p->package( "accessors::classic" )->distribution, "and they're using the same distribution object" ); my $dist = $p->distribution( 'S/SP/SPURKIS/accessors-0.02.tar.gz' ); is( $dist->dist, 'accessors' ); is( $dist, $p->package( "accessors::chained" )->distribution, "by path match by name" ); is_deeply( [ map { $_->package } $dist->contains ], [qw( accessors accessors::chained accessors::classic )], "dist contains packages" ); $d = $p->latest_distribution( "Acme-Colour" ); is( $d->prefix, "L/LB/LBROCARD/Acme-Colour-1.00.tar.gz" ); is( $d->version, "1.00" ); is_deeply( [ sort map { $_->prefix } $p->latest_distributions ], [ 'A/AU/AUTRIJUS/Acme-ComeFrom-0.07.tar.gz', 'K/KA/KANE/Acme-Comment-1.02.tar.gz', 'L/LB/LBROCARD/Acme-Colour-1.00.tar.gz', 'S/SM/SMUELLER/Acme-Currency-0.01.tar.gz', 'S/SP/SPURKIS/accessors-0.02.tar.gz', 'X/XE/XERN/Acme-CramCode-0.01.tar.gz', ] ); # counts is( $p->package_count, scalar @packages, "package count" ); is( $p->distribution_count, 7, "dist count" ); is( $p->latest_distribution_count, 6, "latest dist count" ); return; } sub dist_contents { my ( $p ) = creation_check( @_ ); my $test_dist = $p->dists->{"K/KA/KANE/Acme-Comment-1.02.tar.gz"}; my $file = "Acme-Comment-1.02/lib/Acme/Comment.pm"; is( ( $test_dist->list_files )[0], $file, "listing files in dists works" ); my $test_pkg = $p->data->{"Acme::Comment"}; is( $test_pkg->filename, $file, "a package can generate a good guess file name" ); is( $test_dist->get_file_from_tarball( $file ), "moo", "getting the content of a file in a dist works" ); is( $test_pkg->file_content, "moo", "a package can provide the content of its file" ); return; } sub creation_check { my $reason = pop; my $p = eval { Parse::CPAN::Packages->new( @_ ) }; is( $@, "", "no errors from the eval" ); isa_ok( $p, "Parse::CPAN::Packages" ); my @packages = sort map { $_->package } $p->packages; is_deeply( \@packages, default_packages(), $reason ); return ( $p, @packages ); } sub default_packages { return [qw(Acme::Colour Acme::Colour::Old Acme::ComeFrom Acme::Comment Acme::CramCode Acme::Currency accessors accessors::chained accessors::classic )]; } Parse-CPAN-Packages-2.40/t/02packages.details.txt.gz0000755000175100000000000000101011505635062017541 0ustar root >UA02packages.details.txt.fooMs0zĖ@3P˜xMm1\H@BgR͞lv1Q52xY4p&WM/Ueg0BmAS'B7dѲђ35Za)zm-oJwՂT >L޹6(IV6=z/u4me<цA9pVj{Aj5:KoF9OWOᴩdRY_!4}H/X6dt!*8.$tܺQ= )!?,K]A3JJ R\*oGyyMB,ާp?q$pMcͻф^$|ݕӯۧ Or_+a2iKW9?XDh,=:cOdyŸ< .ԃ _U9.k*XىŘXsɝV?VͭUgRe}OParse-CPAN-Packages-2.40/t/02packages.details.txt0000755000175100000000000000211711505635062017133 0ustar rootFile: 02packages.details.txt URL: http://www.perl.com/CPAN/modules/02packages.details.txt Description: Package names found in directory $CPAN/authors/id/ Columns: package name, version, path Intended-For: Automated fetch routines, namespace documentation. Written-By: Id: mldistwatch 479 2004-01-04 13:29:05Z k Line-Count: 23609 Last-Updated: Fri, 13 Feb 2004 13:50:21 GMT Acme::Colour 1.00 L/LB/LBROCARD/Acme-Colour-1.00.tar.gz Acme::Colour::Old 1.00 L/LB/LBROCARD/Acme-Colour-0.95.tar.gz Acme::ComeFrom 0.07 A/AU/AUTRIJUS/Acme-ComeFrom-0.07.tar.gz Acme::Comment 1.02 K/KA/KANE/Acme-Comment-1.02.tar.gz Acme::CramCode 0.01 X/XE/XERN/Acme-CramCode-0.01.tar.gz Acme::Currency 0.01 S/SM/SMUELLER/Acme-Currency-0.01.tar.gz accessors 0.02 S/SP/SPURKIS/accessors-0.02.tar.gz accessors::chained undef S/SP/SPURKIS/accessors-0.02.tar.gz accessors::classic undef S/SP/SPURKIS/accessors-0.02.tar.gz Parse-CPAN-Packages-2.40/xt/0000755000175100010010000000000012465247244012317 5ustar Parse-CPAN-Packages-2.40/xt/pod.t0000755000175100000000000000022011505635062014156 0ustar root#!perl -T use Test::More; eval "use Test::Pod 1.14"; plan skip_all => "Test::Pod 1.14 required for testing POD: $@" if $@; all_pod_files_ok(); Parse-CPAN-Packages-2.40/xt/pod_coverage.t0000755000175100000000000000104111505635062016033 0ustar rootuse Test::More; eval "use Test::Pod::Coverage 1.00"; plan skip_all => "Test::Pod::Coverage 1.00 required for testing POD coverage" if $@; all_pod_coverage_ok( { also_private => [qr/^[A-Z_]+$/] } ); # Workaround for dumb bug (fixed in 5.8.7) where Test::Builder thinks that # certain "die"s that happen inside evals are not actually inside evals, # because caller() is broken if you turn on $^P like Module::Refresh does # # (I mean, if we've gotten to this line, then clearly the test didn't die, no?) Test::Builder->new->{Test_Died} = 0; Parse-CPAN-Packages-2.40/CHANGES0000755000175100010010000000652012465247106012662 0ustar CHANGES file for Parse::CPAN::Packages 2.40 Fri Feb 7 23:44:00 BST 2015 - changelog fix ( thanks to Karen Etheridge ) 2.39 Fri Feb 6 23:44:00 BST 2015 - ported from Moose to Moo ( thanks to Olaf Alders ) - github repo added to metadata ( thanks to Olaf Alders ) 2.38 Mon Dec 3 14:57:00 BST 2012 - removed some dependency on hash ordering in the tests 2.37 Mon Jul 23 21:59:55 BST 2012 - Version bump to repackage due to issue installing using cpanm 2.36 Mon Jul 9 13:58:00 GMT+2 2012 - moved author tests to xt to avoid them being run on the user side 2.35 Mon Sep 12 21:47:00 GMT+2 2011 - added rudimentary logic to return the subs in a package, as well as verify via regex whether a given sub is in the package 2.34 Tue Aug 30 15:03:00 GMT+2 2011 - added methods to the generated objects to try and retrieve dist contents from a local cpan mirror - some refactoring/cleanup with no functional changes 2.33 Sun Dec 27 10:42:59 GMT+2 2010 - updated documentation - explanation on how to use local cpan cache ( thanks to Slaven Rezić ) - spelling and package name fixes ( Phillip Moore ) - improved memory use a bit by eleminating temp variable ( thanks to Chris 'BinGOs' Williams ) 2.32 Sun Dec 26 14:02:59 GMT+2 2010 - better windows compatibility ( thanks to Vincent Pit ) 2.31 Thu Apr 23 11:55:59 BST 2009 - use BUILDARGS and BUILD instead of having our own constructor 2.30 Fri Jan 23 10:09:05 GMT 2009 - switch to using Moose - use Compress::Zlib instead of IO::Zlib for speed - minor code cleanups - add human-readable license - add an abstract to Makefile.PL - add pod tests, more documentation 2.29 Tue Aug 19 18:37:02 BST 2008 - parse the preamble and add methods to access the fields (patch by Mark Fowler) 2.28 Tue Aug 12 08:02:22 BST 2008 - stop version.pm warnings 2.27 Sun Dec 2 14:50:14 GMT 2007 - fix typo in _ensure_latest_distribution (patch by Kenichi Ishigaki) - stop using Module::Build 2.26 Wed Jun 14 07:31:26 BST 2006 - use version.pm (or number comparison if that fails) instead of Sort::Versions for latest_distribution() (spotted by Thomas Klausner) - run perltidy on the source 2.25 Tue Jul 5 01:16:48 BST 2005 - fix bug where passing in the gz contents was not working 2.24 Sun Feb 6 10:40:36 GMT 2005 - make the new() example in the docs work (spotted by Adam Kennedy) 2.23 Sat Feb 5 17:20:48 GMT 2005 - the distribution() method is now documented - remove an "undefined hash element" warning 2.21 Tue Jun 29 14:57:11 BST 2004 - now we're able to read the gzipped file directly (using IO::ZLib) 2.20 Sat Mar 6 20:21:57 GMT 2004 - list Sort::Versions as a prereq - try and make test more portable (added 'sort') - use Class::Accessor::Fast instead of Class::Accessor::Chained 2.19 Wed Mar 3 22:21:18 GMT 2004 - new methods to get the latest distribution and distributions - possibility to pass the contents of 02packages in addition to the filename 2.17 Wed Feb 25 10:29:07 GMT 2004 - fix a documentation glitch in the main module - make a ->distributions hash available - A distribution now contains packages 2.12 Fri Feb 13 18:42:57 GMT 2004 - first release (suggested by Richard Clamp) Parse-CPAN-Packages-2.40/META.json0000755000175100010010000000267712465247250013321 0ustar { "abstract" : "Parse 02packages.details.txt.gz", "author" : [ "Leon Brocard " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.04, CPAN::Meta::Converter version 2.143240", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Parse-CPAN-Packages", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "Archive::Peek" : "0", "CPAN::DistnameInfo" : "0", "Compress::Zlib" : "0", "File::Slurp" : "0", "Moo" : "0", "PPI" : "0", "Path::Class" : "0", "Test::InDistDir" : "0", "Test::More" : "0", "Type::Utils" : "0", "Types::Standard" : "0", "version" : "0" } } }, "release_status" : "stable", "resources" : { "repository" : { "type" : "git", "url" : "https://github.com/wchristian/parse-cpan-packages.git", "web" : "https://github.com/wchristian/parse-cpan-packages" } }, "version" : "2.40" } Parse-CPAN-Packages-2.40/MANIFEST0000755000175100010010000000100412465247250013010 0ustar CHANGES lib/Parse/CPAN/Packages.pm lib/Parse/CPAN/Packages/Distribution.pm lib/Parse/CPAN/Packages/Package.pm Makefile.PL MANIFEST This list of files README t/02packages.details.txt t/02packages.details.txt.gz t/mirror/authors/id/K/KA/KANE/Acme-Comment-1.02.tar.gz t/mirror/modules/02packages.details.txt.gz t/simple.t xt/pod.t xt/pod_coverage.t META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) Parse-CPAN-Packages-2.40/README0000755000175100000000000001427511505635062013453 0ustar rootNAME Parse::CPAN::Packages - Parse 02packages.details.txt.gz SYNOPSIS use Parse::CPAN::Packages; # must have downloaded my $p = Parse::CPAN::Packages->new("02packages.details.txt.gz"); # either a filename as above or pass in the contents of the file # (uncompressed) my $p = Parse::CPAN::Packages->new($packages_details_contents); my $m = $p->package("Acme::Colour"); # $m is a Parse::CPAN::Packages::Package object print $m->package, "\n"; # Acme::Colour print $m->version, "\n"; # 1.00 my $d = $m->distribution(); # $d is a Parse::CPAN::Packages::Distribution object print $d->prefix, "\n"; # L/LB/LBROCARD/Acme-Colour-1.00.tar.gz print $d->dist, "\n"; # Acme-Colour print $d->version, "\n"; # 1.00 print $d->maturity, "\n"; # released print $d->filename, "\n"; # Acme-Colour-1.00.tar.gz print $d->cpanid, "\n"; # LBROCARD print $d->distvname, "\n"; # Acme-Colour-1.00 # all the package objects my @packages = $p->packages; # all the distribution objects my @distributions = $p->distributions; # the latest distribution $d = $p->latest_distribution("Acme-Colour"); is($d->prefix, "L/LB/LBROCARD/Acme-Colour-1.00.tar.gz"); is($d->version, "1.00"); # all the latest distributions my @distributions = $p->latest_distributions; DESCRIPTION The Comprehensive Perl Archive Network (CPAN) is a very useful collection of Perl code. It has several indices of the files that it hosts, including a file named "02packages.details.txt.gz" in the "modules" directory. This file contains lots of useful information and this module provides a simple interface to the data contained within. In a future release Parse::CPAN::Packages::Package and Parse::CPAN::Packages::Distribution might have more information. Methods new Creates a new instance from a details file. The constructor can be passed either the path to the "02packages.details.txt.gz" file, a path to an ungzipped version of this file, or a scalar containing the entire uncompressed contents of the file. Note that this module does not concern itself with downloading this file. You should do this yourself. For example: use LWP::Simple qw(get); my $data = get("http://www.cpan.org/modules/02packages.details.txt.gz"); my $p = Parse::CPAN::Packages->new($data); package($packagename) Returns a "Parse::CPAN::Packages::Package" that represents the named package. my $p = Parse::CPAN::Distribution->new($gzfilename); my $package = $p->package("Acme::Colour"); packages() Returns a list of Parse::CPAN::Packages::Package objects representing all the packages that were extracted from the file. package_count() Returns the numebr of packages stored. distribution($filename) Returns a Parse::CPAN::Distribution that represents the filename passed: my $p = Parse::CPAN::Distribution->new($gzfilename); my $dist = $p->distribution('L/LB/LBROCARD/Acme-Colour-1.00.tar.gz'); distributions() Returns a list of Parse::CPAN::Distribution objects representing all the known distributions. distribution_count() Returns the number of distributions stored. latest_distribution($distname) Returns the "Parse::CPAN::Distribution" that represents the latest distribution for the named disribution passed, that is to say it returns the distribution that has the highest version number (as determined by version.pm or number comparison if that fails): my $p = Parse::CPAN::Distribution->new($gzfilename); my $dist = $p->distribution('Acme-Color'); latest_distrbutions() Returns a list of Parse::CPAN::Distribution objects representing all the latest distributions. latest_distribution_count() Returns the number of distributions stored. Preamble Methods These methods return the information from the preamble at the start of the file. They return undef if for any reason no matching preamble line was found. file() url() description() columns() intended_for() written_by() line_count() last_updated() Addtional Methods These are additional methods that you may find useful. parse($filename) Parses the filename. Works in a similar fashion to the the constructor (i.e. you can pass it a filename for a compressed/1uncompressed file, a uncompressed scalar containing the file. You can also pass nothing to indicate to load the compressed file from the current working directory.) Note that each time this function is run the packages and distribtions found will be "added" to the current list of packages. add_quick($package_name, $package_version, $prefix) Quick way of adding a new package and distribution. add_package($package_obj) Adds a package. Note that you'll probably want to add the corrisponding distribution for that package too (it's not done automatically.) add_distribution($distribution_obj) Adds a distribution. Note that you'll probably want to add the corrisponding packages for that distribution too (it's not done automatically.) AUTHOR Leon Brocard COPYRIGHT Copyright (C) 2004-9, Leon Brocard LICENSE This module is free software; you can redistribute it or modify it under the same terms as Perl itself. BUGS This module leaks memory as packages hold distributions and distributions hold packages. No attempt has been made to fix this as it's not anticpated that this will be used in long running programs that will dispose of the objects once created. The old interface for "new" where if you passed no arguments it would look for a "02packages.details.txt.gz" in your current directory is no longer supported. TODO delete_* methods. merge_into method. Documentation for other modules. SEE ALSO CPAN::DistInfoname, Parse::CPAN::Packages::Writer. Parse-CPAN-Packages-2.40/Makefile.PL0000755000175100010010000000175412465246777013662 0ustar #!perl use strict; use warnings; use ExtUtils::MakeMaker; WriteMakefile( NAME => 'Parse::CPAN::Packages', VERSION_FROM => 'lib/Parse/CPAN/Packages.pm', AUTHOR => 'Leon Brocard ', ABSTRACT => 'Parse 02packages.details.txt.gz', LICENSE => 'perl', PREREQ_PM => { map { $_ => 0 } qw( Archive::Peek Compress::Zlib CPAN::DistnameInfo File::Slurp Moo Path::Class PPI Test::InDistDir Test::More Type::Utils Types::Standard version ) }, META_MERGE => { 'meta-spec' => { version => 2 }, resources => { repository => { type => 'git', url => 'https://github.com/wchristian/parse-cpan-packages.git', web => 'https://github.com/wchristian/parse-cpan-packages', }, }, }, ); Parse-CPAN-Packages-2.40/lib/0000755000175100010010000000000012465247244012432 5ustar Parse-CPAN-Packages-2.40/lib/Parse/0000755000175100010010000000000012465247244013504 5ustar Parse-CPAN-Packages-2.40/lib/Parse/CPAN/0000755000175100010010000000000012465247244014225 5ustar Parse-CPAN-Packages-2.40/lib/Parse/CPAN/Packages/0000755000175100010010000000000012465247244015743 5ustar Parse-CPAN-Packages-2.40/lib/Parse/CPAN/Packages/Package.pm0000755000175100010010000000367512465246777017664 0ustar package Parse::CPAN::Packages::Package; use Moo; use PPI; use Types::Standard qw( InstanceOf Str ); has 'package' => ( is => 'rw', isa => Str ); has 'version' => ( is => 'rw', isa => Str ); has 'prefix' => ( is => 'rw', isa => Str ); has 'distribution' => ( is => 'rw', isa => InstanceOf ['Parse::CPAN::Packages::Distribution'] ); sub filename { my ( $self ) = @_; my $distribution = $self->distribution; my @filenames = $distribution->list_files; my $package_file = $self->package; $package_file =~ s{::}{/}g; $package_file .= '.pm'; my ( $filename ) = grep { /$package_file$/ } sort { length( $a ) <=> length( $b ) } @filenames; return $filename; } sub file_content { my ( $self ) = @_; my $filename = $self->filename; my $content = $self->distribution->get_file_from_tarball( $filename ); return $content; } sub subs { my ( $self ) = @_; my $document = PPI::Document->new( \( $self->file_content ) ); my $subs = $document->find('PPI::Statement::Sub'); return map { $_->name } @{$subs}; } sub has_matching_sub { my ( $self, $sub_regex ) = @_; my @matching_subs = grep { $_ =~ $sub_regex } $self->subs; return @matching_subs; } 1; __END__ =head1 NAME Parse::CPAN::Packages::Package =head1 DESCRIPTION Represents a CPAN Package. Note: The functions filename and file_content work only if a mirror directory was supplied for parsing or the package file was situated inside a cpan mirror structure. =head1 METHODS =head2 filename Tries to guess the name of the file containing this package by looking through the files contained in the distribution it belongs to. =head2 file_content Tries to return the contents of the file returned by filename(). =head2 subs Experimental function. Tries to return the names of all subs in the package. =head2 has_matching_sub( $regex ) Experimental function. Tries to see if any sub name in the package matches the regex. Parse-CPAN-Packages-2.40/lib/Parse/CPAN/Packages/Distribution.pm0000755000175100010010000000345112465246777021000 0ustar package Parse::CPAN::Packages::Distribution; use Moo; use Archive::Peek; use Path::Class 'file'; use Types::Standard qw( ArrayRef Maybe Str ); has 'prefix' => ( is => 'rw', isa => Str ); has 'dist' => ( is => 'rw', isa => Maybe [Str] ); has 'version' => ( is => 'rw', isa => Maybe [Str] ); has 'maturity' => ( is => 'rw', isa => Str ); has 'filename' => ( is => 'rw', isa => Str ); has 'cpanid' => ( is => 'rw', isa => Str ); has 'distvname' => ( is => 'rw', isa => Maybe [Str] ); has 'packages' => ( is => 'rw', isa => ArrayRef, default => sub { [] } ); has 'mirror_dir' => ( is => 'rw', isa => Maybe [Str] ); sub contains { my $self = shift; return @{ $self->packages }; } sub add_package { my $self = shift; push @{ $self->packages }, @_; } sub list_files { my ( $self ) = @_; my @filenames = $self->_tarball->files; return @filenames; } sub get_file_from_tarball { my ( $self, $filename ) = @_; my $contents = $self->_tarball->file( $filename ); return $contents; } sub _tarball { my ( $self ) = @_; my $file = file( $self->mirror_dir, 'authors', 'id', $self->prefix ); my $peek = Archive::Peek->new( filename => $file ); return $peek; } 1; __END__ =head1 NAME Parse::CPAN::Packages::Distribution =head1 DESCRIPTION Represents a CPAN distribution. Note: The functions list_files and get_file_from_tarball work only if a mirror directory was supplied for parsing or the package file was situated inside a cpan mirror structure. =head1 METHODS =head2 contains Returns the packages in the distribution. =head2 add_package Adds a package to the distribution. =head2 list_files Tries to list all files in the distribution. =head2 get_file_from_tarball( $filename ) Tries to retrieve the contents of a file from the distribution. Parse-CPAN-Packages-2.40/lib/Parse/CPAN/Packages.pm0000755000175100010010000003071612465247137016314 0ustar package Parse::CPAN::Packages; use Moo; use CPAN::DistnameInfo; use Compress::Zlib; use Path::Class (); use File::Slurp 'read_file'; use Parse::CPAN::Packages::Distribution; use Parse::CPAN::Packages::Package; use Types::Standard qw( HashRef Maybe Str ); use version; our $VERSION = '2.40'; has 'filename' => ( is => 'rw', isa => Str ); has 'mirror_dir' => ( is => 'lazy', isa => Maybe [Str] ); has 'details' => ( is => 'rw', isa => HashRef, default => sub { {} } ); has 'data' => ( is => 'rw', isa => HashRef, default => sub { {} } ); has 'dists' => ( is => 'rw', isa => HashRef, default => sub { {} } ); has 'latestdists' => ( is => 'rw', isa => HashRef, default => sub { {} } ); sub BUILDARGS { my ( $class, @args ) = @_; return {@args} if @args > 1; return { filename => $args[0] }; } sub BUILD { my $self = shift; my $filename = $self->filename; # read the file then parse it if present $self->parse( $filename ) if $filename; return $self; } sub _build_mirror_dir { my ( $self ) = @_; return if $self->filename =~ /\n/; return if !-f $self->filename; my $dir = Path::Class::file( $self->filename )->dir->parent; return $dir->stringify; } # read the file into memory and return it sub _slurp_details { my ( $self, $filename ) = @_; $filename ||= '02packages.details.txt.gz'; return $filename if $filename =~ /Description:/; return Compress::Zlib::memGunzip( $filename ) if $filename =~ /^\037\213/; my @read_params = ( $filename ); push @read_params, ( binmode => ':raw' ) if $filename =~ /\.gz/; my $data = read_file( @read_params ); return Compress::Zlib::memGunzip( $data ) if $filename =~ /\.gz/; return $data; } for my $subname ( qw(file url description columns intended_for written_by line_count last_updated) ) { no strict 'refs'; *{$subname} = sub { return shift->{preamble}{$subname} }; } sub parse { my ( $self, $filename ) = @_; # read the preamble my @details = split "\n", $self->_slurp_details( $filename ); while ( @details ) { local $_ = shift @details; last if /^\s*$/; next unless /^([^:]+):\s*(.*)/; my ( $key, $value ) = ( lc( $1 ), $2 ); $key =~ tr/-/_/; $self->{preamble}{$key} = $value; } # run though each line of the file for my $line ( @details ) { # make a package object from the line my ( $package_name, $package_version, $prefix ) = split ' ', $line; $self->add_quick( $package_name, $package_version, $prefix ); } } sub add_quick { my ( $self, $package_name, $package_version, $prefix ) = @_; # create a distribution object (or get an existing one) my $dist = $self->distribution_from_prefix( $prefix ); # create the package object my $m = Parse::CPAN::Packages::Package->new( { package => $package_name, version => $package_version, distribution => $dist } ); # make the package have the distribion and the distribution # have the package. Yes, this creates a cirtular reference. eek! $dist->add_package( $m ); # record this distribution and package $self->add_distribution( $dist ); $self->add_package( $m ); } sub distribution_from_prefix { my ( $self, $prefix ) = @_; # see if we have one of these already and return it if we do. my $d = $self->distribution( $prefix ); return $d if $d; # create a new one otherwise my $i = CPAN::DistnameInfo->new( $prefix ); $d = Parse::CPAN::Packages::Distribution->new( { prefix => $prefix, dist => $i->dist, version => $i->version, maturity => $i->maturity, filename => $i->filename, cpanid => $i->cpanid, distvname => $i->distvname, mirror_dir => $self->mirror_dir, } ); return $d; } sub add_package { my ( $self, $package ) = @_; # store it $self->data->{ $package->package } = $package; return $self; } sub package { my ( $self, $package_name ) = @_; return $self->data->{$package_name}; } sub packages { my $self = shift; return values %{ $self->data }; } sub add_distribution { my ( $self, $dist ) = @_; $self->_store_distribution( $dist ); $self->_ensure_latest_distribution( $dist ); } sub _store_distribution { my ( $self, $dist ) = @_; $self->dists->{ $dist->prefix } = $dist; } sub _ensure_latest_distribution { my ( $self, $new ) = @_; my $latest = $self->latest_distribution( $new->dist ); if ( !$latest ) { $self->_set_latest_distribution( $new ); return; } my $new_version = $new->version; my $latest_version = $latest->version; my ( $newv, $latestv ); eval { no warnings; $newv = version->new( $new_version || 0 ); $latestv = version->new( $latest_version || 0 ); }; $self->_set_latest_distribution( $new ) if $self->_dist_is_latest( $newv, $latestv, $new_version, $latest_version ); return; } sub _dist_is_latest { my ( $self, $newv, $latestv, $new_version, $latest_version ) = @_; return 1 if $newv && $latestv && $newv > $latestv; no warnings; return 1 if $new_version > $latest_version; return 0; } sub distribution { my ( $self, $dist ) = @_; return $self->dists->{$dist}; } sub distributions { my $self = shift; return values %{ $self->dists }; } sub _set_latest_distribution { my ( $self, $dist ) = @_; return unless $dist->dist; $self->latestdists->{ $dist->dist } = $dist; } sub latest_distribution { my ( $self, $dist ) = @_; return unless $dist; return $self->latestdists->{$dist}; } sub latest_distributions { my $self = shift; return values %{ $self->latestdists }; } sub package_count { my $self = shift; return scalar scalar $self->packages; } sub distribution_count { my $self = shift; return scalar $self->distributions; } sub latest_distribution_count { my $self = shift; return scalar $self->latest_distributions; } 1; __END__ =head1 NAME Parse::CPAN::Packages - Parse 02packages.details.txt.gz =head1 SYNOPSIS use Parse::CPAN::Packages; # must have downloaded my $p = Parse::CPAN::Packages->new("02packages.details.txt.gz"); # either a filename as above or pass in the contents of the file # (uncompressed) my $p = Parse::CPAN::Packages->new($packages_details_contents); my $m = $p->package("Acme::Colour"); # $m is a Parse::CPAN::Packages::Package object print $m->package, "\n"; # Acme::Colour print $m->version, "\n"; # 1.00 my $d = $m->distribution(); # $d is a Parse::CPAN::Packages::Distribution object print $d->prefix, "\n"; # L/LB/LBROCARD/Acme-Colour-1.00.tar.gz print $d->dist, "\n"; # Acme-Colour print $d->version, "\n"; # 1.00 print $d->maturity, "\n"; # released print $d->filename, "\n"; # Acme-Colour-1.00.tar.gz print $d->cpanid, "\n"; # LBROCARD print $d->distvname, "\n"; # Acme-Colour-1.00 # all the package objects my @packages = $p->packages; # all the distribution objects my @distributions = $p->distributions; # the latest distribution $d = $p->latest_distribution("Acme-Colour"); is($d->prefix, "L/LB/LBROCARD/Acme-Colour-1.00.tar.gz"); is($d->version, "1.00"); # all the latest distributions my @distributions = $p->latest_distributions; =head1 DESCRIPTION The Comprehensive Perl Archive Network (CPAN) is a very useful collection of Perl code. It has several indices of the files that it hosts, including a file named "02packages.details.txt.gz" in the "modules" directory. This file contains lots of useful information and this module provides a simple interface to the data contained within. In a future release L and L might have more information. =head2 Methods =over =item new Creates a new instance from a details file. The constructor can be passed either the path to the C<02packages.details.txt.gz> file, a path to an ungzipped version of this file, or a scalar containing the entire uncompressed contents of the file. Note that this module does not concern itself with downloading this file. You should do this yourself. For example: use LWP::Simple qw(get); my $data = get("http://www.cpan.org/modules/02packages.details.txt.gz"); my $p = Parse::CPAN::Packages->new($data); If you have a configured L, then there's usually already a cached file available: use CPAN; $CPAN::Be_Silent = 1; CPAN::HandleConfig->load; my $file = $CPAN::Config->{keep_source_where} . "/modules/02packages.details.txt.gz"; my $p = Parse::CPAN::Packages->new($file); =item package($packagename) Returns a C that represents the named package. my $p = Parse::CPAN::Packages->new($gzfilename); my $package = $p->package("Acme::Colour"); =item packages() Returns a list of B objects representing all the packages that were extracted from the file. =item package_count() Returns the number of packages stored. =item distribution($filename) Returns a B object that represents the filename passed: my $p = Parse::CPAN::Packages->new($gzfilename); my $dist = $p->distribution('L/LB/LBROCARD/Acme-Colour-1.00.tar.gz'); =item distributions() Returns a list of B objects representing all the known distributions. =item distribution_count() Returns the number of distributions stored. =item latest_distribution($distname) Returns the C object that represents the latest distribution for the named disribution passed, that is to say it returns the distribution that has the highest version number (as determined by version.pm or number comparison if that fails): my $p = Parse::CPAN::Packages->new($gzfilename); my $dist = $p->distribution('Acme-Color'); =item latest_distrbutions() Returns a list of B objects representing all the latest distributions. =item latest_distribution_count() Returns the number of distributions stored. =back =head2 Preamble Methods These methods return the information from the preamble at the start of the file. They return undef if for any reason no matching preamble line was found. =over =item file() =item url() =item description() =item columns() =item intended_for() =item written_by() =item line_count() =item last_updated() =back =head2 Addtional Methods These are additional methods that you may find useful. =over =item parse($filename) Parses the filename. Works in a similar fashion to the the constructor (i.e. you can pass it a filename for a compressed/1uncompressed file, a uncompressed scalar containing the file. You can also pass nothing to indicate to load the compressed file from the current working directory.) Note that each time this function is run the packages and distribtions found will be C to the current list of packages. =item add_quick($package_name, $package_version, $prefix) Quick way of adding a new package and distribution. =item add_package($package_obj) Adds a package. Note that you'll probably want to add the corrisponding distribution for that package too (it's not done automatically.) =item add_distribution($distribution_obj) Adds a distribution. Note that you'll probably want to add the corresponding packages for that distribution too (it's not done automatically.) =item distribution_from_prefix($prefix) Returns a distribution given a prefix. =item latest_distributions Returns all the latest distributions: my @distributions = $p->latest_distributions; =cut =back =head1 AUTHOR Leon Brocard =head1 COPYRIGHT Copyright (C) 2004-9, Leon Brocard =head1 LICENSE This module is free software; you can redistribute it or modify it under the same terms as Perl itself. =head1 BUGS This module leaks memory as packages hold distributions and distributions hold packages. No attempt has been made to fix this as it's not anticpated that this will be used in long running programs that will dispose of the objects once created. The old interface for C where if you passed no arguments it would look for a C<02packages.details.txt.gz> in your current directory is no longer supported. =head1 TODO delete_* methods. merge_into method. Documentation for other modules. =head1 SEE ALSO L, L. Parse-CPAN-Packages-2.40/META.yml0000755000175100010010000000143212465247245013141 0ustar --- abstract: 'Parse 02packages.details.txt.gz' author: - 'Leon Brocard ' build_requires: ExtUtils::MakeMaker: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.04, CPAN::Meta::Converter version 2.143240' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Parse-CPAN-Packages no_index: directory: - t - inc requires: Archive::Peek: '0' CPAN::DistnameInfo: '0' Compress::Zlib: '0' File::Slurp: '0' Moo: '0' PPI: '0' Path::Class: '0' Test::InDistDir: '0' Test::More: '0' Type::Utils: '0' Types::Standard: '0' version: '0' resources: repository: https://github.com/wchristian/parse-cpan-packages.git version: '2.40'