File Coverage

blib/lib/BackPAN/Index/Create.pm
Criterion Covered Total %
statement 70 75 93.3
branch 12 22 54.5
condition 6 14 42.8
subroutine 11 11 100.0
pod 0 1 0.0
total 99 123 80.4


line stmt bran cond sub pod time code
1             package BackPAN::Index::Create;
2             $BackPAN::Index::Create::VERSION = '0.13';
3 5     5   59697 use 5.006;
  5         15  
  5         153  
4 5     5   16 use strict;
  5         7  
  5         125  
5 5     5   24 use warnings;
  5         5  
  5         122  
6 5     5   2089 use Exporter::Lite;
  5         2569  
  5         21  
7 5     5   2966 use Path::Iterator::Rule;
  5         53034  
  5         174  
8 5     5   32 use Scalar::Util qw/ reftype /;
  5         6  
  5         491  
9 5     5   2815 use Module::Loader;
  5         9278  
  5         129  
10 5     5   25 use Carp;
  5         7  
  5         270  
11 5     5   2420 use autodie;
  5         66360  
  5         30  
12              
13             our @EXPORT_OK = qw(create_backpan_index);
14             my $FORMAT_REVISION = 1;
15             my $DEFAULT_ORDER = 'dist';
16             my $PLUGIN_NAMESPACE = 'BackPAN::Index::Create::OrderBy';
17             my $COMMA = q{,};
18              
19             sub create_backpan_index
20             {
21 4 50 33 4 0 7145 if (@_ != 1 || reftype($_[0]) ne 'HASH') {
22 0         0 croak "create_backpan_index() expects a single hashref argument\n";
23             }
24 4         9 my $argref = shift;
25 4   33     21 my $basedir = $argref->{basedir}
26             || croak "create_backpan_index() must be given a 'basedir'\n";
27 4 50       16 my $order = defined($argref->{order})
28             ? $argref->{order}
29             : $DEFAULT_ORDER;
30 4         10 my $author_dir = "$basedir/authors";
31 4         11 my $stem = "$author_dir/id";
32 4   100     15 my $releases_only = $argref->{releases_only} || 0;
33 4   33     31 my $loader = Module::Loader->new()
34             || croak "failed to instantiate Module::Loader\n";
35 4         53 my @plugins = $loader->find_modules($PLUGIN_NAMESPACE);
36 4         8613 my @plugin_basenames = map { my $p = $_; $p =~ s/^.*:://; $p } @plugins;
  12         15  
  12         40  
  12         19  
37 4         5 my $fh;
38              
39              
40 4 50       46 if (not -d $author_dir) {
41 0         0 croak "create_backpan_index() can't find 'authors' directory in basedir ($basedir)\n";
42             }
43              
44 4         8 my ($basename) = grep { lc($_) eq lc($order) } @plugin_basenames;
  12         24  
45 4 50       13 if (not defined($basename)) {
46 0         0 croak "order '$order' not known. Supported orders are ",
47 0         0 join($COMMA, map { "'".lc($_)."'" } @plugin_basenames), "\n";
48             }
49 4         11 my $plugin_class = $PLUGIN_NAMESPACE.'::'.$basename;
50              
51 4         22 $loader->load($plugin_class);
52              
53 4 50       35 if (exists($argref->{output})) {
54 4         24 open($fh, '>', $argref->{output});
55             }
56             else {
57 0         0 $fh = \*STDOUT;
58             }
59              
60 4   33     8545 my $plugin = $plugin_class->new(filehandle => $fh)
61             || croak "failed to create instance of '$order' plugin ($plugin_class)";
62              
63 4         80 print $fh "#FORMAT $FORMAT_REVISION\n";
64              
65 4         35 my $rule = Path::Iterator::Rule->new();
66              
67 4         42 $rule->file->name("*");
68              
69 4 100       608 if ($releases_only) {
70             # A 'releases only' index contains just the tarballs
71             # and the paths don't include the leading 'authors/id'
72             # Does a BackPAN ever contain anything in a directory
73             # other than authors?
74 3 50   18   21 $rule->and(sub { /\.(tar\.gz|tgz|zip)$/ }) if $releases_only;
  18         6082  
75 3         33 $stem = "$author_dir/id";
76             }
77             else {
78 1         2 $stem = $basedir;
79             }
80              
81 4         22 foreach my $path ($rule->all($author_dir)) {
82 12 50       2083 next if $path =~ /\s+\z/;
83 12 50       27 next if $path =~ /\n/;
84 12         14 my $tail = $path;
85 12         113 $tail =~ s!^\Q${stem}\E[^A-Za-z0-9]+!!;
86 12 50       53 $tail =~ s!\\!/!g if $^O eq 'MSWin32';
87 12         159 my @stat = stat($path);
88 12         17 my $time = $stat[9];
89 12         18 my $size = $stat[7];
90             # printf $fh "%s %d %d\n", $tail, $time, $size;
91 12         35 $plugin->add_file($tail, $time, $size);
92             }
93              
94 4         106 $plugin->finish();
95              
96 4 50       27 close($fh) if exists($argref->{output});
97             }
98              
99             1;
100              
101             =head1 NAME
102              
103             BackPAN::Index::Create - generate an index file for a BackPAN mirror
104              
105             =head1 SYNOPSIS
106              
107             use BackPAN::Index::Create qw/ create_backpan_index /;
108              
109             create_backpan_index({
110             basedir => '/path/to/backpan'
111             releases_only => 0 | 1,
112             output => 'backpan-index.txt',
113             order => 'dist' # or 'author' or 'age'
114             });
115              
116             =head1 DESCRIPTION
117              
118             B provides a function C
119             that will create a text index file for a BackPAN CPAN mirror.
120             A BackPAN CPAN mirror is like a regular CPAN mirror, but it has everything
121             that has ever been released to CPAN.
122             The canonical BackPAN mirror is L.
123              
124             By default the generated index will look like this:
125              
126             #FORMAT 1
127             authors/id/B/BA/BARBIE/Acme-CPANAuthors-British-1.01.meta.txt 1395991503 1832
128             authors/id/B/BA/BARBIE/Acme-CPANAuthors-British-1.01.readme.txt 1395991503 1912
129             authors/id/B/BA/BARBIE/Acme-CPANAuthors-British-1.01.tar.gz 1395991561 11231
130              
131             The first line is a comment that identifies the revision number of the index format.
132             For each file in the BackPAN mirror the index will then contain one line.
133             Each line contains three items:
134              
135             =over 4
136              
137             =item * path
138              
139             =item * timestamp
140              
141             =item * size (in bytes)
142              
143             =back
144              
145             You can see indexes created using this module on the
146             L,
147             the home page of which also has more information about BackPAN mirrors.
148              
149             =head1 ARGUMENTS
150              
151             The supported arguments are all shown in the SYNOPSIS.
152              
153             =head2 basedir
154              
155             The path to the top of your BackPAN repository, without a trailing slash.
156              
157             =head2 releases_only
158              
159             If the C option is true, then the index will only contain
160             release tarballs, and the paths won't include the leading C:
161              
162             #FORMAT 1
163             B/BA/BARBIE/Acme-CPANAuthors-British-1.01.tar.gz 1395991561 11231
164              
165             =head2 output
166              
167             The path to the file where the index should be generated.
168             If the file already exists, it will be over-written.
169              
170             =head2 order
171              
172             Specifies what order the entries should be written to the index in.
173             Currently supported values are:
174              
175             =over 4
176              
177             =item * 'dist'
178              
179             Entries are sorted first by dist name (as determined by L,
180             and then by age. This means that when processing the file, you'll see
181             entries dist by dist, and within each dist you'll see them in order they
182             were released.
183              
184             =item * 'author'
185              
186             Entries are sorted first by author, and then by filename.
187             This will cluster files from the same release.
188              
189             =item * 'age'
190              
191             Entries are sorted first by age, and then by filename.
192             Given that filenames are unique on CPAN, this should give
193             a deterministic result for a specific BackPAN.
194              
195             =back
196              
197             The supported sort orders are defined by plugins in the
198             C namespace.
199             C, C, and C are included in the base distribution.
200             If you have installed additional plugins, they'll be automatically available.
201              
202             Note: CPAN::DistnameInfo doesn't handle paths for files other than
203             tarballs, so if you generate a full index, you may not get the results
204             you expect.
205              
206             =head1 Note for Windows users
207              
208             On Windows the generated index will use '/' as the directory separator,
209             as I I that's the right thing to do.
210             Please let me know if you think I'm wrong.
211              
212             =head1 SEE ALSO
213              
214             L - a script that provides a command-line interface
215             to this module, included in the same distribution.
216              
217             L - an interface to an alternate BackPAN index.
218              
219             L -
220             one of the BackPAN mirrors. It provides four different indexes
221             that are generated using this module. The script used to generate
222             these can be found in the C directory of this distribution.
223              
224             =head1 REPOSITORY
225              
226             L
227              
228             =head1 AUTHOR
229              
230             Neil Bowers Eneilb@cpan.orgE
231              
232             =head1 COPYRIGHT AND LICENSE
233              
234             This software is copyright (c) 2014 by Neil Bowers .
235              
236             This is free software; you can redistribute it and/or modify it under
237             the same terms as the Perl 5 programming language system itself.
238