File Coverage

blib/lib/JSAN/Index/Release.pm
Criterion Covered Total %
statement 163 214 76.1
branch 48 76 63.1
condition 2 5 40.0
subroutine 40 50 80.0
pod 23 31 74.1
total 276 376 73.4


line stmt bran cond sub pod time code
1             package JSAN::Index::Release;
2              
3             # See POD at end for docs
4              
5 6     6   134 use 5.008005;
  6         19  
  6         256  
6 6     6   36 use strict;
  6         11  
  6         260  
7 6     6   33 use warnings;
  6         13  
  6         176  
8 6     6   33 use Carp ();
  6         18  
  6         124  
9 6     6   35 use File::Spec ();
  6         9  
  6         116  
10 6     6   41 use File::Spec::Unix ();
  6         22  
  6         108  
11 6     6   31 use File::Path ();
  6         12  
  6         124  
12 6     6   32 use Params::Util ();
  6         11  
  6         96  
13 6     6   3772 use JSAN::Index::Distribution ();
  6         15  
  6         114  
14 6     6   37 use JSAN::Index::Author ();
  6         10  
  6         254  
15              
16             our $VERSION = '0.29';
17              
18             BEGIN {
19             # Optional prefork.pm support
20 6     6   436 eval "use prefork 'YAML'";
  6     6   3118  
  0         0  
  0         0  
21 6     6   364 eval "use prefork 'Archive::Tar'";
  6         3897  
  0         0  
  0         0  
22 6     6   390 eval "use prefork 'Archive::Zip'";
  6         3368  
  0         0  
  0         0  
23             }
24              
25             # Make the tar code read saner below
26 6     6   36 use constant COMPRESSED => 1;
  6         11  
  6         22363  
