File Coverage

blib/lib/IO/File/CompressOnClose.pm
Criterion Covered Total %
statement 99 106 93.4
branch 58 64 90.6
condition 1 6 16.6
subroutine 14 15 93.3
pod 7 8 87.5
total 179 199 89.9


line stmt bran cond sub pod time code
1             #
2             # $Id: CompressOnClose.pm,v 1.3 2003/12/28 15:29:05 james Exp $
3             #
4              
5             =head1 NAME
6              
7             IO::File::CompressOnClose - compress a file when done writing to it
8              
9             =head1 SYNOPSIS
10              
11             use IO::File::CompressOnClose;
12             my $io = IO::File::CompressOnClose->new('>foo');
13             print $io "foo bar baz\n";
14             $io->close; # file will be compressed to foo.gz on unix or
15             # foo.zip on Windows
16              
17             To change compression schema to a class (which is expected to have
18             a C<< ->compress() >> class method):
19              
20             $io->compressor('Foo::Bar');
21              
22             To change compression scheme to an arbitrary coderef:
23              
24             $io->compressor(\&coderef);
25              
26             =cut
27              
28             package IO::File::CompressOnClose;
29              
30 10     10   8472 use strict;
  10         22  
  10         444  
31 10     10   54 use warnings;
  10         16  
  10         372  
32              
33 10     10   63 use vars qw|@ISA $VERSION|;
  10         16  
  10         810  
34              
35             @ISA = qw|IO::File|;
36             $VERSION = '0.11';
37              
38 10     10   58 use Carp qw|croak|;
  10         21  
  10         613  
39 10     10   5404 use IO::File;
  10         79776  
  10         21182  
