File Coverage

blib/lib/File/Inplace.pm
Criterion Covered Total %
statement 112 112 100.0
branch 34 44 77.2
condition 7 8 87.5
subroutine 23 23 100.0
pod 9 10 90.0
total 185 197 93.9


line stmt bran cond sub pod time code
1             package File::Inplace;
2 1     1   29792 use strict;
  1         2  
  1         43  
3              
4 1     1   6 use Carp qw/carp croak/;
  1         2  
  1         88  
5 1     1   7 use File::Basename qw/dirname/;
  1         7  
  1         108  
6 1     1   1318 use File::Temp qw/tempfile/;
  1         22595  
  1         66  
7 1     1   10 use File::Copy;
  1         2  
  1         46  
8 1     1   923 use IO::File;
  1         1061  
  1         137  
9 1     1   7 use IO::Handle;
  1         1  
  1         1099  
10              
11             our $VERSION = '0.20';
12              
13             my @allowed_options = qw/chomp regex separator suffix file/;
14             my %allowed_options = map { $_ => 1 } @allowed_options;
15              
16             sub new {
17 10     10 1 3168 my $class = shift;
18 10         40 my %params = @_;
19              
20 10         35 for my $opt (keys %params) {
21 15 50       54 croak "Invalid constructor option '$opt'" unless exists $allowed_options{$opt};
22             }
23 10 50       32 croak "Required parameter 'file' not specified in constructor"
24             unless exists $params{file};
25              
26 10         26 my $self = bless \%params, $class;
27              
28 10 100       37 $params{chomp} = 1 unless exists $params{chomp};
29 10   66     99 $params{regex} = $params{regex} || $params{separator} || qr/\s+/;
30 10   100     42 $params{separator} ||= ' ';
31              
32 10 100       31 if ($self->{suffix}) {
33 3         10 $self->{backup_name} = $self->{file} . $self->{suffix};
34 3 50       13 copy($self->{file} => $self->{backup_name})
35             or croak "error creating backup: $!";
36             }
37              
38 10         761 $self->_open_input_file;
39 10         25 $self->_open_output_file;
40              
41 10         28 $self->{current_line} = undef;
42              
43 10         29 return $self;
44             }
45              
46             sub has_lines {
47 27     27 1 121 my $self = shift;
48              
49 27 100       84 return 1 if not $self->{infh}->eof();
50 7         79 return 0;
51             }
52              
53             sub next_line {
54 26     26 1 84 my $self = shift;
55              
56 26         52 $self->_write_current_line;
57              
58 26         111 $self->{current_line} = $self->_read_next_line();
59              
60 26 100       50 if (wantarray) {
61 6 100       12 if (defined $self->{current_line}) {
62 5         15 return ($self->{current_line});
63             }
64             else {
65 1         3 return ();
66             }
67             }
68              
69 20         53 return $self->{current_line};
70             }
71              
72             sub next_line_split {
73 2     2 1 20 my $self = shift;
74              
75 2         6 my $line = $self->next_line;
76              
77 2         31 return split $self->{regex}, $line;
78             }
79              
80             sub all_lines {
81 1     1 1 7 my $self = shift;
82              
83 1 50       6 croak "cannot use all_lines after any lines have been read"
84             if defined $self->{current_line};
85              
86 1         2 my @ret;
87 1         2 while (1) {
88 4         11 my $line = $self->_read_next_line;
89 4 100       10 last unless defined $line;
90 3         5 push @ret, $line;
91             }
92              
93 1         7 return @ret;
94             }
95              
96             sub replace_line {
97 9     9 1 62 my $self = shift;
98              
99 9 100       23 if (@_ == 1) {
100 7         24 $self->{current_line} = shift;
101             }
102             else {
103 2         33 $self->{current_line} = join($self->{separator}, @_);
104             }
105             }
106              
107             sub replace_lines {
108 1     1 0 6 my $self = shift;
109 1         5 my @lines = @_;
110              
111 1         2 my $fh = $self->{outfh};
112 1         5 for my $line (@lines) {
113 3         18 $fh->print($line);
114 3 50       30 if ($self->{chomp}) {
115 3         8 $fh->print($/);
116             }
117             }
118             }
119              
120             sub _open_input_file {
121 10     10   13 my $self = shift;
122              
123 10         70 $self->{infh} = new IO::File("<$self->{file}");
124 10 50       693 croak "open $self->{file}: $!" if not $self->{infh};
125             }
126              
127             sub _open_output_file {
128 10     10   14 my $self = shift;
129              
130 10         379 my $dir = dirname $self->{file};
131 10         39 my ($tmpfh, $tmpname) = tempfile(DIR => $dir);
132 10         3066 $self->{outfh} = bless $tmpfh, "IO::Handle";
133 10         25 $self->{tmpfile} = $tmpname;
134             }
135              
136             sub _write_current_line {
137 33     33   35 my $self = shift;
138              
139 33         41 my $fh = $self->{outfh};
140 33 100       82 if (defined $self->{current_line}) {
141 22         63 $fh->print($self->{current_line});
142 22 100       224 if ($self->{chomp}) {
143 19         63 $fh->print($/);
144             }
145             }
146             }
147              
148             sub _read_next_line {
149 30     30   35 my $self = shift;
150              
151 30         41 my $fh = $self->{infh};
152 30 50       57 return undef unless $fh;
153 30         793 my $line = $fh->getline;
154 30 100       885 if (not defined $line) {
155 2         8 $fh->close;
156 2         29 delete $self->{infh};
157             }
158              
159 30 100 100     139 if (defined $line and $self->{chomp}) {
160 25         36 chomp $line;
161             }
162              
163 30         76 return $line;
164             }
165              
166             sub commit {
167 6     6 1 20 my $self = shift;
168              
169 6         13 $self->_write_current_line;
170              
171 6 50       385 rename $self->{tmpfile} => $self->{file}
172             or croak "Can't rename $self->{tmpname} => $self->{file}: $!";
173              
174 6         16 $self->_close_all();
175             }
176              
177             sub commit_to_backup {
178 1     1 1 2 my $self = shift;
179              
180 1         4 $self->_write_current_line;
181              
182 1 50       11 croak "cannot commit_to_backup if no backup file is in use"
183             unless $self->{backup_name};
184              
185 1 50       80 rename $self->{tmpfile} => $self->{backup_name}
186             or croak "Can't rename $self->{tmpname} => $self->{backup_name}: $!";
187              
188 1         3 $self->_close_all();
189             }
190              
191             sub rollback {
192 2     2 1 4 my $self = shift;
193              
194 2         5 $self->_close_all();
195 2         224 unlink $self->{tmpfile};
196             }
197              
198             sub DESTROY {
199 10     10   4918 my $self = shift;
200              
201 10         21 $self->_close_all();
202 10         393 unlink $self->{tmpfile};
203             }
204              
205             sub _close_all {
206 19     19   25 my $self = shift;
207              
208 19         32 for my $handle (qw/infh outfh/) {
209 38 100       434 $self->{$handle}->close()
210             if $self->{$handle};
211             }
212             }
213              
214             1;
215             __END__