File Coverage

blib/lib/OrePAN2/Index.pm
Criterion Covered Total %
statement 72 72 100.0
branch 16 18 88.8
condition 7 8 87.5
subroutine 15 15 100.0
pod 5 6 83.3
total 115 119 96.6


line stmt bran cond sub pod time code
1             package OrePAN2::Index;
2              
3 14     14   423834 use autodie;
  14         255919  
  14         78  
4 14     14   97996 use utf8;
  14         770  
  14         163  
5              
6 14     14   3101 use IO::Uncompress::Gunzip qw( $GunzipError );
  14         168945  
  14         2044  
7 14     14   4503 use OrePAN2 ();
  14         34  
  14         411  
8 14     14   1507 use version;
  14         6926  
  14         123  
9 14     14   7478 use OrePAN2::Logger;
  14         72  
  14         516  
10              
11 14     14   82 use Moo;
  14         33  
  14         72  
12             with 'OrePAN2::Role::HasLogger';
13 14     14   10287 use Types::Standard qw( HashRef );
  14         474298  
  14         318  
14 14     14   46587 use namespace::clean;
  14         229042  
  14         100  
15              
16             has index => ( is => 'ro', isa => HashRef, default => sub { +{} } );
17              
18             sub load {
19 9     9 1 194 my ( $self, $fname ) = @_;
20              
21 9         27 my $fh = do {
22 9 100       64 if ( $fname =~ /\.gz\z/ ) {
23 3 50       43 IO::Uncompress::Gunzip->new($fname)
24             or die "gzip failed: $GunzipError\n";
25             }
26             else {
27 6         46 open my $fh, '<', $fname;
28 6         6686 $fh;
29             }
30             };
31              
32             # skip headers
33 9         7734 while (<$fh>) {
34 60 100       1903 last unless /\S/;
35             }
36              
37 9         39 while (<$fh>) {
38 24 50       663 if (/^(\S+)\s+(\S+)\s+(.*)$/) {
39 24 100       110 $self->add_index( $1, $2 eq 'undef' ? undef : $2, $3 );
40             }
41             }
42              
43 9         111 close $fh;
44             }
45              
46             sub lookup {
47 9     9 1 14449 my ( $self, $package ) = @_;
48 9 100       48 if ( my $entry = $self->index->{$package} ) {
49 8         42 return @$entry;
50             }
51 1         8 return;
52             }
53              
54             sub packages {
55 19     19 0 68 my ($self) = @_;
56 19         111 sort { lc $a cmp lc $b } keys %{ $self->index };
  81         181  
  19         207  
57             }
58              
59             sub delete_index {
60 1     1 1 5 my ( $self, $package ) = @_;
61 1         7 delete $self->index->{$package};
62 1         3 return;
63             }
64              
65             # Order of preference is last updated. So if some modules maintain the same
66             # version number across multiple uploads, we'll point to the module in the
67             # latest archive.
68              
69             sub add_index {
70 84     84 1 2190 my ( $self, $package, $version, $archive_file ) = @_;
71              
72 84 100       531 if ( $self->index->{$package} ) {
73 13         25 my ($orig_ver) = @{ $self->index->{$package} };
  13         45  
74              
75 13 100       222 if ( version->parse($orig_ver) > version->parse($version) ) {
76 2   50     10 $version //= 'undef';
77 2         107 $self->log->info("Not adding $package in $archive_file");
78 2         238 $self->log->info(
79             "Existing version $orig_ver is greater than $version");
80 2         78 return;
81             }
82             }
83 82         2116 $self->index->{$package} = [ $version, $archive_file ];
84             }
85              
86             sub as_string {
87 16     16 1 153 my ( $self, $opts ) = @_;
88 16   100     73 $opts ||= +{};
89 16   100     114 my $simple = $opts->{simple} || 0;
90              
91 16         81 my @buf;
92              
93 16 100       120 push @buf,
94             (
95             'File: 02packages.details.txt',
96             'URL: http://www.perl.com/CPAN/modules/02packages.details.txt',
97             'Description: DarkPAN',
98             'Columns: package name, version, path',
99             'Intended-For: Automated fetch routines, namespace documentation.',
100             $simple
101             ? ()
102             : (
103             "Written-By: OrePAN2 $OrePAN2::VERSION",
104 12         37 "Line-Count: @{[ scalar(keys %{$self->index}) ]}",
  12         106  
105 12         573 "Last-Updated: @{[ scalar localtime ]}",
106             ),
107             q{},
108             );
109              
110 16         111 for my $pkg ( $self->packages ) {
111 51         130 my $entry = $self->index->{$pkg};
112              
113             # package name, version, path
114 51   100     314 push @buf, sprintf '%-22s %-22s %s', $pkg, $entry->[0] || 'undef',
115             $entry->[1];
116             }
117 16         606 return join( "\n", @buf ) . "\n";
118             }
119              
120             1;
121             __END__
122              
123             =head1 NAME
124              
125             OrePAN2::Index - Index
126              
127             =head1 DESCRIPTION
128              
129             This is a module to manipulate 02packages.details.txt.
130              
131             =head1 METHODS
132              
133             =over 4
134              
135             =item C<< my $index = OrePAN2::Index->new(%attr) >>
136              
137             =item C<< $index->load($filename) >>
138              
139             Load an existing 02.packages.details.txt
140              
141             =item C<< my ($version, $path) = $index->lookup($package) >>
142              
143             Perform a package lookup on the index.
144              
145             =item C<< $index->delete_index($package) >>
146              
147             Delete a package from the index.
148              
149             =item C<< $index->add_index($package, $version, $path) >>
150              
151             Add a new entry to the index.
152              
153             =item C<< $index->as_string() >>
154              
155             Returns the content of the index as a string. Some of the index metadata can
156             cause merge conflicts when multiple developers are working on the same project.
157             You can avoid this problem by using a paring down the metadata. "simple"
158             defaults to 0.
159              
160             $index->as_string( simple => 1 );
161              
162             Make index as string.
163              
164             =back