File Coverage

blib/lib/Module/Extract.pm
Criterion Covered Total %
statement 37 49 75.5
branch 8 22 36.3
condition 1 3 33.3
subroutine 13 13 100.0
pod 6 6 100.0
total 65 93 69.8


line stmt bran cond sub pod time code
1             package Module::Extract;
2              
3             =pod
4              
5             =head1 NAME
6              
7             Module::Extract - Base class for working with Perl distributions
8              
9             =head1 SYNOPSIS
10              
11             Creating a Module::Extract subclass.
12              
13             package My::Readme;
14            
15             # Shows the README file for a module
16            
17             use strict;
18             use base 'Module::Extract';
19            
20             sub show {
21             my $self = shift;
22             my $readme = $self->file_path('README');
23             if ( -f $readme ) {
24             system( "cat $readme" );
25             } else {
26             print "Dist does not have a README file";
27             }
28             }
29            
30             1;
31              
32             A script that uses the module to show the readme file for a distribution
33             filename provided by the user.
34              
35             #!/usr/bin/perl
36            
37             use My::Readme;
38            
39             My::Readme->new( dist_file => $ARGV[0] )->show;
40            
41             exit(0);
42              
43             =head1 DESCRIPTION
44              
45             B is a convenience base class for creating module that
46             work with Perl distributions.
47              
48             Its purpose is to take care of the mechanisms of locating and extracting
49             a Perl distribution so that your module can do something specific to the
50             distribution.
51              
52             This module was originally created to provide an abstraction for the
53             extraction logic for both L and L and
54             to allow additional features to be added in the future without having
55             to modify both of them, because the general problem of "locate, download,
56             and expand a distribution" is one that is almost ideal for adding
57             additional features down the line.
58              
59             =cut
60              
61 2     2   41573 use strict;
  2         6  
  2         81  
62 2     2   13 use Carp ();
  2         5  
  2         173  
63 2     2   21 use File::Path ();
  2         5  
  2         27  
64 2     2   3046 use File::Temp ();
  2         71607  
  2         66  
65              
66 2     2   19 use vars qw{$VERSION};
  2         4  
  2         174  
67             BEGIN {
68 2     2   1453 $VERSION = '0.01';
69             }
70              
71             # Flag Archive::Extract for prefork'ing if needed
72 2     2   13878 eval " use prefork 'Archive::Extract'; ";
  0         0  
  0         0  