40              
41             # default compression format by platform
42             my %platform_to_compressor = (
43             dos => 'IO::File::CompressOnClose::Zip',
44             MSWin32 => 'IO::File::CompressOnClose::Zip',
45             '*' => 'IO::File::CompressOnClose::Gzip',
46             );
47              
48             # open the file
49             sub open
50             {
51              
52 25     25 1 20664 my $self = shift;
53 25         54 my($file) = @_;
54              
55             # if we are the base class, set our compressor per our platform
56             # otherwise compressing using whatever class we are
57 25 100       95 if( ref $self eq __PACKAGE__ ) {
58 22   33     215 $self->compressor( $platform_to_compressor{$^O} ||
59             $platform_to_compressor{'*'} );
60             }
61             else {
62 3         26 $self->compressor( ref $self );
63             }
64            
65             # default to removing the original file after compression
66 25         97 $self->delete_after_compress(1);
67              
68             # figure out if the file was opened for write
69             # (borrowed from IO::File::open)
70 25 100       63 if( @_ > 1 ) {
71 9         14 my $mode = $_[1];
72 9 100       92 if( $mode =~ m/^\d+$/ ) {
    100          
73             # we don't deal with numeric modes yet
74 1         16 croak("numeric modes not supported by IO::File::CompressOnClose");
75             }
76             elsif( $mode =~ m/:/ ) {
77             # nor do we deal with IO layers
78 1         11 croak("io layers not supported by IO::File::CompressOnClose");
79             }
80 7 100       34 unless( IO::Handle::_open_mode_string($mode) =~ m/>/ ) {
81 1         27 return $self->SUPER::open(@_);
82             }
83 6         129 $self->compress_on_close(1);
84             }
85             else {
86 16 100       68 if( $file =~ m/>/ ) {
87 11         42 $self->compress_on_close(1);
88             }
89             }
90            
91             # remove redirection characters from the filename
92 22         73 $file =~ s/[<>]+//;
93              
94             # get the absolute path to the file
95 22 100       471 if (! File::Spec->file_name_is_absolute($file)) {
96 21         1123 $file = File::Spec->rel2abs(File::Spec->curdir(),$file);
97             }
98 22         102 $self->filename( $file );
99            
100             # get our parent class to do the real open
101 22         133 my $rc = $self->SUPER::open(@_);
102            
103             # if the file doesn't exist then we probably were given
104             # something esoteric like >&1
105 22 100       16808 unless( -f $file ) {
106 4         44 $self->compress_on_close(0);
107 4         248 croak("'$file' does not exist after open");
108             }
109            
110 18         63 return $rc;
111            
112             }
113              
114             # set the default compression scheme
115             sub _set_compressor
116             {
117            
118 0     0   0 my $self = shift;
119            
120             # set the compressor based upon our class
121 0   0     0 $self->compressor( $platform_to_compressor{$^O} ||
122             $platform_to_compressor{'*'} );
123              
124 0         0 return $self;
125            
126             }
127              
128             # close and compress the file
129             sub close
130             {
131              
132 22     22 0 1150 my $self = shift;
133              
134             # dispatch to our parent class to do the real close
135 22         160 $self->SUPER::close(@_);
136              
137             # skip out if we've already been invoked
138 22 100       4255 return 1 if( $self->compressed );
139            
140             # skip out if we aren't supposed to compress
141 21 100       79 return 1 unless( $self->compress_on_close );
142            
143             # make sure we have a valid compression class or func
144 15         49 my $compressor = $self->compressor;
145 15 100       49 if( ref $compressor eq 'CODE' ) {
146 1 50       4 $compressor->($self->filename, $self->compress_to)
147             && $self->compressed(1);
148             }
149             else {
150             # load the compression class if it isn't already
151 14 50       104 unless( UNIVERSAL::isa($compressor, 'UNIVERSAL') ) {
152 0         0 eval "require $compressor";
153 0 0       0 if( $@ ) {
154 0         0 croak("could not load compression class $compressor: $@");
155             }
156             }
157             # make sure it is a subclass
158 14 100       84 unless( UNIVERSAL::isa($compressor, __PACKAGE__) ) {
159 8         206 croak("$compressor is not a subclass of " . __PACKAGE__);
160             }
161             # make sure it can compress
162 6 100       47 unless( UNIVERSAL::can($compressor, 'compress') ) {
163 1         14 croak("$compressor cannot 'compress'");
164             }
165             # re-bless ourselves into the subclass
166 5         13 bless $self, $compressor;
167            
168             # dispatch to the compress method
169 5 100       28 $self->compress($self->filename, $self->compress_to)
170             && $self->compressed(1);
171             }
172            
173             # unlink the original file
174 6 50       47 if( $self->delete_after_compress ) {
175 6 50       20 unless( unlink($self->filename) ) {
176 0         0 croak("cannot unlink ", $self->filename, " after compress: $!");
177             }
178             }
179            
180 6         26 return 1;
181              
182             }
183              
184              
185             # make sure that our close is called on object destruction
186             sub DESTROY
187             {
188            
189 26     26   22652 my $self = shift;
190 26 100       221 if( $self->opened ) { $self->close }
  8         78  
191              
192             }
193              
194              
195             # accessor methods
196             sub filename
197             {
198            
199 42     42 1 1630 my($self, $newval) = @_;
200 42         57 my $oldval = ${*$self}->{filename};
  42         132  
201 42 100       137 ${*$self}->{filename} = $newval if( @_ > 1 );
  22         58  
202 42         983 return $oldval;
203            
204             }
205              
206             sub compress_to
207             {
208            
209 16     16 1 60 my($self, $newval) = @_;
210 16         25 my $oldval = ${*$self}->{compress_to};
  16         77  
211 16 100       56 ${*$self}->{compress_to} = $newval if( @_ > 1 );
  8         22  
212 16         106 return $oldval;
213            
214             }
215              
216             sub compressor
217             {
218            
219 50     50 1 3039 my($self, $newval) = @_;
220 50         65 my $oldval = ${*$self}->{compressor};
  50         232  
221 50 100       895 ${*$self}->{compressor} = $newval if( @_ > 1 );
  31         90  
222 50         131 return $oldval;
223            
224             }
225              
226             sub compress_on_close
227             {
228            
229 59     59 1 1674 my($self, $newval) = @_;
230 59 100       72 my $oldval = ${*$self}->{compress_on_close} ? 1 : 0;
  59         249  
231 59 100       242 if( @_ > 1 ) {
232 25 100       97 ${*$self}->{compress_on_close} = $newval ? 1 : 0;
  25         83  
233             }
234 59         333 return $oldval;
235            
236             }
237              
238             sub delete_after_compress
239             {
240            
241 39     39 1 175 my($self, $newval) = @_;
242 39 100       51 my $oldval = ${*$self}->{delete_after_compress} ? 1 : 0;
  39         159  
243 39 100       136 if( @_ > 1 ) {
244 29 100       111 ${*$self}->{delete_after_compress} = $newval ? 1 : 0;
  29         139  
245             }
246 39         114 return $oldval;
247            
248             }
249              
250             sub compressed
251             {
252            
253 34     34 1 6638 my($self, $newval) = @_;
254 34 100       50 my $oldval = ${*$self}->{compressed} ? 1 : 0;
  34         175  
255 34 100       130 if( @_ > 1 ) {
256 8 100       31 ${*$self}->{compressed} = $newval ? 1 : 0;
  8         24  
257             }
258 34         431 return $oldval;
259            
260             }
261              
262             # keep require happy
263             1;
264              
265              
266             __END__