File Coverage

blib/lib/Archive/Builder/Archive.pm
Criterion Covered Total %
statement 115 126 91.2
branch 30 50 60.0
condition 5 13 38.4
subroutine 25 26 96.1
pod 5 9 55.5
total 180 224 80.3


line stmt bran cond sub pod time code
1             package Archive::Builder::Archive;
2              
3             # Represents the actual or potential Archive.
4              
5 7     7   39 use strict;
  7         13  
  7         223  
6 7     7   35 use Scalar::Util ();
  7         12  
  7         352  
7 7     7   35 use Params::Util ('_STRING');
  7         12  
  7         336  
8 7     7   36 use Archive::Builder ();
  7         13  
  7         103  
9 7     7   179 use Class::Inspector ();
  7         34  
  7         147  
10              
11 7     7   34 use vars qw{$VERSION};
  7         12  
  7         559  
12             BEGIN {
13 7     7   152 $VERSION = '1.16';
14             }
15              
16              
17              
18              
19              
20             # This module makes use of several Archive related modules as needed.
21             # To start, catalogue the ones we can use.
22 7     7   34 use vars qw{$dependencies $support};
  7         12  
  7         1822  
23             BEGIN {
24 7     7   64 $dependencies = {
25             'zip' => [ 'Archive::Zip', 'Compress::Zlib' ],
26             'tar' => [ 'Archive::Tar' ],
27             'tgz' => [ 'Archive::Tar', 'Compress::Zlib' ],
28             'tar.gz' => [ 'Archive::Tar', 'Compress::Zlib' ],
29             };
30              
31             # Which types are we able to create
32 7         48 foreach my $type ( keys %$dependencies ) {
33 49         9174 $support->{$type} = ! grep { ! Class::Inspector->installed( $_ ) }
  28         67  
34 28         6578 @{$dependencies->{$type}};
35             }
36             }
37              
38              
39              
40              
41             # Which types are supported
42             sub types {
43 1     1 1 31 grep { $support->{$_} } sort keys %$support;
  4         13  
44             }
45              
46              
47              
48              
49              
50             # Create the new Archive handle
51             sub new {
52 9     9 1 25 my $class = shift;
53 9 50 33     106 my $type = (_STRING($_[0]) and exists $support->{$_[0]}) ? shift : return undef;
54 9 50       185 my $Source = _CAN(shift, '_archive_content') or return undef;
55              
56             # Can we use the type?
57 9 50       33 unless ( $support->{$type} ) {
58 0         0 my $modules = join ', ', @{ $dependencies->{$type} };
  0         0  
59 0         0 return $class->_error( "$type support requires that the modules $modules are installed" );
60             }
61              
62             # Make sure there is at least one file
63 9 50       41 unless ( $Source->file_count > 0 ) {
64 0         0 return $class->_error( "Your Source does not contain any files" );
65             }
66              
67             # Get the generated files
68 9         38 my $files = $Source->_archive_content;
69 9 50 0     27 return $class->_error(
70             "Error generating content to create archive: "
71             . $Source->errstr || 'Unknown Error'
72             ) unless $files;
73              
74             # Find any special modes we need to set
75 9         39 my $modes = $Source->_archive_mode;
76 9 50 0     29 return $class->_error(
77             "Error generated permissions to create archive: "
78             . $Source->errstr || 'Unknown Error'
79             ) unless $modes;
80              
81             # Create the object
82 9         49 my $self = bless {
83             type => $type,
84             files => $files,
85             modes => $modes,
86             }, $class;
87              
88 9         32 $self;
89             }
90              
91             # Get the type
92             sub type {
93 4     4 1 5446 $_[0]->{type};
94             }
95              
96             # Get the file hash
97             sub files {
98 4     4 0 36 $_[0]->{files};
99             }
100              
101             # Get the mode hash
102             sub modes {
103 0     0 0 0 $_[0]->{modes};
104             }
105              
106             # Get them in the special sorted order
107             sub sorted_files {
108 4     4 0 368 my $self = shift;
109 4         9 my @files = sort keys %{$self->files};
  4         14  
110 4 50       29 return () unless @files;
111 4         9 my $first = undef;
112 4         42 my $parts = undef;
113 4         15 foreach ( 0 .. $#files ) {
114 15         59 my @f = split /\//, $files[$_];
115 15         21 my $this = scalar @f;
116 15 100 100     72 if ( defined $parts and $this >= $parts ) {
117 10         27 next;
118             }
119 5         8 $first = $_;
120 5         13 $parts = $this;
121             }
122 4         15 unshift @files, splice( @files, $first, 1 );
123 4         17 return @files;
124             }
125              
126             # Get the generated file as a scalar ref
127             sub generate {
128 8     8 1 31 my $self = shift;
129 8 100       229 $self->{generated} || ($self->{generated} = $self->_generate);
130             }
131              
132             sub _generate {
133 4     4   8 my $self = shift;
134              
135             # Load the required modules
136 4         7 my @modules = @{ $dependencies->{ $self->{type} } };
  4         20  
137 4         12 foreach ( @modules ) {
138 7         73247 Class::Autouse->load( $_ );
139             }
140              
141 4 100       371797 if ( $self->{type} eq 'zip' ) {
    100          
    100          
    50          
142 1         5 return $self->_zip;
143             } elsif ( $self->{type} eq 'tar' ) {
144 1         6 return $self->_tar;
145             } elsif ( $self->{type} eq 'tar.gz' ) {
146 1         5 return $self->_tar_gz;
147             } elsif ( $self->{type} eq 'tgz' ) {
148 1         7 return $self->_tgz;
149             } else {
150 0         0 return undef;
151             }
152             }
153              
154             # Saves the archive to disk
155             sub save {
156 4     4 1 7830 my $self = shift;
157 4         10 my $filename = shift;
158              
159             # Add the extension to the filename if needed
160 4         15 my $type = quotemeta $self->{type};
161 4 50       128 unless ( $filename =~ /\.$type$/ ) {
162 4         12 $filename .= '.' . $self->{type};
163             }
164              
165             # Can we write to the location
166 4 50       44 unless ( File::Flat->canWrite( $filename ) ) {
167 0         0 return $self->_error( "Insufficient permissions to write to '$filename'" );
168             }
169              
170             # Get the generated archive
171 4         1735 my $contents = $self->generate;
172 4 50       12 unless ( $contents ) {
173 0         0 return $self->_error( "Error generating $self->{type} archive" );
174             }
175              
176             # Write the file
177 4 50       25 unless ( File::Flat->write( $filename, $contents ) ) {
178 0         0 return $self->_error( "Error writing $self->{type} archive '$filename' to disk" );
179             }
180              
181 4         19805 1;
182             }
183              
184              
185              
186              
187              
188             #####################################################################
189             # Generators
190              
191             # We should never get to these methods if the correct modules arn't
192             # installed. They should also be loaded.
193              
194             sub _zip {
195 1     1   2 my $self = shift;
196              
197             # Create the new, empty archive
198 1         6 my $Archive = Archive::Zip->new;
199              
200             # Add each file to it
201 1         53 my $files = $self->{files};
202 1         3 my $modes = $self->{modes};
203 1         5 foreach my $path ( keys %$files ) {
204 4         76 my $content = $files->{$path};
205 4         16 my $member = $Archive->addString( $$content, $path );
206 4         20427 $member->desiredCompressionMethod(
207             Archive::Zip::COMPRESSION_DEFLATED()
208             );
209 4 50       54 if ( $modes->{$path} ) {
210 4         12 $member->unixFileAttributes( $modes->{$path} );
211             }
212             }
213              
214             # Now stringify the Archive and return it
215 1         28 my $handle = IO::String->new;
216 1 50       182 unless ( $Archive->writeToFileHandle( $handle ) == Archive::Zip::AZ_OK() ) {
217 0         0 return undef;
218             }
219 1         7866 return $handle->string_ref;
220             }
221              
222             sub _tar {
223 3     3   7 my $self = shift;
224              
225             # Create the empty tar object
226 3         30 my $Archive = Archive::Tar->new;
227 3 50       64 unless ( $Archive ) {
228 0         0 return $self->_error( 'Error creating tar object' );
229             }
230              
231             # Add each file to it
232 3         10 my $files = $self->{files};
233 3         6 my $modes = $self->{modes};
234 3         13 foreach my $path ( $self->sorted_files ) {
235 12         92 my $content = $files->{$path};
236 12         45 my $member = $Archive->add_data( $path, $$content );
237 12 50       6463 if ( $modes->{$path} ) {
238 12         40 $member->mode( $modes->{$path} );
239             }
240             }
241              
242             # Get the output
243 3         82 my $string = $Archive->write;
244              
245             # Free up some memory
246 3         23979 $Archive->clear;
247              
248 3 50       117 return $string ? \$string : undef;
249             }
250              
251             sub _tar_gz {
252 2     2   5 my $self = shift;
253              
254             # Get the normal tar
255 2 50       10 my $tar = $self->_tar or return undef;
256              
257             # Compress it
258 2         15 my $compressed = Compress::Zlib::memGzip( $$tar );
259 2 50       1868 $compressed ? \$compressed : undef;
260             }
261              
262             # Exactly the same as _tar_gz
263 1     1   5 sub _tgz { shift->_tar_gz }
264              
265              
266              
267              
268              
269             #####################################################################
270             # Utility methods
271              
272             # Pass through error
273 15     15 0 9254 sub errstr { Archive::Builder->errstr }
274 1     1   607 sub _error { shift; Archive::Builder->_error(@_) }
  1         6  
275 1     1   530 sub _clear { Archive::Builder->_clear }
276              
277             # Params::Util style checking function
278             sub _CAN {
279 9 50 33 9   180 (defined $_[0] and Scalar::Util::blessed($_[0]) and $_[0]->can($_[1])) ? $_[0] : undef;
280             }
281              
282             1;
283              
284             __END__