27              
28             sub distribution {
29 2     2 1 768 JSAN::Index::Distribution->retrieve(
30             name => $_[0]->{distribution},
31             );
32             }
33              
34             sub author {
35 1     1 1 17 JSAN::Index::Author->retrieve(
36             login => $_[0]->{author},
37             );
38             }
39              
40             sub retrieve {
41 618     618 0 1791 my $class = shift;
42 618         1875 my %params = @_;
43 618         1655 my $sql = join " and ", map { "$_ = ?" } keys(%params);
  618         2653  
44 618         3463 my @result = $class->select( "where $sql", values(%params) );
45 618 50       2543 if ( @result == 1 ) {
46 618         3679 return $result[0];
47             }
48 0 0       0 if ( @result > 1 ) {
49 0         0 Carp::croak("Found more than one author record");
50             } else {
51 0         0 return undef;
52             }
53             }
54              
55              
56             sub search_like {
57 0     0 0 0 my $class = shift;
58 0         0 my %params = @_;
59 0         0 my $sql = join " and ", map { "$_ like ?" } keys(%params);
  0         0  
60            
61 0         0 my @result = $class->select( "where $sql", values(%params) );
62            
63             return @result
64 0         0 }
65              
66              
67             sub retrieve_all {
68             shift->select
69 6     6 0 28 }
70              
71              
72             sub file_path {
73 3     3 1 30 JSAN::Index->transport->file_location($_[0]->source)->path;
74             }
75              
76             sub file_mirrored {
77 2     2 1 1939 !! -f $_[0]->file_path;
78             }
79              
80             sub mirror {
81 19     19 1 1104 my $self = shift;
82 19         186 my $location = JSAN::Index->transport->file_mirror($self->source);
83 19         126 $location->path;
84             }
85              
86             sub created_string {
87 0     0 1 0 scalar localtime( shift()->created );
88             }
89              
90             sub requires {
91 2148     2148 1 7320 my $self = shift;
92              
93             # Get the raw dependency hash
94 2148         4797 my $meta = $self->meta_data;
95 2148 100       10557 unless ( UNIVERSAL::isa($meta, 'HASH') ) {
96             # If it has no META.yml at all, we assume that it
97             # has no dependencies.
98 642         3490 return ();
99             }
100 1506 100       13685 my $requires = $meta->{requires} or return {};
101 423 50       2326 if ( UNIVERSAL::isa($requires, 'HASH') ) {
102             # To be safe (mainly in case it's a dependency object of
103             # some sort) make sure it's a plain hash before returning.
104 0         0 my %hash = %$requires;
105 0         0 return \%hash;
106             }
107              
108             # It could be an array of Requires objects
109 423 50       2694 if ( UNIVERSAL::isa($requires, 'ARRAY') ) {
110 423         1043 my %hash = ();
111 423         1693 foreach my $dep ( @$requires ) {
112 606 50       5776 unless ( Params::Util::_INSTANCE($dep, 'Module::META::Requires') ) {
113 0         0 Carp::croak("Unknown dependency structure in META.yml for "
114             . $self->source);
115             }
116 606         2983 $hash{ $dep->{name} } = $dep->{version};
117             }
118 423         9456 return \%hash;
119             }
120              
121 0         0 Carp::croak("Unknown 'requires' dependency structure in META.yml for "
122             . $self->source);
123             }
124              
125             sub build_requires {
126 357     357 1 707 my $self = shift;
127              
128             # Get the raw dependency hash
129 357         1080 my $meta = $self->meta_data;
130 357 100       1931 unless ( UNIVERSAL::isa($meta, 'HASH') ) {
131             # If it has no META.yml at all, we assume that it
132             # has no dependencies.
133 107         282 return ();
134             }
135 250 100       2271 my $requires = $meta->{build_requires} or return {};
136 120 50       639 if ( UNIVERSAL::isa($requires, 'HASH') ) {
137             # To be safe (mainly in case it's a dependency object of
138             # some sort) make sure it's a plain hash before returning.
139 0         0 my %hash = %$requires;
140 0         0 return \%hash;
141             }
142              
143             # It could be an array of Requires objects
144 120 50       567 if ( UNIVERSAL::isa($requires, 'ARRAY') ) {
145 120         327 my %hash = ();
146 120         451 foreach my $dep ( @$requires ) {
147 147 50       1497 unless ( Params::Util::_INSTANCE($dep, 'Module::META::Requires') ) {
148 0         0 Carp::croak("Unknown dependency structure in META.yml for "
149             . $self->source);
150             }
151 147         877 $hash{ $dep->{name} } = $dep->{version};
152             }
153 120         1405 return \%hash;
154             }
155              
156 0         0 Carp::croak("Unknown 'build_requires' dependency structure in META.yml for "
157             . $self->source);
158             }
159              
160             sub requires_libraries {
161 2146     2146 1 4753 my $self = shift;
162 2146         7587 my $requires = $self->requires;
163              
164             # Find the library object for each key
165 2146         4696 my @libraries = ();
166 2146         14337 foreach my $name ( sort keys %$requires ) {
167 604         4329 my $library = JSAN::Index::Library->retrieve( name => $name );
168 604 100       3473 push @libraries, $library if $library;
169             }
170              
171 2146         8202 @libraries;
172             }
173              
174             sub build_requires_libraries {
175 357     357 1 748 my $self = shift;
176 357         1194 my $requires = $self->build_requires;
177              
178             # Find the library object for each key
179 357         855 my @libraries = ();
180 357         1894 foreach my $name ( sort keys %$requires ) {
181 147         2202 my $library = JSAN::Index::Library->retrieve( name => $name );
182 147 100       741 push @libraries, $library if $library;
183             }
184              
185 357         1468 @libraries;
186             }
187              
188             sub requires_releases {
189 2144     2144 1 3442 my $self = shift;
190 2144         5593 my @libraries = $self->requires_libraries;
191              
192             # Derive a list of releases
193 2144         5012 my @releases = map { $_->release } @libraries;
  464         1989  
194 2144         9757 return @releases;
195             }
196              
197             sub build_requires_releases {
198 357     357 1 761 my $self = shift;
199 357         1395 my @libraries = $self->build_requires_libraries;
200              
201             # Derive a list of releases
202 357         876 my @releases = map { $_->release } @libraries;
  138         585  
203 357         1882 return @releases;
204             }
205              
206             sub meta_data {
207 2513     2513 1 3525 my $self = shift;
208 2513         26504 require YAML;
209 2513         59756 my @structs = YAML::Load($self->meta);
210 2513 50       19130400 unless ( defined $structs[0] ) {
211 0         0 Carp::croak("Failed to load META.yml struct for "
212             . $self->source );
213             }
214 2513         10708 $structs[0];
215             }
216              
217             sub archive {
218             # Cache result of the real method
219 29 100   29 1 560 $_[0]->{archive} or
220             $_[0]->{archive} = $_[0]->_archive;
221             }
222              
223             sub _archive {
224 9     9   18 my $self = shift;
225              
226             # Load tarballs
227 9 50       29 if ( $self->source =~ /\.(tar\.gz|tgz)$/ ) {
228 9         17092 require Archive::Tar;
229 9         1083362 my $tar = Archive::Tar->new;
230 9         268 my $path = $self->mirror;
231 9 50       108 unless ( $tar->read($path, COMPRESSED) ) {
232 0         0 Carp::croak("Failed to open tarball '$path'");
233             }
234 9         180945 return $tar;
235             }
236              
237             # Load zip files
238 0 0       0 if ( $self->source =~ /\.zip$/ ) {
239 0         0 require Archive::Zip;
240 0         0 my $zip = Archive::Zip->new;
241 0         0 my $path = $self->mirror;
242 0 0       0 unless ( $zip->read($path) == Archive::Zip::AZ_OK() ) {
243 0         0 Carp::croak("Failed to open zip file '$path'");
244             }
245 0         0 return $zip;
246             }
247              
248             # We don't support anything else
249 0         0 Carp::croak('Failed to load unsupported archive type '
250             . $self->source);
251             }
252              
253             sub extract_libs {
254 7     7 0 15 my $self = shift;
255 7         33 $self->extract_resource('lib', @_);
256             }
257              
258              
259             sub extract_static_files {
260 7     7 0 16 my $self = shift;
261            
262 7   50     28 my $static_dir = $self->meta_data->{static_dir} || 'static';
263            
264 6         68 $self->extract_resource($static_dir, @_, is_static => 1);
265             }
266              
267              
268             sub extract_tests {
269 0     0 0 0 my $self = shift;
270 0         0 $self->extract_resource('tests', @_);
271             }
272              
273             sub extract_resource {
274 14     14 0 27 my $self = shift;
275 14 50       68 my $resource = shift
276             or Carp::croak("No resource name provided to extract_resource");
277 14         71 my %params = @_;
278              
279             # Check the extraction destination
280 14   33     60 $params{to} ||= File::Spec->curdir;
281 14 50       342 unless ( -d $params{to} ) {
282 0         0 Carp::croak("Extraction directory '$params{to}' does not exist");
283             }
284 14 50       213 unless ( -w $params{to} ) {
285 0         0 Carp::croak("No permissions to write to extraction directory '$params{to}'");
286             }
287              
288             # Split on archive type
289 14 50       95 if ( $self->archive->isa('Archive::Tar') ) {
290 14         74 return $self->_extract_resource_from_tar($resource, @_);
291             }
292 0 0       0 if ( $self->archive->isa('Archive::Zip') ) {
293 0         0 return $self->_extract_resource_from_zip($resource, @_);
294             }
295 0         0 Carp::croak("Unsupported archive type " . ref($self->archive));
296             }
297              
298              
299              
300              
301              
302             #####################################################################
303             # Support Methods
304              
305             sub _extract_resource_from_tar {
306 14     14   60 my ($self, $resource, %params) = @_;
307 14         52 my $tar = $self->archive;
308 14         86 my @files = $tar->get_files;
309            
310             # Determine which files to extract, and to where
311 14         243 my $extracted_files = 0;
312 14         44 foreach my $item ( @files ) {
313 272 100       1844 next unless $item->is_file;
314              
315             # Split into parts and remove the top level dir
316 176         2343 my ($vol, $dir, $file)
317             = File::Spec::Unix->splitpath($item->full_path);
318 176         5757 my @dirs = File::Spec::Unix->splitdir($dir);
319 176         233 shift @dirs;
320              
321             # Is this file in the resource directory
322             # Also skips all root-level files
323 176 100       474 my $res = shift(@dirs) or next;
324 119 100       410 next unless $res eq $resource;
325            
326             # Static files are put into the library, so /static/all.css becomes /Dist/Name/static/all.css
327 8 50       29 @dirs = (split(/\./, $self->distribution->name), $res, @dirs) if $params{is_static};
328              
329             # These are STILL relative, but we'll deal with that later.
330 8         137 my $write_dir = File::Spec->catfile($params{to}, @dirs);
331              
332             # Write the file
333 8         45 $self->_write( $write_dir, $file, $item->get_content, $params{is_static} );
334 8         35 $extracted_files++;
335             }
336              
337             # Return the number of files, or error if none
338 14 100       252 return $extracted_files if $extracted_files;
339 6         21 my $path = $self->source;
340            
341             # Only resource 'static' is optional
342 6 50       66 Carp::croak("Tarball '$path' does not contain resource '$resource'") unless $params{is_static};
343             }
344              
345             sub _extract_resource_from_zip {
346 0     0   0 Carp::croak("Zip support not yet completed");
347             }
348              
349              
350             sub _write {
351 8     8   182 my ($self, $dir, $file, $content, $is_static) = @_;
352              
353             # Localise newlines in the files unless we are extracting the static file (which can be binary)
354 8 50       12423 $content =~ s/(\015{1,2}\012|\015|\012)/\n/g unless $is_static;
355              
356             # Create the save directory if needed
357 8 100       1617 File::Path::mkpath( $dir, 0, 0755 ) unless -d $dir;
358              
359             # Save it
360 8         110 my $path = File::Spec->catfile( $dir, $file );
361 8 50       831 unless ( open( LIBRARY, '>', $path ) ) {
362 0         0 Carp::croak( "Failed to open '$path' for writing: $!" );
363             }
364 8 50       81 unless ( print LIBRARY $content ) {
365 0         0 Carp::croak( "Failed to write to '$path'" );
366             }
367 8 50       450 unless ( close LIBRARY ) {
368 0         0 Carp::croak( "Failed to close '$path' after writing" );
369             }
370              
371 8         22 1;
372             }
373              
374              
375              
376              
377              
378             ######################################################################
379             # Generated by ORLite 1.25 (Unused parts are commented out)
380              
381             #sub base { 'JSAN::Index' }
382             #
383             #sub table { 'release' }
384              
385             sub select {
386 628     628 1 1058 my $class = shift;
387 628         1592 my $sql = 'select "id", "distribution", "author", "checksum", "created", "doc", "meta", "latest", "source", "srcdir", "version" from release ';
388 628 100       2290 $sql .= shift if @_;
389 628         3934 my $rows = JSAN::Index->selectall_arrayref( $sql, { Slice => {} }, @_ );
390 628         779013 bless( $_, 'JSAN::Index::Release' ) foreach @$rows;
391 628 100       4303 wantarray ? @$rows : $rows;
392             }
393              
394             #sub count {
395             # my $class = shift;
396             # my $sql = 'select count(*) from release ';
397             # $sql .= shift if @_;
398             # JSAN::Index->selectrow_array( $sql, {}, @_ );
399             #}
400             #
401             #sub iterate {
402             # my $class = shift;
403             # my $call = pop;
404             # my $sql = 'select "id", "distribution", "author", "checksum", "created", "doc", "meta", "latest", "source", "srcdir", "version" from release ';
405             # $sql .= shift if @_;
406             # my $sth = JSAN::Index->prepare( $sql );
407             # $sth->execute( @_ );
408             # while ( $_ = $sth->fetchrow_hashref ) {
409             # bless( $_, 'JSAN::Index::Release' );
410             # $call->() or last;
411             # }
412             # $sth->finish;
413             #}
414              
415             sub id {
416 0     0 1 0 $_[0]->{id};
417             }
418              
419             #sub distribution {
420             # $_[0]->{distribution};
421             #}
422              
423             #sub author {
424             # $_[0]->{author};
425             #}
426              
427             sub checksum {
428 0     0 1 0 $_[0]->{checksum};
429             }
430              
431             sub created {
432 0     0 1 0 $_[0]->{created};
433             }
434              
435             sub doc {
436 0     0 1 0 $_[0]->{doc};
437             }
438              
439             sub meta {
440 2513     2513 1 12978 $_[0]->{meta};
441             }
442              
443             sub latest {
444 0     0 1 0 $_[0]->{latest};
445             }
446              
447             sub source {
448 2798     2798 1 11894 $_[0]->{source};
449             }
450              
451             sub srcdir {
452 0     0 0 0 $_[0]->{srcdir};
453             }
454              
455             sub version {
456 48     48 1 166 $_[0]->{version};
457             }
458              
459             1;
460              
461             __END__