File Coverage

blib/lib/Pinto/IndexWriter.pm
Criterion Covered Total %
statement 84 84 100.0
branch 4 6 66.6
condition 4 7 57.1
subroutine 14 14 100.0
pod 0 1 0.0
total 106 112 94.6


line stmt bran cond sub pod time code
1             # ABSTRACT: Write records to an 02packages.details.txt file
2              
3             package Pinto::IndexWriter;
4              
5 51     51   339 use Moose;
  51         114  
  51         403  
6 51     51   351614 use MooseX::StrictConstructor;
  51         128  
  51         452  
7 51     51   164748 use MooseX::MarkAsMethods ( autoclean => 1 );
  51         118  
  51         485  
8              
9 51     51   179014 use IO::Zlib;
  51         122  
  51         453  
10 51     51   2585 use Module::CoreList;
  51         101  
  51         479  
11 51     51   1727 use Path::Class qw(file);
  51         104  
  51         3312  
12 51     51   302 use HTTP::Date qw(time2str);
  51         111  
  51         16188  
13              
14 51     51   297 use Pinto::Types qw(File);
  51         110  
  51         468  
15 51     51   308332 use Pinto::Util qw(debug throw);
  51         125  
  51         42715  
16              
17             #------------------------------------------------------------------------------
18              
19             our $VERSION = '0.13'; # VERSION
20              
21             #------------------------------------------------------------------------------
22              
23             has stack => (
24             is => 'ro',
25             isa => 'Pinto::Schema::Result::Stack',
26             required => 1,
27             );
28              
29             has index_file => (
30             is => 'ro',
31             isa => File,
32             default => sub { $_[0]->stack->modules_dir->file('02packages.details.txt.gz') },
33             lazy => 1,
34             );
35              
36             #------------------------------------------------------------------------------
37              
38             sub write_index {
39 285     285 0 882 my ($self) = @_;
40              
41 285         8831 my $index_file = $self->index_file;
42 285         6816 my $stack = $self->stack;
43              
44 285         1917 debug("Writing index for stack $stack at $index_file");
45              
46 285 50       1529 my $handle = IO::Zlib->new( $index_file->stringify, 'wb' )
47             or throw "Cannot open $index_file: $!";
48              
49 285         574145 my @records = $self->_get_index_records($stack);
50 285         44393 my $count = scalar @records;
51              
52 285         3494 debug("Index for stack $stack has $count records");
53              
54 285         2041 $self->_write_header( $handle, $index_file, $count );
55 285         2940 $self->_write_records( $handle, @records );
56 285         1796 close $handle;
57              
58 285         294532 return $self;
59             }
60              
61             #------------------------------------------------------------------------------
62              
63             sub _write_header {
64 285     285   1190 my ( $self, $fh, $filename, $line_count ) = @_;
65              
66 285         2128 my $base = $filename->basename;
67 285         4624 my $uri = 'file://' . $filename->absolute->as_foreign('Unix');
68              
69 285         111206 my $writer = ref $self;
70 285   50     5637 my $version = $self->VERSION || 'UNKNOWN';
71 285         2841 my $date = time2str(time);
72              
73 285         7141 print {$fh} <<"END_PACKAGE_HEADER";
  285         6005  
74             File: $base
75             URL: $uri
76             Description: Package names found in directory \$CPAN/authors/id/
77             Columns: package name, version, path
78             Intended-For: Automated fetch routines, namespace documentation.
79             Written-By: $writer version $version
80             Line-Count: $line_count
81             Last-Updated: $date
82              
83             END_PACKAGE_HEADER
84              
85 285         72601 return $self;
86             }
87              
88             #------------------------------------------------------------------------------
89              
90             sub _write_records {
91 285     285   7797 my ( $self, $fh, @records ) = @_;
92              
93 285         1102 for my $record (@records) {
94 187510         19651666 my ( $name, $version, $author, $archive ) = @{$record};
  187510         519118  
95 187510         469382 my $path = join '/', substr( $author, 0, 1 ), substr( $author, 0, 2 ), $author, $archive;
96 187510         272632 my $width = 38 - length $version;
97 187510 100       359036 $width = length $name if $width < length $name;
98 187510         220486 printf {$fh} "%-${width}s %s %s\n", $name, $version, $path;
  187510         608668  
99             }
100              
101 285         51115 return $self;
102             }
103              
104             #------------------------------------------------------------------------------
105              
106             sub _get_index_records {
107 285     285   1082 my ( $self, $stack ) = @_;
108              
109             # The index is rewritten after almost every action, so we want
110             # this to be as fast as possible (especially during an Add or
111             # Remove action). Therefore, we use a cursor to get raw data and
112             # skip all the DBIC extras.
113              
114             # Yes, slurping all the records at once consumes a lot of memory,
115             # but I want them to be sorted the way perl sorts them, not the
116             # way sqlite sorts them. That way, the index file looks more
117             # like one produced by PAUSE. Also, this is about twice as fast
118             # as using an iterator to read each record lazily.
119              
120 285         1279 my @joins = qw(package distribution);
121 285         1269 my @selects = qw(package.name package.version distribution.author distribution.archive);
122              
123 285         1472 my $attrs = { join => \@joins, select => \@selects };
124 285         9232 my $rs = $stack->head->search_related( 'registrations', {}, $attrs );
125 285         1624356 my %stack_records = map { ($_->[0] => $_) } $rs->cursor->all;
  266         1333684  
126              
127             # Now, we merge the stuff from the stack with core modules. If
128             # the stack has a newer version of a core module (dual-life) then
129             # it should be the one that appears in the index. Then finally
130             # we sort them.
131              
132 285         1426442 my %fake_records = $self->_get_fake_records;
133 285         57040 my %merged_records = (%fake_records, %stack_records);
134              
135 187510         299021 return map { $merged_records{$_} }
136 285         25490 sort {lc $a cmp lc $b}
  1528189         1896017  
137             keys %merged_records;
138              
139             }
140              
141             #------------------------------------------------------------------------------
142              
143             sub _get_fake_records {
144 285     285   974 my ($self) = @_;
145              
146             # We generate artificial records for all the (non-deprecated) core modules
147             # that are in the target perl. That way, the index appears to have perl
148             # itself (just like the real CPAN) and installers can handle requests to
149             # install a core module.
150              
151 285         11100 my $tpv = $self->stack->target_perl_version;
152 285         2497 my $tpv_normal = $tpv->normal; $tpv_normal =~ s/^v//;
  285         1703  
153 285         1484 my @fake = ("FAKE", "perl-$tpv_normal.tar.gz");
154              
155 285         5005 my $core_versions = $Module::CoreList::version{$tpv->numify + 0};
156 285         2530 my $deprecated_modules = $Module::CoreList::deprecated{$tpv->numify + 0};
157              
158 285         884 my $fake_records = {};
159 285         695 for my $module (keys %{ $core_versions }) {
  285         69396  
160 187245 50 33     449276 next if $deprecated_modules && exists $deprecated_modules->{ $module };
161 187245   100     645441 $fake_records->{$module} = [$module, $core_versions->{$module} || 0, @fake];
162             }
163              
164 285         10118 return %{ $fake_records };
  285         75437  
165             }
166              
167             #------------------------------------------------------------------------------
168              
169             __PACKAGE__->meta->make_immutable;
170              
171             #------------------------------------------------------------------------------
172              
173             1;
174              
175             __END__
176              
177             =pod
178              
179             =encoding UTF-8
180              
181             =for :stopwords Jeffrey Ryan Thalhammer
182              
183             =head1 NAME
184              
185             Pinto::IndexWriter - Write records to an 02packages.details.txt file
186              
187             =head1 VERSION
188              
189             version 0.13
190              
191             =head1 AUTHOR
192              
193             Jeffrey Ryan Thalhammer <jeff@stratopan.com>
194              
195             =head1 COPYRIGHT AND LICENSE
196              
197             This software is copyright (c) 2015 by Jeffrey Ryan Thalhammer.
198              
199             This is free software; you can redistribute it and/or modify it under
200             the same terms as the Perl 5 programming language system itself.
201              
202             =cut