73              
74              
75              
76              
77              
78             #####################################################################
79             # Constructor and Accessors
80              
81             =pod
82              
83             =head2 new
84              
85             my $from_file = My::Class->new(
86             dist_file => 'tarball.tar.gz',
87             );
88            
89             my $from_dir = My::Class->new(
90             dist_dir => 'some/dir',
91             );
92              
93             The C constructor takes a C and/or a C param,
94             locates and (if needed) expands the distribution tarball.
95              
96             It takes either a C param, which should be the local path
97             to the tarball, or a C which should be the path to a directory
98             which contains an already-expanded distribution (such as from a
99             repository checkout).
100              
101             Return a new B-subclass object, or dies on error.
102              
103             =cut
104              
105             sub new {
106 1     1 1 1496 my $class = shift;
107 1         6 my $self = bless { @_ }, $class;
108              
109 1 50       8 if ( $self->dist_file ) {
    0          
110             # Create the inspector for a tarball
111 1 50       6 unless ( $self->dist_file =~ /\.(?:zip|tgz|tar\.gz)$/ ) {
112 0         0 Carp::croak("The dist_file '" . $self->dist_file . "' is not a zip|tgz|tar.gz");
113             }
114 1 50       4 unless ( -f $self->dist_file ) {
115 0         0 Carp::croak("The dist_file '" . $self->dist_file . "' does not exist");
116             }
117              
118             # Do we have a directory to unroll to
119 1 50       5 if ( $self->dist_dir ) {
120             # The directory should not exist
121 0 0       0 if ( -d $self->dist_dir ) {
122 0         0 Carp::croak("Cannot reuse an pre-existing dist_dir '"
123             . $self->dist_dir
124             . "'" );
125             }
126              
127             # Create it
128 0         0 File::Path::mkpath( $self->dist_dir );
129             } else {
130             # Find a temp directory
131 1         7 $self->{dist_dir} = File::Temp::tempdir( CLEANUP => 1 );
132             }
133              
134             # Double check it now exists and is writable
135 1 50 33     838 unless ( -d $self->dist_dir and -w $self->dist_dir ) {
136 0         0 Carp::croak("The dist_dir '" . $self->dist_dir . "' is not writeable");
137             }
138              
139             # Unpack dist_file into dist_dir
140 1         1397 require Archive::Extract;
141 1 50       246232 my $archive = Archive::Extract->new( archive => $self->dist_file )
142             or Carp::croak("Failed to extract dist_file '"
143             . $self->dist_file . "'"
144             );
145 1         251 $self->{dist_type} = $archive->type;
146 1 50       13 unless ( $archive->extract( to => $self->dist_dir ) ) {
147 0         0 Carp::croak("Failed to extract dist_file '"
148             . $self->dist_file . "'"
149             );
150             }
151              
152             # Double check the expansion directory
153 1 50       542825 if ( $archive->extract_path ne $self->dist_dir ) {
154             # Archive::Extract can extract to a single
155             # directory beneath the target, in which case
156             # we actually want to be using that as our dist_dir.
157 1         7 $self->{dist_dir} = $archive->extract_path;
158             }
159              
160             } elsif ( $self->dist_dir ) {
161             # Create the inspector for a directory
162 0 0       0 unless ( -d $self->dist_dir ) {
163 0         0 Carp::croak("Missing or invalid module root $self->{dist_dir}");
164             }
165              
166             } else {
167             # Missing a module location
168 0         0 Carp::croak("Did not provide a dist_file or dist_dir param");
169             }
170              
171 1         63 $self;
172             }
173              
174             =pod
175              
176             =head2 dist_file
177              
178             The C accessor returns the (absolute) path to the
179             distribution tarball. If the inspector was created with C
180             rather than C, this will return C.
181              
182             =cut
183              
184             sub dist_file {
185 5     5 1 1498 $_[0]->{dist_file};
186             }
187              
188             =pod
189              
190             =head2 dist_type
191              
192             The C method returns the archive type of the
193             distribution tarball. This will be either 'tar.gz', 'tgz', or
194             'zip'. Other file types are not supported at this time.
195              
196             If the inspector was created with C rather than
197             C, this will return C.
198              
199             =cut
200              
201             sub dist_type {
202 1     1 1 20 $_[0]->{dist_type};
203             }
204              
205             =pod
206              
207             =head2 dist_dir
208              
209             The C method returns the (absolute) distribution root directory.
210              
211             If the inspector was created with C rather than C,
212             this method will return the temporary directory created to hold the
213             unwrapped tarball.
214              
215             =cut
216              
217             sub dist_dir {
218 8     8 1 229 $_[0]->{dist_dir};
219             }
220              
221             =pod
222              
223             =head2 file_path
224              
225             my $local_path = $inspector->file_path('lib/Foo.pm');
226              
227             To simplify implementations, most tools that work with distributions
228             identify files in unix-style relative paths.
229              
230             The C method takes a unix-style relative path and returns
231             a localised absolute path to the file on disk (either in the actual
232             distribution directory, or the temp directory holding the expanded
233             tarball.
234              
235             =cut
236              
237             sub file_path {
238 1     1 1 5 File::Spec->catfile( $_[0]->dist_dir, $_[1] );
239             }
240              
241             =pod
242              
243             =head2 dir_path
244              
245             my $local_path = $inspector->file_path('lib');
246              
247             The C method is the matching pair of the C method.
248              
249             As for that method, it takes a unix-style relative directory name,
250             and returns a localised absolute path to the directory.
251              
252             =cut
253              
254             sub dir_path {
255 1     1 1 7 File::Spec->catdir( $_[0]->dist_dir, $_[1] );
256             }
257              
258             1;
259              
260             =pod
261              
262             =head1 SUPPORT
263              
264             This module is stored in an Open Repository at the following address.
265              
266             L
267              
268             Write access to the repository is made available automatically to any
269             published CPAN author, and to most other volunteers on request.
270              
271             If you are able to submit your bug report in the form of new (failing)
272             unit tests, or can apply your fix directly instead of submitting a patch,
273             you are B encouraged to do so as the author currently maintains
274             over 100 modules and it can take some time to deal with non-Critcal bug
275             reports or patches.
276              
277             This will guarentee that your issue will be addressed in the next
278             release of the module.
279              
280             If you cannot provide a direct test or fix, or don't have time to do so,
281             then regular bug reports are still accepted and appreciated via the CPAN
282             bug tracker.
283              
284             L
285              
286             For other issues, for commercial enhancement or support, or to have your
287             write access enabled for the repository, contact the author at the email
288             address above.
289              
290             =head1 AUTHORS
291              
292             Adam Kennedy Eadamk@cpan.orgE
293              
294             =head1 SEE ALSO
295              
296             L, L
297              
298             =head1 COPYRIGHT
299              
300             Copyright 2006 Adam Kennedy.
301              
302             This program is free software; you can redistribute
303             it and/or modify it under the same terms as Perl itself.
304              
305             The full text of the license can be found in the
306             LICENSE file included with this module.
307              
308             =cut