File Coverage

blib/lib/BackPAN/Index/Create.pm
Criterion Covered Total %
statement 72 77 93.5
branch 13 22 59.0
condition 5 11 45.4
subroutine 12 12 100.0
pod 0 1 0.0
total 102 123 82.9


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