File-Inplace-0.20/0000755001211700121200000000000010176603742015047 5ustar cturnercturner00000000000000File-Inplace-0.20/t/0000755001211700121200000000000010176603742015312 5ustar cturnercturner00000000000000File-Inplace-0.20/t/abc.txt0000644001211700121200000000000610176036501016565 0ustar cturnercturner00000000000000a b c File-Inplace-0.20/t/File-Inplace.t0000644001211700121200000001243310176246277017740 0ustar cturnercturner00000000000000use strict; use Test::More tests => 21; use File::Copy; use File::Spec; BEGIN { use_ok('File::Inplace') }; my $data_dir = sub { File::Spec->catfile('t', shift); }; my $abc_file = $data_dir->("abc.txt"); my $cba_file = $data_dir->("cba.txt"); my $adc_file = $data_dir->("adc.txt"); my $csv_file = $data_dir->("csv.txt"); my $csv_squared_file = $data_dir->("csv-squared.txt"); # Test case 1, make sure a change, with a backup, works { copy($abc_file, $data_dir->("test01.txt")) or die "copy: $!"; my $edit = new File::Inplace(file => $data_dir->("test01.txt"), suffix => ".bak"); quick_change($edit, b => 'd'); $edit->commit; ok(same_file($data_dir->("test01.txt"), $adc_file), "file changed properly"); ok(same_file($abc_file, $data_dir->("test01.txt.bak")), "backup unchanged"); } # Test case 2, make sure a change, with a backup, can save changes only to the backup { copy($abc_file, $data_dir->("test02.txt")) or die "copy: $!"; my $edit = new File::Inplace(file => $data_dir->("test02.txt"), suffix => ".bak"); quick_change($edit, b => 'd'); $edit->commit_to_backup; ok(same_file($data_dir->("test02.txt"), $abc_file), "original unchanged"); ok(same_file($adc_file, $data_dir->("test02.txt.bak")), "changes written to backup"); } # Test case 3, make sure a rollback works { copy($abc_file, $data_dir->("test03.txt")) or die "copy: $!"; my $edit = new File::Inplace(file => $data_dir->("test03.txt"), suffix => ".bak"); quick_change($edit, b => 'd'); $edit->rollback; ok(same_file($data_dir->("test03.txt"), $abc_file), "original unchanged"); ok(same_file($abc_file, $data_dir->("test03.txt.bak")), "backup unchanged"); } # Test case 4, make sure an edit w/o a backup works { copy($abc_file, $data_dir->("test04.txt")) or die "copy: $!"; my $edit = new File::Inplace(file => $data_dir->("test04.txt")); quick_change($edit, b => 'd'); $edit->commit; ok(same_file($data_dir->("test04.txt"), $adc_file), "original changed"); ok(file_not_there($data_dir->("test04.txt.bak")), "backup does not exist"); } # Test case 5, make sure an rolled back edit w/o a backup works { copy($abc_file, $data_dir->("test05.txt")) or die "copy: $!"; my $edit = new File::Inplace(file => $data_dir->("test05.txt")); quick_change($edit, b => 'd'); $edit->rollback; ok(same_file($data_dir->("test05.txt"), $abc_file), "original unchanged"); ok(file_not_there($data_dir->("test05.txt.bak")), "backup does not exist"); } # Test case 6, make sure non-chomping works { copy($abc_file, $data_dir->("test06.txt")) or die "copy: $!"; my $edit = new File::Inplace(file => $data_dir->("test06.txt"), chomp => 0); quick_change($edit, b => 'd'); $edit->commit; ok(same_file($data_dir->("test06.txt"), $abc_file), "original unchanged"); ok(file_not_there($data_dir->("test06.txt.bak")), "backup does not exist"); } # Test case 7, make sure non-chomping works { copy($csv_file, $data_dir->("test07.txt")) or die "copy: $!"; my $edit = new File::Inplace(file => $data_dir->("test07.txt"), separator => ","); while ($edit->has_lines) { my @fields = $edit->next_line_split; $fields[$_] **= 2 for 0 .. $#fields; $edit->replace_line(@fields); } $edit->commit; ok(same_file($data_dir->("test07.txt"), $csv_squared_file), "csv edit successful"); ok(file_not_there($data_dir->("test07.txt.bak")), "backup does not exist"); } # Test case 8, array access works { copy($abc_file, $data_dir->("test08.txt")) or die "copy: $!"; my $edit = new File::Inplace(file => $data_dir->("test08.txt")); my @lines = $edit->all_lines; $edit->replace_lines(reverse @lines); $edit->commit; ok(same_file($data_dir->("test08.txt"), $cba_file), "array edit successful"); ok(file_not_there($data_dir->("test08.txt.bak")), "backup does not exist"); } # Test case 9, simplified interface works { copy($abc_file, $data_dir->("test09.txt")) or die "copy: $!"; my $edit = new File::Inplace(file => $data_dir->("test09.txt")); while (my ($line) = $edit->next_line) { if ($line eq 'b') { $edit->replace_line('d'); } } $edit->commit; ok(same_file($data_dir->("test09.txt"), $adc_file), "file changed properly"); ok(file_not_there($data_dir->("test09.txt.bak")), "backup does not exist"); } # Test case 10, abort changes half way through { copy($abc_file, $data_dir->("test10.txt")) or die "copy: $!"; my $edit = new File::Inplace(file => $data_dir->("test10.txt")); # this simulates, say, an exception being raised halfway through a change while (my ($line) = $edit->next_line) { if ($line eq 'a') { $edit->replace_line('x'); } if ($line eq 'b') { last; } } undef $edit; ok(same_file($data_dir->("test10.txt"), $abc_file), "file unchanged"); ok(file_not_there($data_dir->("test10.txt.bak")), "backup does not exist"); } sub quick_change { my $edit = shift; my $from = shift; my $to = shift; while ($edit->has_lines) { my $line = $edit->next_line; if ($line eq $from) { $edit->replace_line($to); } } } sub same_file { my $file_a = shift; my $file_b = shift; local $/ = undef; open FHA, "<$file_a" or die "open $file_a: $!"; my $a = ; open FHB, "<$file_b" or die "open $file_b: $!"; my $b = ; close FHA; close FHB; return $a eq $b; } sub file_not_there { my $file = shift; return not -e $file; } File-Inplace-0.20/t/adc.txt0000644001211700121200000000000610176071635016576 0ustar cturnercturner00000000000000a d c File-Inplace-0.20/t/cba.txt0000644001211700121200000000000610176173545016577 0ustar cturnercturner00000000000000c b a File-Inplace-0.20/t/csv-squared.txt0000644001211700121200000000001710176100115020270 0ustar cturnercturner000000000000001,4,9 16,25,36 File-Inplace-0.20/t/csv.txt0000644001211700121200000000001410176100074016627 0ustar cturnercturner000000000000001,2,3 4,5,6 File-Inplace-0.20/MANIFEST0000644001211700121200000000032210176603624016174 0ustar cturnercturner00000000000000Changes Makefile.PL MANIFEST README t/File-Inplace.t lib/File/Inplace.pm META.yml Module meta-data (added by MakeMaker) t/abc.txt t/adc.txt t/cba.txt t/csv-squared.txt t/csv.txt File-Inplace-0.20/Changes0000644001211700121200000000024210176036400016327 0ustar cturnercturner00000000000000Revision history for Perl extension File::Inplace. 0.01 Wed Jan 26 19:59:44 2005 - original version; created by h2xs 1.23 with options -AX -n File::Inplace File-Inplace-0.20/META.yml0000644001211700121200000000046410176603742016324 0ustar cturnercturner00000000000000# http://module-build.sourceforge.net/META-spec.html #XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX# name: File-Inplace version: 0.20 version_from: lib/File/Inplace.pm installdirs: site requires: distribution_type: module generated_by: ExtUtils::MakeMaker version 6.17 File-Inplace-0.20/Makefile.PL0000644001211700121200000000105110176603734017017 0ustar cturnercturner00000000000000use 5.006000; use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. WriteMakefile( NAME => 'File::Inplace', VERSION_FROM => 'lib/File/Inplace.pm', # finds $VERSION PREREQ_PM => {}, # e.g., Module::Name => 1.1 ($] >= 5.005 ? ## Add these new keywords supported since 5.005 (ABSTRACT_FROM => 'lib/File/Inplace.pm', # retrieve abstract from module AUTHOR => 'Chip Turner ') : ()), ); File-Inplace-0.20/lib/0000755001211700121200000000000010176603742015615 5ustar cturnercturner00000000000000File-Inplace-0.20/lib/File/0000755001211700121200000000000010176603742016474 5ustar cturnercturner00000000000000File-Inplace-0.20/lib/File/Inplace.pm0000644001211700121200000001577510176602263020421 0ustar cturnercturner00000000000000package File::Inplace; use strict; use Carp qw/carp croak/; use File::Basename qw/dirname/; use File::Temp qw/tempfile/; use File::Copy; use IO::File; use IO::Handle; our $VERSION = '0.20'; my @allowed_options = qw/chomp regex separator suffix file/; my %allowed_options = map { $_ => 1 } @allowed_options; sub new { my $class = shift; my %params = @_; for my $opt (keys %params) { croak "Invalid constructor option '$opt'" unless exists $allowed_options{$opt}; } croak "Required parameter 'file' not specified in constructor" unless exists $params{file}; my $self = bless \%params, $class; $params{chomp} = 1 unless exists $params{chomp}; $params{regex} = $params{regex} || $params{separator} || qr/\s+/; $params{separator} ||= ' '; if ($self->{suffix}) { $self->{backup_name} = $self->{file} . $self->{suffix}; copy($self->{file} => $self->{backup_name}) or croak "error creating backup: $!"; } $self->_open_input_file; $self->_open_output_file; $self->{current_line} = undef; return $self; } sub has_lines { my $self = shift; return 1 if not $self->{infh}->eof(); return 0; } sub next_line { my $self = shift; $self->_write_current_line; $self->{current_line} = $self->_read_next_line(); if (wantarray) { if (defined $self->{current_line}) { return ($self->{current_line}); } else { return (); } } return $self->{current_line}; } sub next_line_split { my $self = shift; my $line = $self->next_line; return split $self->{regex}, $line; } sub all_lines { my $self = shift; croak "cannot use all_lines after any lines have been read" if defined $self->{current_line}; my @ret; while (1) { my $line = $self->_read_next_line; last unless defined $line; push @ret, $line; } return @ret; } sub replace_line { my $self = shift; if (@_ == 1) { $self->{current_line} = shift; } else { $self->{current_line} = join($self->{separator}, @_); } } sub replace_lines { my $self = shift; my @lines = @_; my $fh = $self->{outfh}; for my $line (@lines) { $fh->print($line); if ($self->{chomp}) { $fh->print($/); } } } sub _open_input_file { my $self = shift; $self->{infh} = new IO::File("<$self->{file}"); croak "open $self->{file}: $!" if not $self->{infh}; } sub _open_output_file { my $self = shift; my $dir = dirname $self->{file}; my ($tmpfh, $tmpname) = tempfile(DIR => $dir); $self->{outfh} = bless $tmpfh, "IO::Handle"; $self->{tmpfile} = $tmpname; } sub _write_current_line { my $self = shift; my $fh = $self->{outfh}; if (defined $self->{current_line}) { $fh->print($self->{current_line}); if ($self->{chomp}) { $fh->print($/); } } } sub _read_next_line { my $self = shift; my $fh = $self->{infh}; return undef unless $fh; my $line = $fh->getline; if (not defined $line) { $fh->close; delete $self->{infh}; } if (defined $line and $self->{chomp}) { chomp $line; } return $line; } sub commit { my $self = shift; $self->_write_current_line; rename $self->{tmpfile} => $self->{file} or croak "Can't rename $self->{tmpname} => $self->{file}: $!"; $self->_close_all(); } sub commit_to_backup { my $self = shift; $self->_write_current_line; croak "cannot commit_to_backup if no backup file is in use" unless $self->{backup_name}; rename $self->{tmpfile} => $self->{backup_name} or croak "Can't rename $self->{tmpname} => $self->{backup_name}: $!"; $self->_close_all(); } sub rollback { my $self = shift; $self->_close_all(); unlink $self->{tmpfile}; } sub DESTROY { my $self = shift; $self->_close_all(); unlink $self->{tmpfile}; } sub _close_all { my $self = shift; for my $handle (qw/infh outfh/) { $self->{$handle}->close() if $self->{$handle}; } } 1; __END__ =head1 NAME File::Inplace - Perl module for in-place editing of files =head1 SYNOPSIS use File::Inplace; my $editor = new File::Inplace(file => "file.txt"); while (my ($line) = $editor->next_line) { $editor->replace_line(reverse $line); } $editor->commit; =head1 DESCRIPTION File::Inplace is a perl module intended to ease the common task of editing a file in-place. Inspired by variations of perl's -i option, this module is intended for somewhat more structured and reusable editing than command line perl typically allows. File::Inplace endeavors to guarantee file integrity; that is, either all of the changes made will be saved to the file, or none will. It also offers functionality such as backup creation, automatic field splitting per-line, automatic chomping/unchomping, and aborting edits partially through without affecting the original file. =head1 CONSTRUCTOR File::Inplace offers one constructor that accepts a number of parameters, one of which is required. =over 4 =item File::Inplace->new(file => "filename", ...) =over 4 =item file The one required parameter. This is the name of the file to edit. =item suffix The suffix for backup files. If not specified, no backups are made. =item chomp If set to zero, then automatic chomping will not be performed. Newlines (actually, the contents of $/) will remain in strings returned from C. Additionally, the contents of $/ will not be appended when replacing lines. =item regex If specified, then each line will be split by this parameter when using C method. If unspecified, then this defaults to \s+. =item separator The default character used to join each line when replace_line is invoked with a list instead of a single value. Defaults to a single space. =back =head1 INSTANCE METHODS =item $editor->next_line () In scalar context, it returns the next line of the input file, or undef if there is no line. In an array context, it returns a single value of the line, or an empty list if there is no line. =item $editor->replace_line (value) Replaces the current line in the output file with the specified value. If passed a list, then each valie is joined by the C specified at construction time. =item $editor->next_line_split () Line C, except splits based on the C specified in the constructor. =item $editor->has_lines () Returns true if the file contains any further lines. =item $editor->all_lines () Returns an array of all lines in the file being edited. =item $editor->replace_all_lines (@lines) Replaces B remaining lines in the file with the specified @lines. =item $editor->commit () Completes the edit operation and saves the changes to the edited file. =item $editor->rollback () Aborts the edit process. =item $editor->commit_to_backup () Saves edits to the backup file instead of the original file. =back =head1 AUTHOR Chip Turner, Echipt@cpan.orgE =head1 COPYRIGHT AND LICENSE Copyright (C) 2005 by Chip Turner This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.6.0 or, at your option, any later version of Perl 5 you may have available. =cut File-Inplace-0.20/README0000644001211700121200000000223110176036400015714 0ustar cturnercturner00000000000000File-Inplace version 0.01 ========================= The README is used to introduce the module and provide instructions on how to install the module, any machine dependencies it may have (for example C compilers and installed libraries) and any other information that should be provided before the module is installed. A README file is required for CPAN modules since CPAN extracts the README file from a module distribution so that people browsing the archive can use it get an idea of the modules uses. It is usually a good idea to provide version information here so that people can decide whether fixes for the module are worth downloading. INSTALLATION To install this module type the following: perl Makefile.PL make make test make install DEPENDENCIES This module requires these other modules and libraries: blah blah blah COPYRIGHT AND LICENCE Put the correct copyright and licence information here. Copyright (C) 2005 by Chip Turner This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.5 or, at your option, any later version of Perl 5 you may have available.