| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package List::RewriteElements; | 
| 2 |  |  |  |  |  |  | #$Id: RewriteElements.pm 1123 2007-01-23 03:39:35Z jimk $ | 
| 3 |  |  |  |  |  |  | $VERSION = 0.09; | 
| 4 | 7 |  |  | 7 |  | 8789 | use strict; | 
|  | 7 |  |  |  |  | 17 |  | 
|  | 7 |  |  |  |  | 494 |  | 
| 5 | 7 |  |  | 7 |  | 42 | use warnings; | 
|  | 7 |  |  |  |  | 25 |  | 
|  | 7 |  |  |  |  | 243 |  | 
| 6 | 7 |  |  | 7 |  | 53 | use Carp; | 
|  | 7 |  |  |  |  | 13 |  | 
|  | 7 |  |  |  |  | 598 |  | 
| 7 | 7 |  |  | 7 |  | 40 | use Cwd; | 
|  | 7 |  |  |  |  | 21 |  | 
|  | 7 |  |  |  |  | 503 |  | 
| 8 | 7 |  |  | 7 |  | 42 | use File::Basename; | 
|  | 7 |  |  |  |  | 13 |  | 
|  | 7 |  |  |  |  | 647 |  | 
| 9 | 7 |  |  | 7 |  | 6958 | use File::Copy; | 
|  | 7 |  |  |  |  | 19714 |  | 
|  | 7 |  |  |  |  | 431 |  | 
| 10 | 7 |  |  | 7 |  | 43 | use File::Spec; | 
|  | 7 |  |  |  |  | 12 |  | 
|  | 7 |  |  |  |  | 126 |  | 
| 11 | 7 |  |  | 7 |  | 9273 | use Tie::File; | 
|  | 7 |  |  |  |  | 181316 |  | 
|  | 7 |  |  |  |  | 10261 |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | sub new { | 
| 14 | 32 |  |  | 32 | 1 | 402030 | my ($class, $argsref) = @_; | 
| 15 | 32 | 100 |  |  |  | 375 | croak "Hash ref passed to constructor must contain 'body_rule' element" | 
| 16 |  |  |  |  |  |  | unless defined $argsref->{body_rule}; | 
| 17 | 31 | 100 |  |  |  | 292 | croak "'body_rule' element value must be a code ref" | 
| 18 |  |  |  |  |  |  | unless ref($argsref->{body_rule}) eq 'CODE'; | 
| 19 | 30 | 100 | 100 |  |  | 394 | croak "Hash ref passed to constructor must have either a 'file' element or a 'list' element" | 
| 20 |  |  |  |  |  |  | unless (defined $argsref->{file} or defined $argsref->{list}); | 
| 21 | 29 | 100 | 100 |  |  | 479 | croak "'file' element passed to constructor not located" | 
| 22 |  |  |  |  |  |  | if (defined $argsref->{file} and not -f $argsref->{file}); | 
| 23 | 28 | 100 | 100 |  |  | 555 | croak "'list' element passed to constructor must be array ref" | 
|  |  |  | 66 |  |  |  |  | 
| 24 |  |  |  |  |  |  | if  ( defined $argsref->{list} and | 
| 25 |  |  |  |  |  |  | ( | 
| 26 |  |  |  |  |  |  | (not ref($argsref->{list})) or | 
| 27 |  |  |  |  |  |  | (ref($argsref->{list}) ne 'ARRAY') | 
| 28 |  |  |  |  |  |  | ) | 
| 29 |  |  |  |  |  |  | ); | 
| 30 | 26 | 100 | 100 |  |  | 359 | croak "'body_suppress' element passed to constructor must be code ref" | 
|  |  |  | 66 |  |  |  |  | 
| 31 |  |  |  |  |  |  | if  ( defined $argsref->{body_suppress} and | 
| 32 |  |  |  |  |  |  | ( | 
| 33 |  |  |  |  |  |  | (not ref($argsref->{body_suppress})) or | 
| 34 |  |  |  |  |  |  | (ref($argsref->{body_suppress}) ne 'CODE') | 
| 35 |  |  |  |  |  |  | ) | 
| 36 |  |  |  |  |  |  | ); | 
| 37 | 24 | 100 | 100 |  |  | 505 | croak "'header_rule' element passed to constructor must be code ref" | 
|  |  |  | 66 |  |  |  |  | 
| 38 |  |  |  |  |  |  | if  ( defined $argsref->{header_rule} and | 
| 39 |  |  |  |  |  |  | ( | 
| 40 |  |  |  |  |  |  | (not ref($argsref->{header_rule})) or | 
| 41 |  |  |  |  |  |  | (ref($argsref->{header_rule}) ne 'CODE') | 
| 42 |  |  |  |  |  |  | ) | 
| 43 |  |  |  |  |  |  | ); | 
| 44 | 22 | 100 | 100 |  |  | 1124 | croak "If 'header_suppress' criterion is supplied, a 'header_rule' element must be supplied as well" | 
| 45 |  |  |  |  |  |  | if  ( defined $argsref->{header_suppress} and | 
| 46 |  |  |  |  |  |  | ! defined $argsref->{header_rule} | 
| 47 |  |  |  |  |  |  | ); | 
| 48 | 21 | 100 | 100 |  |  | 357 | croak "'header_suppress' element passed to constructor must be code ref" | 
|  |  |  | 66 |  |  |  |  | 
| 49 |  |  |  |  |  |  | if  ( defined $argsref->{header_suppress} and | 
| 50 |  |  |  |  |  |  | ( | 
| 51 |  |  |  |  |  |  | (not ref($argsref->{header_suppress})) or | 
| 52 |  |  |  |  |  |  | (ref($argsref->{header_suppress}) ne 'CODE') | 
| 53 |  |  |  |  |  |  | ) | 
| 54 |  |  |  |  |  |  | ); | 
| 55 |  |  |  |  |  |  |  | 
| 56 | 19 | 100 |  |  |  | 73 | if ($argsref->{file}) { | 
| 57 | 5 |  |  |  |  | 14 | my @elements; | 
| 58 | 5 | 50 |  |  |  | 108 | tie @elements, 'Tie::File', $argsref->{file}, recsep => $/ | 
| 59 |  |  |  |  |  |  | or croak "Unable to tie to $argsref->{file}"; | 
| 60 | 5 |  |  |  |  | 1639 | $argsref->{working} = \@elements; | 
| 61 |  |  |  |  |  |  | } else { | 
| 62 | 14 |  |  |  |  | 56 | $argsref->{working} = $argsref->{list}; | 
| 63 |  |  |  |  |  |  | } | 
| 64 |  |  |  |  |  |  |  | 
| 65 | 19 |  |  |  |  | 130 | my $self = bless ($argsref, $class); | 
| 66 |  |  |  |  |  |  |  | 
| 67 | 19 |  |  |  |  | 29 | $self->{rows_in} = scalar(@{$self->{working}}); | 
|  | 19 |  |  |  |  | 133 |  | 
| 68 | 19 | 100 |  |  |  | 1434 | if (defined $self->{header_rule}) { | 
| 69 | 8 |  |  |  |  | 29 | $self->{records_in} = $self->{rows_in} - 1; | 
| 70 |  |  |  |  |  |  | } else { | 
| 71 | 11 |  |  |  |  | 45 | $self->{records_in} = $self->{rows_in}; | 
| 72 |  |  |  |  |  |  | } | 
| 73 |  |  |  |  |  |  | # Next attributes are initialized to empty strings because their value | 
| 74 |  |  |  |  |  |  | # is not fixed until after generate_output() has been called. | 
| 75 | 19 |  |  |  |  | 70 | $self->{output_path} = q{}; | 
| 76 | 19 |  |  |  |  | 70 | $self->{output_basename} = q{}; | 
| 77 |  |  |  |  |  |  | # Next attributes are initialized to zero because their value | 
| 78 |  |  |  |  |  |  | # is not fixed until after generate_output() has been called. | 
| 79 | 19 |  |  |  |  | 51 | $self->{rows_out} = 0; | 
| 80 | 19 |  |  |  |  | 41 | $self->{records_out} = 0; | 
| 81 | 19 |  |  |  |  | 45 | $self->{records_changed} = 0; | 
| 82 | 19 |  |  |  |  | 42 | $self->{records_unchanged} = 0; | 
| 83 | 19 |  |  |  |  | 47 | $self->{records_deleted} = 0; | 
| 84 | 19 |  |  |  |  | 78 | return $self; | 
| 85 |  |  |  |  |  |  | } | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | sub generate_output { | 
| 88 | 19 |  |  | 19 | 1 | 17258 | my $self = shift; | 
| 89 | 19 | 100 | 100 |  |  | 199 | if  ( !                 # print to STDOUT | 
| 90 |  |  |  |  |  |  | ( | 
| 91 |  |  |  |  |  |  | defined $self->{output_file}   or | 
| 92 |  |  |  |  |  |  | defined $self->{output_suffix} | 
| 93 |  |  |  |  |  |  | ) | 
| 94 |  |  |  |  |  |  | ) { | 
| 95 | 11 |  |  |  |  | 42 | $self->_handler_control(); | 
| 96 |  |  |  |  |  |  | } else {                # print to file | 
| 97 | 8 |  |  |  |  | 44 | my $outfile; | 
| 98 | 8 | 100 |  |  |  | 73 | if (defined $self->{output_file}) { | 
| 99 | 7 |  |  |  |  | 35 | $outfile = $self->{output_file}; | 
| 100 |  |  |  |  |  |  | } else { | 
| 101 | 1 |  |  |  |  | 13380 | $outfile = File::Spec->catfile( ( cwd() ), | 
| 102 |  |  |  |  |  |  | basename($self->{file}) . $self->{output_suffix} ); | 
| 103 |  |  |  |  |  |  | } | 
| 104 | 8 | 50 |  |  |  | 1443 | open my $OUT, ">$outfile" | 
| 105 |  |  |  |  |  |  | or croak "Unable to open $outfile for writing"; | 
| 106 | 8 |  |  |  |  | 82 | my $oldfh = select($OUT); | 
| 107 | 8 |  |  |  |  | 63 | $self->_handler_control(); | 
| 108 | 8 | 50 |  |  |  | 1483 | close $OUT | 
| 109 |  |  |  |  |  |  | or croak "Unable to close $outfile after writing"; | 
| 110 | 8 |  |  |  |  | 42 | select $oldfh; | 
| 111 | 8 |  |  |  |  | 40 | $self->{output_path} = $outfile; | 
| 112 | 8 |  |  |  |  | 929 | $self->{output_basename} = basename($self->{output_path}); | 
| 113 |  |  |  |  |  |  | } | 
| 114 | 19 |  |  |  |  | 235 | $self->{records_out} = $self->{records_in} - $self->{records_deleted}; | 
| 115 | 19 |  |  |  |  | 50 | $self->{records_unchanged} = | 
| 116 |  |  |  |  |  |  | $self->{records_out} - $self->{records_changed}; | 
| 117 | 19 | 100 |  |  |  | 237 | if (! defined $self->{header_rule}) { | 
| 118 | 11 |  |  |  |  | 42 | $self->{rows_out} = $self->{records_out}; | 
| 119 |  |  |  |  |  |  | } else { | 
| 120 | 8 | 100 |  |  |  | 34 | if ($self->{header_status} != -1) { | 
| 121 | 6 |  |  |  |  | 20 | $self->{rows_out} = $self->{records_out} + 1; | 
| 122 |  |  |  |  |  |  | } else { | 
| 123 | 2 |  |  |  |  | 7 | $self->{rows_out} = $self->{records_out}; | 
| 124 |  |  |  |  |  |  | } | 
| 125 |  |  |  |  |  |  | } | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | sub _handler_control { | 
| 129 | 19 |  |  | 19 |  | 43 | my $self = shift; | 
| 130 | 19 | 100 |  |  |  | 72 | if (! defined $self->{header_rule}) { | 
| 131 | 11 |  |  |  |  | 94 | $self->_body_rule_handler(); | 
| 132 |  |  |  |  |  |  | } else { | 
| 133 | 8 |  |  |  |  | 33 | $self->_header_body_rule_handler(); | 
| 134 |  |  |  |  |  |  | } | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | sub _body_rule_handler { | 
| 138 | 19 |  |  | 19 |  | 35 | my $self = shift; | 
| 139 | 19 |  |  |  |  | 136 | RECORD:  foreach my $el (@{$self->{working}}) { | 
|  | 19 |  |  |  |  | 114 |  | 
| 140 | 178 |  |  |  |  | 1214 | chomp $el; | 
| 141 | 178 | 100 |  |  |  | 14926 | if (defined $self->{body_suppress}) { | 
| 142 | 40 | 100 |  |  |  | 40 | unless (defined (&{$self->{body_suppress}}($el))) { | 
|  | 40 |  |  |  |  | 108 |  | 
| 143 | 6 |  |  |  |  | 40 | $self->{records_deleted}++; | 
| 144 | 6 |  |  |  |  | 21 | next RECORD; | 
| 145 |  |  |  |  |  |  | } | 
| 146 |  |  |  |  |  |  | } | 
| 147 | 172 |  |  |  |  | 393 | my $newel = &{$self->{body_rule}}($el); | 
|  | 172 |  |  |  |  | 561 |  | 
| 148 | 172 |  |  |  |  | 2458 | print "$newel\n"; | 
| 149 | 172 | 100 |  |  |  | 6520 | $self->{records_changed}++ if $el ne $newel; | 
| 150 |  |  |  |  |  |  | } | 
| 151 |  |  |  |  |  |  | } | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | sub _header_body_rule_handler { | 
| 154 | 8 |  |  | 8 |  | 17 | my $self = shift; | 
| 155 | 8 |  |  |  |  | 69 | $self->{header_status} = 0; # header present, as yet unchanged | 
| 156 | 8 |  |  |  |  | 13 | my $header = shift(@{$self->{working}}); | 
|  | 8 |  |  |  |  | 28 |  | 
| 157 | 8 |  |  |  |  | 327 | chomp $header; | 
| 158 | 8 | 100 |  |  |  | 31 | if (defined $self->{header_suppress}) { | 
| 159 | 5 | 100 |  |  |  | 13 | if (defined (&{$self->{header_suppress}}($header))) { | 
|  | 5 |  |  |  |  | 20 |  | 
| 160 | 3 |  |  |  |  | 24 | my $newheader = &{$self->{header_rule}}($header); | 
|  | 3 |  |  |  |  | 11 |  | 
| 161 | 3 |  |  |  |  | 44 | print "$newheader\n"; | 
| 162 | 3 | 100 |  |  |  | 22 | $self->{header_status} = 1 if $header ne $newheader; | 
| 163 |  |  |  |  |  |  | # header changed | 
| 164 |  |  |  |  |  |  | } else { | 
| 165 | 2 |  |  |  |  | 17 | $self->{header_status} = -1;  # header suppressed | 
| 166 |  |  |  |  |  |  | } | 
| 167 |  |  |  |  |  |  | } else { | 
| 168 | 3 |  |  |  |  | 4 | my $newheader = &{$self->{header_rule}}($header); | 
|  | 3 |  |  |  |  | 11 |  | 
| 169 | 3 |  |  |  |  | 49 | print "$newheader\n"; | 
| 170 | 3 | 100 |  |  |  | 22 | $self->{header_status} = 1 if $header ne $newheader; | 
| 171 |  |  |  |  |  |  | # header changed | 
| 172 |  |  |  |  |  |  | } | 
| 173 | 8 |  |  |  |  | 33 | $self->_body_rule_handler(); | 
| 174 |  |  |  |  |  |  | } | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | sub get_output_path { | 
| 177 | 2 |  |  | 2 | 1 | 654 | my $self = shift; | 
| 178 | 2 |  |  |  |  | 30 | return $self->{output_path}; | 
| 179 |  |  |  |  |  |  | } | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | sub get_output_basename { | 
| 182 | 3 |  |  | 3 | 1 | 91 | my $self = shift; | 
| 183 | 3 |  |  |  |  | 105 | return $self->{output_basename}; | 
| 184 |  |  |  |  |  |  | } | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | sub get_total_rows { | 
| 187 | 13 |  |  | 13 | 1 | 5363 | my $self = shift; | 
| 188 | 13 |  |  |  |  | 84 | return $self->{rows_out}; | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | sub get_total_records { | 
| 192 | 13 |  |  | 13 | 1 | 32 | my $self = shift; | 
| 193 | 13 |  |  |  |  | 74 | return $self->{records_out}; | 
| 194 |  |  |  |  |  |  | } | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | sub get_records_changed { | 
| 197 | 13 |  |  | 13 | 1 | 37 | my $self = shift; | 
| 198 | 13 |  |  |  |  | 76 | return $self->{records_changed}; | 
| 199 |  |  |  |  |  |  | } | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | sub get_records_unchanged { | 
| 202 | 13 |  |  | 13 | 1 | 34 | my $self = shift; | 
| 203 | 13 |  |  |  |  | 83 | return $self->{records_unchanged}; | 
| 204 |  |  |  |  |  |  | } | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | sub get_records_deleted { | 
| 207 | 13 |  |  | 13 | 1 | 943 | my $self = shift; | 
| 208 | 13 |  |  |  |  | 112 | return $self->{records_deleted}; | 
| 209 |  |  |  |  |  |  | } | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | sub get_header_status { | 
| 212 | 10 |  |  | 10 | 1 | 21 | my $self = shift; | 
| 213 | 10 |  |  |  |  | 76 | return $self->{header_status}; | 
| 214 |  |  |  |  |  |  | } | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | 1; | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | #################### DOCUMENTATION ################### | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | =head1 NAME | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | List::RewriteElements - Create a new list by rewriting elements of a first list | 
| 224 |  |  |  |  |  |  |  | 
| 225 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | use List::RewriteElements; | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | =head2 Constructor | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | Simplest case:  Input from array, output to STDOUT. | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | $lre = List::RewriteElements->new( { | 
| 234 |  |  |  |  |  |  | list        => \@source, | 
| 235 |  |  |  |  |  |  | body_rule   => sub { | 
| 236 |  |  |  |  |  |  | my $record = shift; | 
| 237 |  |  |  |  |  |  | $record .= q{additional field}; | 
| 238 |  |  |  |  |  |  | }, | 
| 239 |  |  |  |  |  |  | } ); | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | Input from file, output to STDOUT: | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | $lre = List::RewriteElements->new( { | 
| 244 |  |  |  |  |  |  | file        => "/path/to/source/file", | 
| 245 |  |  |  |  |  |  | body_rule   => sub { | 
| 246 |  |  |  |  |  |  | my $record = shift; | 
| 247 |  |  |  |  |  |  | $record .= q{,additional field}; | 
| 248 |  |  |  |  |  |  | }, | 
| 249 |  |  |  |  |  |  | } ); | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | Provide a different rule for the first element in the list: | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | $lre = List::RewriteElements->new( { | 
| 254 |  |  |  |  |  |  | file        => "/path/to/source/file", | 
| 255 |  |  |  |  |  |  | header_rule => sub { | 
| 256 |  |  |  |  |  |  | my $record = shift; | 
| 257 |  |  |  |  |  |  | $record .= q{,ADDITIONAL HEADER}; | 
| 258 |  |  |  |  |  |  | }, | 
| 259 |  |  |  |  |  |  | body_rule   => sub { | 
| 260 |  |  |  |  |  |  | my $record = shift; | 
| 261 |  |  |  |  |  |  | $record .= q{,additional field}; | 
| 262 |  |  |  |  |  |  | }, | 
| 263 |  |  |  |  |  |  | } ); | 
| 264 |  |  |  |  |  |  |  | 
| 265 |  |  |  |  |  |  | Input from file, output to file: | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | $lre = List::RewriteElements->new( { | 
| 268 |  |  |  |  |  |  | file        => "/path/to/source/file", | 
| 269 |  |  |  |  |  |  | body_rule   => sub { | 
| 270 |  |  |  |  |  |  | my $record = shift; | 
| 271 |  |  |  |  |  |  | $record .= q{additional field}; | 
| 272 |  |  |  |  |  |  | }, | 
| 273 |  |  |  |  |  |  | output_file => "/path/to/output/file", | 
| 274 |  |  |  |  |  |  | } ); | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | To name output file, just provide a suffix to filename: | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | $lre = List::RewriteElements->new( { | 
| 279 |  |  |  |  |  |  | file            => "/path/to/source/file", | 
| 280 |  |  |  |  |  |  | body_rule       => sub { | 
| 281 |  |  |  |  |  |  | my $record = shift; | 
| 282 |  |  |  |  |  |  | $record .= q{additional field}; | 
| 283 |  |  |  |  |  |  | }, | 
| 284 |  |  |  |  |  |  | output_suffix   => '.out', | 
| 285 |  |  |  |  |  |  | } ); | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | Provide criteria to suppress output of header or individual record. | 
| 288 |  |  |  |  |  |  |  | 
| 289 |  |  |  |  |  |  | $lre = List::RewriteElements->new( { | 
| 290 |  |  |  |  |  |  | file            => "/path/to/source/file", | 
| 291 |  |  |  |  |  |  | header_suppress => sub { | 
| 292 |  |  |  |  |  |  | my $record = shift; | 
| 293 |  |  |  |  |  |  | return if $record =~ /$somepattern/; | 
| 294 |  |  |  |  |  |  | }, | 
| 295 |  |  |  |  |  |  | body_suppress   => sub { | 
| 296 |  |  |  |  |  |  | my $record = shift; | 
| 297 |  |  |  |  |  |  | return if $record ne 'somestring'; | 
| 298 |  |  |  |  |  |  | }, | 
| 299 |  |  |  |  |  |  | body_rule       => sub { | 
| 300 |  |  |  |  |  |  | my $record = shift; | 
| 301 |  |  |  |  |  |  | $record .= q{additional field}; | 
| 302 |  |  |  |  |  |  | }, | 
| 303 |  |  |  |  |  |  | } ); | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | =head2 Generate Output | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | $lre->generate_output(); | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | =head2 Report Output Information | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | $path_to_output_file    = $lre->get_output_path(); | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | $output_file_basename   = $lre->get_output_basename(); | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | $output_row_count       = $lre->get_total_rows(); | 
| 316 |  |  |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | $output_record_count    = $lre->get_total_records(); | 
| 318 |  |  |  |  |  |  |  | 
| 319 |  |  |  |  |  |  | $records_changed        = $lre->get_records_changed(); | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | $records_unchanged      = $lre->get_records_unchanged(); | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | $records_deleted        = $lre->get_records_deleted(); | 
| 324 |  |  |  |  |  |  |  | 
| 325 |  |  |  |  |  |  | $header_status          = $lre->get_header_status(); | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 328 |  |  |  |  |  |  |  | 
| 329 |  |  |  |  |  |  | It is common in many situations for you to receive a flat data file from someone | 
| 330 |  |  |  |  |  |  | else and have to generate a new file in which each row or record in the | 
| 331 |  |  |  |  |  |  | incoming file must either (a) be transformed according to some rule before | 
| 332 |  |  |  |  |  |  | being printing to the new file; or (b) if it meets certain criteria, not output to the new file at all. | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | List::RewriteElements enables you to write such rules and criteria, generate | 
| 335 |  |  |  |  |  |  | the file of transformed data records, and get back some basic statistics about | 
| 336 |  |  |  |  |  |  | the transformation. | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | List::RewriteElements is useful when the number of records in the incoming | 
| 339 |  |  |  |  |  |  | file may be large and you do not want to hold the entire list in memory. | 
| 340 |  |  |  |  |  |  | Similarly, the newly generated records are not held in memory but are | 
| 341 |  |  |  |  |  |  | immediately Ced to STDOUT or to file. | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | On the other hand, if for some reason you already have an array of records in | 
| 344 |  |  |  |  |  |  | memory, you can use List::RewriteElements to apply rules and criteria to each | 
| 345 |  |  |  |  |  |  | element of the array and then print the transformed records (again, without | 
| 346 |  |  |  |  |  |  | holding the output in memory). | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | =head1 SUBROUTINES | 
| 349 |  |  |  |  |  |  |  | 
| 350 |  |  |  |  |  |  | =head2 C | 
| 351 |  |  |  |  |  |  |  | 
| 352 |  |  |  |  |  |  | B  List::RewriteElements constructor. | 
| 353 |  |  |  |  |  |  |  | 
| 354 |  |  |  |  |  |  | B  Reference to a hash holding the following keys: | 
| 355 |  |  |  |  |  |  |  | 
| 356 |  |  |  |  |  |  | =over 4 | 
| 357 |  |  |  |  |  |  |  | 
| 358 |  |  |  |  |  |  | =item * C or C   | 
| 359 |  |  |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | The hash must hold either a C element or a C  element -- but not  | 
| 361 |  |  |  |  |  |  | both!  The value for the C key must be an absolute path to an input | 
| 362 |  |  |  |  |  |  | file.  The value for C  must be a reference to an array in memory.  | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | =item * C | 
| 365 |  |  |  |  |  |  |  | 
| 366 |  |  |  |  |  |  | The hash must have a C element whose value is a reference to a | 
| 367 |  |  |  |  |  |  | subroutine providing a formula for the transformation of an individual record | 
| 368 |  |  |  |  |  |  | in the incoming file to a record in the outgoing file.  The first argument | 
| 369 |  |  |  |  |  |  | passed to this subroutine must be the record from the incoming file.  The | 
| 370 |  |  |  |  |  |  | return value from this subroutine should be a string immediately ready for | 
| 371 |  |  |  |  |  |  | printing to the output file (though the string should not end in a newline, as | 
| 372 |  |  |  |  |  |  | printing will be handled by C). | 
| 373 |  |  |  |  |  |  |  | 
| 374 |  |  |  |  |  |  | =item * C | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | Optionally, you may provide a C element whose value is a | 
| 377 |  |  |  |  |  |  | reference to a subroutine providing a criterion according to which an | 
| 378 |  |  |  |  |  |  | individual record in the incoming file should be output to the outgoing file | 
| 379 |  |  |  |  |  |  | or not output, I, omitted from the output entirely.  The first argument | 
| 380 |  |  |  |  |  |  | to this subroutine should be the record from the incoming file.  The | 
| 381 |  |  |  |  |  |  | subroutine should, at least implicitly, return a true value when the record | 
| 382 |  |  |  |  |  |  | I be output.  The subroutine should simply C, , | 
| 383 |  |  |  |  |  |  | return an implicit C, when the record should be omitted from the | 
| 384 |  |  |  |  |  |  | outgoing file. | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  | =item * C | 
| 387 |  |  |  |  |  |  |  | 
| 388 |  |  |  |  |  |  | Frequently the first row in a flat data file is a header row containing, say, | 
| 389 |  |  |  |  |  |  | the names of the columns in a data table, joined by a delimiter.  Because the | 
| 390 |  |  |  |  |  |  | header row is different from all subsequent rows, you may optionally provide a | 
| 391 |  |  |  |  |  |  | C element whose value is a reference to a | 
| 392 |  |  |  |  |  |  | subroutine providing a formula for the transformation of the header row | 
| 393 |  |  |  |  |  |  | in the incoming file to the header in the outgoing file.  The first argument | 
| 394 |  |  |  |  |  |  | passed to this subroutine must be the header row from the incoming file.  The | 
| 395 |  |  |  |  |  |  | return value from this subroutine should be a string immediately ready for | 
| 396 |  |  |  |  |  |  | printing to the output file (though the string should not end in a newline, as | 
| 397 |  |  |  |  |  |  | printing will be handled by C). | 
| 398 |  |  |  |  |  |  |  | 
| 399 |  |  |  |  |  |  | =item * C | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | Optionally, if you have provided a C element, you may provide | 
| 402 |  |  |  |  |  |  | a C element whose value is a | 
| 403 |  |  |  |  |  |  | reference to a subroutine providing a criterion according to which an | 
| 404 |  |  |  |  |  |  | the header row from the incoming file should be output to the outgoing file | 
| 405 |  |  |  |  |  |  | or not output, I, omitted from the output entirely.  The first argument | 
| 406 |  |  |  |  |  |  | to this subroutine should be the header from the incoming file.  The | 
| 407 |  |  |  |  |  |  | subroutine should, at least implicitly, return a true value when the header | 
| 408 |  |  |  |  |  |  | I be output.  The subroutine should simply C, , | 
| 409 |  |  |  |  |  |  | return an implicit C, when the header should be omitted from the | 
| 410 |  |  |  |  |  |  | outgoing file. | 
| 411 |  |  |  |  |  |  |  | 
| 412 |  |  |  |  |  |  | =item * C or C | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | It is recommended that you supply either an C or an | 
| 415 |  |  |  |  |  |  | C element to the constructor; otherwise, the new list generated | 
| 416 |  |  |  |  |  |  | by application of the rules and criteria will simply C to C. | 
| 417 |  |  |  |  |  |  | The value of an C element should be a full path to the newly | 
| 418 |  |  |  |  |  |  | created file.  If you wish to create a new file name without specifying a full | 
| 419 |  |  |  |  |  |  | path but simply by tacking on a suffix to the name of the incoming file, | 
| 420 |  |  |  |  |  |  | provide an C element and the outgoing file will be created in | 
| 421 |  |  |  |  |  |  | the directory which is the I as of the point where | 
| 422 |  |  |  |  |  |  | C is called.  An C element will | 
| 423 |  |  |  |  |  |  | be ignored if an C element is provided. | 
| 424 |  |  |  |  |  |  |  | 
| 425 |  |  |  |  |  |  | =item * Note 1 | 
| 426 |  |  |  |  |  |  |  | 
| 427 |  |  |  |  |  |  | If neither a C or C element is provide to the | 
| 428 |  |  |  |  |  |  | constructor, List::RewriteElements will treat the first row of the incoming | 
| 429 |  |  |  |  |  |  | file the same as any other row, C, it will apply the C | 
| 430 |  |  |  |  |  |  | transformation formula. | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | =item * Note 2 | 
| 433 |  |  |  |  |  |  |  | 
| 434 |  |  |  |  |  |  | A C or C criterion, if present, will be | 
| 435 |  |  |  |  |  |  | logically applied I any C or C formula.  We | 
| 436 |  |  |  |  |  |  | don't apply the formula to transform a record if the record should not be | 
| 437 |  |  |  |  |  |  | output at all. | 
| 438 |  |  |  |  |  |  |  | 
| 439 |  |  |  |  |  |  | =item * Note 3 | 
| 440 |  |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  | =back | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | B  List::RewriteElements object. | 
| 444 |  |  |  |  |  |  |  | 
| 445 |  |  |  |  |  |  | =head2 C | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | B  Generates the output specified by arguments to C, | 
| 448 |  |  |  |  |  |  | I, creates an output file or Cs to C with records | 
| 449 |  |  |  |  |  |  | transformed as per those arguments. | 
| 450 |  |  |  |  |  |  |  | 
| 451 |  |  |  |  |  |  | B  None. | 
| 452 |  |  |  |  |  |  |  | 
| 453 |  |  |  |  |  |  | B  Returns true value upon success.  In case of failure it will | 
| 454 |  |  |  |  |  |  | C with some error message. | 
| 455 |  |  |  |  |  |  |  | 
| 456 |  |  |  |  |  |  | =head2 C | 
| 457 |  |  |  |  |  |  |  | 
| 458 |  |  |  |  |  |  | B  Get the full path to the newly created output file. | 
| 459 |  |  |  |  |  |  |  | 
| 460 |  |  |  |  |  |  | B  None. | 
| 461 |  |  |  |  |  |  |  | 
| 462 |  |  |  |  |  |  | B  String holding path to newly created output file. | 
| 463 |  |  |  |  |  |  |  | 
| 464 |  |  |  |  |  |  | B  Since use of the C attribute means that the full | 
| 465 |  |  |  |  |  |  | path to the output file will not be known until C has been | 
| 466 |  |  |  |  |  |  | called, C will only give a meaningful result once | 
| 467 |  |  |  |  |  |  | C has been called.  Otherwise, it will default to an empty | 
| 468 |  |  |  |  |  |  | string. | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  | =head2 C | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | B  Get only the basename of the newly created output file. | 
| 473 |  |  |  |  |  |  |  | 
| 474 |  |  |  |  |  |  | B  None. | 
| 475 |  |  |  |  |  |  |  | 
| 476 |  |  |  |  |  |  | B  String holding basename of newly created output file. | 
| 477 |  |  |  |  |  |  |  | 
| 478 |  |  |  |  |  |  | B  Since use of the C attribute means that the full | 
| 479 |  |  |  |  |  |  | path to the output file will not be known until C has been | 
| 480 |  |  |  |  |  |  | called, C will only give a meaningful result once | 
| 481 |  |  |  |  |  |  | C has been called.  Otherwise, it will default to an empty | 
| 482 |  |  |  |  |  |  | string. | 
| 483 |  |  |  |  |  |  |  | 
| 484 |  |  |  |  |  |  | =head2 C | 
| 485 |  |  |  |  |  |  |  | 
| 486 |  |  |  |  |  |  | B  Get the total number of rows in the newly created output file. | 
| 487 |  |  |  |  |  |  | This will include any header row. | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | B  None. | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | B  Nonnegative integer. | 
| 492 |  |  |  |  |  |  |  | 
| 493 |  |  |  |  |  |  | =head2 C | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | B  Get the total number of data records in the newly created output | 
| 496 |  |  |  |  |  |  | file.  If a header row is present in that file, C will | 
| 497 |  |  |  |  |  |  | return a value C<1> less than that returned by C. | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | B  None. | 
| 500 |  |  |  |  |  |  |  | 
| 501 |  |  |  |  |  |  | B  Nonnegative integer. | 
| 502 |  |  |  |  |  |  |  | 
| 503 |  |  |  |  |  |  | =head2 C | 
| 504 |  |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  | B  Get the number of data records in the newly created output file | 
| 506 |  |  |  |  |  |  | that are altered versions of records in the incoming file.  This value does | 
| 507 |  |  |  |  |  |  | not include changes in the header row. | 
| 508 |  |  |  |  |  |  |  | 
| 509 |  |  |  |  |  |  | B  None. | 
| 510 |  |  |  |  |  |  |  | 
| 511 |  |  |  |  |  |  | B  Nonnegative integer. | 
| 512 |  |  |  |  |  |  |  | 
| 513 |  |  |  |  |  |  | =head2 C | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | B  Get the number of data records in the newly created output file | 
| 516 |  |  |  |  |  |  | that are unaltered versions of records in the incoming file.  This value does | 
| 517 |  |  |  |  |  |  | not include changes in the header row. | 
| 518 |  |  |  |  |  |  |  | 
| 519 |  |  |  |  |  |  | B  None. | 
| 520 |  |  |  |  |  |  |  | 
| 521 |  |  |  |  |  |  | B  Nonnegative integer. | 
| 522 |  |  |  |  |  |  |  | 
| 523 |  |  |  |  |  |  | =head2 C | 
| 524 |  |  |  |  |  |  |  | 
| 525 |  |  |  |  |  |  | B  Get the number of data records in the original source (file or | 
| 526 |  |  |  |  |  |  | list) that were omitted from the newly created output file due to application | 
| 527 |  |  |  |  |  |  | of a C criterion.  This value does not include any suppression | 
| 528 |  |  |  |  |  |  | of a header row following application of a C criterion. | 
| 529 |  |  |  |  |  |  |  | 
| 530 |  |  |  |  |  |  | B  None. | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  | B  Nonnegative integer. | 
| 533 |  |  |  |  |  |  |  | 
| 534 |  |  |  |  |  |  | =head2 C | 
| 535 |  |  |  |  |  |  |  | 
| 536 |  |  |  |  |  |  | B  Indicate whether any header row in the original source (file or | 
| 537 |  |  |  |  |  |  | list) | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  | =over 4 | 
| 540 |  |  |  |  |  |  |  | 
| 541 |  |  |  |  |  |  | =item * | 
| 542 |  |  |  |  |  |  |  | 
| 543 |  |  |  |  |  |  | was rewritten in the newly created output file:  return value C<1>; | 
| 544 |  |  |  |  |  |  |  | 
| 545 |  |  |  |  |  |  | =item * | 
| 546 |  |  |  |  |  |  |  | 
| 547 |  |  |  |  |  |  | was transferred to the newly created output file without alteration:  return | 
| 548 |  |  |  |  |  |  | value C<0>; | 
| 549 |  |  |  |  |  |  |  | 
| 550 |  |  |  |  |  |  | =item * | 
| 551 |  |  |  |  |  |  |  | 
| 552 |  |  |  |  |  |  | was suppressed from appearing in the output file by application of a | 
| 553 |  |  |  |  |  |  | C criterion:  return value C<-1>; | 
| 554 |  |  |  |  |  |  |  | 
| 555 |  |  |  |  |  |  | =item * | 
| 556 |  |  |  |  |  |  |  | 
| 557 |  |  |  |  |  |  | no header row in the source:  return value C. | 
| 558 |  |  |  |  |  |  |  | 
| 559 |  |  |  |  |  |  | =back | 
| 560 |  |  |  |  |  |  |  | 
| 561 |  |  |  |  |  |  | B  None. | 
| 562 |  |  |  |  |  |  |  | 
| 563 |  |  |  |  |  |  | B  Numerical flag:  C<1>, C<0>, C<-1> or C as described | 
| 564 |  |  |  |  |  |  | above. | 
| 565 |  |  |  |  |  |  |  | 
| 566 |  |  |  |  |  |  | =head1 FAQ | 
| 567 |  |  |  |  |  |  |  | 
| 568 |  |  |  |  |  |  | =head2  Can I simultaneously rewrite records and interact with the external environment? | 
| 569 |  |  |  |  |  |  |  | 
| 570 |  |  |  |  |  |  | Yes.  If a C, C, C or C | 
| 571 |  |  |  |  |  |  | either (a) needs additional information from the external environment above | 
| 572 |  |  |  |  |  |  | and beyond that contained in the individual data record or (b) needs to cause | 
| 573 |  |  |  |  |  |  | a change in the external environment, you can write a closure and call that | 
| 574 |  |  |  |  |  |  | closure insider the rule. | 
| 575 |  |  |  |  |  |  |  | 
| 576 |  |  |  |  |  |  | Example: | 
| 577 |  |  |  |  |  |  |  | 
| 578 |  |  |  |  |  |  | my @greeks = qw( alpha beta gamma ); | 
| 579 |  |  |  |  |  |  |  | 
| 580 |  |  |  |  |  |  | my $get_a_greek = sub { | 
| 581 |  |  |  |  |  |  | return (shift @greeks); | 
| 582 |  |  |  |  |  |  | }; | 
| 583 |  |  |  |  |  |  |  | 
| 584 |  |  |  |  |  |  | my $lre  = List::RewriteElements->new ( { | 
| 585 |  |  |  |  |  |  | list        => [ map {"$_\n"} (1..5) ], | 
| 586 |  |  |  |  |  |  | body_rule   => sub { | 
| 587 |  |  |  |  |  |  | my $record = shift; | 
| 588 |  |  |  |  |  |  | my $rv; | 
| 589 |  |  |  |  |  |  | chomp $record; | 
| 590 |  |  |  |  |  |  | if ($record eq '4') { | 
| 591 |  |  |  |  |  |  | $rv = &{$get_a_greek}; | 
| 592 |  |  |  |  |  |  | } else { | 
| 593 |  |  |  |  |  |  | $rv = (10 * $record); | 
| 594 |  |  |  |  |  |  | } | 
| 595 |  |  |  |  |  |  | return $rv; | 
| 596 |  |  |  |  |  |  | }, | 
| 597 |  |  |  |  |  |  | body_suppress   => sub { | 
| 598 |  |  |  |  |  |  | my $record = shift; | 
| 599 |  |  |  |  |  |  | chomp $record; | 
| 600 |  |  |  |  |  |  | return if $record eq '5'; | 
| 601 |  |  |  |  |  |  | }, | 
| 602 |  |  |  |  |  |  | } ); | 
| 603 |  |  |  |  |  |  |  | 
| 604 |  |  |  |  |  |  | $lre->generate_output(); | 
| 605 |  |  |  |  |  |  |  | 
| 606 |  |  |  |  |  |  | This will produce: | 
| 607 |  |  |  |  |  |  |  | 
| 608 |  |  |  |  |  |  | 10 | 
| 609 |  |  |  |  |  |  | 20 | 
| 610 |  |  |  |  |  |  | 30 | 
| 611 |  |  |  |  |  |  | alpha | 
| 612 |  |  |  |  |  |  |  | 
| 613 |  |  |  |  |  |  | =head2 Can I use List-Rewrite Elements with fixed-width data? | 
| 614 |  |  |  |  |  |  |  | 
| 615 |  |  |  |  |  |  | Yes.  Suppose that you have this fixed-width data (adapted from Dave Cross' | 
| 616 |  |  |  |  |  |  | I): | 
| 617 |  |  |  |  |  |  |  | 
| 618 |  |  |  |  |  |  | my @dataset = ( | 
| 619 |  |  |  |  |  |  | q{00374Bloggs & Co       19991105100103+00015000}, | 
| 620 |  |  |  |  |  |  | q{00375Smith Brothers    19991106001234-00004999}, | 
| 621 |  |  |  |  |  |  | q{00376Camel Inc         19991107289736+00002999}, | 
| 622 |  |  |  |  |  |  | q{00377Generic Code      19991108056789-00003999}, | 
| 623 |  |  |  |  |  |  | ); | 
| 624 |  |  |  |  |  |  |  | 
| 625 |  |  |  |  |  |  | Suppose further that you need to update certain records and that C<%revisions> | 
| 626 |  |  |  |  |  |  | holds the data for updating: | 
| 627 |  |  |  |  |  |  |  | 
| 628 |  |  |  |  |  |  | my %revisions = ( | 
| 629 |  |  |  |  |  |  | 376 => [ 'Camel Inc', 20061107, 388293, '+', 4999 ], | 
| 630 |  |  |  |  |  |  | 377 => [ 'Generic Code', 20061108, 99821, '-',  6999 ], | 
| 631 |  |  |  |  |  |  | ); | 
| 632 |  |  |  |  |  |  |  | 
| 633 |  |  |  |  |  |  | Write a C subroutine which uses C, C and C | 
| 634 |  |  |  |  |  |  | as needed to update the records. | 
| 635 |  |  |  |  |  |  |  | 
| 636 |  |  |  |  |  |  | my $lre  = List::RewriteElements->new ( { | 
| 637 |  |  |  |  |  |  | list        => \@dataset, | 
| 638 |  |  |  |  |  |  | body_rule   => sub { | 
| 639 |  |  |  |  |  |  | my $record = shift; | 
| 640 |  |  |  |  |  |  | my $template = 'A5A18A8A6AA8'; | 
| 641 |  |  |  |  |  |  | my @rec  = unpack($template, $record); | 
| 642 |  |  |  |  |  |  | $rec[0] =~ s/^0+//; | 
| 643 |  |  |  |  |  |  | my ($acctno, %values, $result); | 
| 644 |  |  |  |  |  |  | $acctno = $rec[0]; | 
| 645 |  |  |  |  |  |  | $values{$acctno} = [ @rec[1..$#rec] ]; | 
| 646 |  |  |  |  |  |  | if ($revisions{$acctno}) { | 
| 647 |  |  |  |  |  |  | $values{$acctno} = $revisions{$acctno}; | 
| 648 |  |  |  |  |  |  | } | 
| 649 |  |  |  |  |  |  | $result = sprintf  "%05d%-18s%8d%06d%1s%08d", | 
| 650 |  |  |  |  |  |  | ($acctno, @{$values{$acctno}}); | 
| 651 |  |  |  |  |  |  | return $result; | 
| 652 |  |  |  |  |  |  | }, | 
| 653 |  |  |  |  |  |  | } ); | 
| 654 |  |  |  |  |  |  |  | 
| 655 |  |  |  |  |  |  | =head2 How does this differ from Tie::File? | 
| 656 |  |  |  |  |  |  |  | 
| 657 |  |  |  |  |  |  | Mark Jason Dominus' Tie::File module is one of my Fave 5 CPAN modules.  It's | 
| 658 |  |  |  |  |  |  | excellent for modifying a file in place.  But I frequently have to leave the | 
| 659 |  |  |  |  |  |  | source file unmodified and create a new file, which implies, at the very | 
| 660 |  |  |  |  |  |  | least, opening, printing to, and closing filehandles in addition to using | 
| 661 |  |  |  |  |  |  | Tie::File.  List::RewriteElements hides all | 
| 662 |  |  |  |  |  |  | that.  It also provides the statistical report methods. | 
| 663 |  |  |  |  |  |  |  | 
| 664 |  |  |  |  |  |  | =head2 Couldn't I do this with C | 
| 665 |  |  |  |  |  |  |  | 
| 666 |  |  |  |  |  |  | Quite possibly.  But if your rules and criteria were complicated or long, the | 
| 667 |  |  |  |  |  |  | content of the C | 
| 668 |  |  |  |  |  |  | also wouldn't get the statistical report methods. | 
| 669 |  |  |  |  |  |  |  | 
| 670 |  |  |  |  |  |  | =head2 How Does It Work? | 
| 671 |  |  |  |  |  |  |  | 
| 672 |  |  |  |  |  |  | Why do you care?  Why do you want to look inside the black box?  If you really | 
| 673 |  |  |  |  |  |  | want to know, read the source! | 
| 674 |  |  |  |  |  |  |  | 
| 675 |  |  |  |  |  |  | =head1 PREREQUISITES | 
| 676 |  |  |  |  |  |  |  | 
| 677 |  |  |  |  |  |  | List::RewriteElements relies only on modules distributed with the Perl core as | 
| 678 |  |  |  |  |  |  | of 5.8.0.  IO::Capture::Stdout is required for the test suite, but a copy is | 
| 679 |  |  |  |  |  |  | included in the distribution under the F directory. | 
| 680 |  |  |  |  |  |  |  | 
| 681 |  |  |  |  |  |  | =head1 BUGS | 
| 682 |  |  |  |  |  |  |  | 
| 683 |  |  |  |  |  |  | None known at this time.  File bug reports at L. | 
| 684 |  |  |  |  |  |  |  | 
| 685 |  |  |  |  |  |  | =head1 HISTORY | 
| 686 |  |  |  |  |  |  |  | 
| 687 |  |  |  |  |  |  | 0.09 Mon Jan 22 22:35:56 EST 2007 | 
| 688 |  |  |  |  |  |  | - Update version number and release date only.  Purpose:  generate new | 
| 689 |  |  |  |  |  |  | round of tests by cpan testers, in the hope that it eliminates a FAIL report | 
| 690 |  |  |  |  |  |  | on v0.08 where failure was due solely to error on tester's box. | 
| 691 |  |  |  |  |  |  |  | 
| 692 |  |  |  |  |  |  | 0.08 Mon Jan  1 08:54:01 EST 2007 | 
| 693 |  |  |  |  |  |  | - xdg to the rescue!  Applied and extended patches supplied by David | 
| 694 |  |  |  |  |  |  | Golden for Win32.  In constructor, value of C<$/> is supplied to the C | 
| 695 |  |  |  |  |  |  | option. | 
| 696 |  |  |  |  |  |  |  | 
| 697 |  |  |  |  |  |  | 0.07 Sun Dec 31 11:13:04 EST 2006 | 
| 698 |  |  |  |  |  |  | - Switched to using File::Spec::catfile() to generate one path (rather | 
| 699 |  |  |  |  |  |  | than Cwd::realpath().  This was done in an attempt to respond to corion's FAIL | 
| 700 |  |  |  |  |  |  | reports (but I don't have a good Windows box, so I can't be certain of the | 
| 701 |  |  |  |  |  |  | results). | 
| 702 |  |  |  |  |  |  |  | 
| 703 |  |  |  |  |  |  | 0.06 Sat Dec 16 11:31:38 EST 2006 | 
| 704 |  |  |  |  |  |  | - Created t/07_fixed_width.t and t/testlib/fixed.t to illustrate use of | 
| 705 |  |  |  |  |  |  | List::RewriteElements with fixed-width data. | 
| 706 |  |  |  |  |  |  |  | 
| 707 |  |  |  |  |  |  | 0.05 Thu Dec 14 07:42:24 EST 2006 | 
| 708 |  |  |  |  |  |  | - Correction of POD formatting errors only; no change in functionality. | 
| 709 |  |  |  |  |  |  | CPAN upload. | 
| 710 |  |  |  |  |  |  |  | 
| 711 |  |  |  |  |  |  | 0.04 Wed Dec 13 23:04:33 EST 2006 | 
| 712 |  |  |  |  |  |  | - More tests; fine-tuning of code and documentation.  First CPAN upload. | 
| 713 |  |  |  |  |  |  |  | 
| 714 |  |  |  |  |  |  | 0.03 Tue Dec 12 22:13:00 EST 2006 | 
| 715 |  |  |  |  |  |  | - Implementation of statistical methods; more tests. | 
| 716 |  |  |  |  |  |  |  | 
| 717 |  |  |  |  |  |  | 0.02 Mon Dec 11 19:38:26 EST 2006 | 
| 718 |  |  |  |  |  |  | - Added tests to demonstrate use of closures to supply additional | 
| 719 |  |  |  |  |  |  | information to elements such as body_rule. | 
| 720 |  |  |  |  |  |  |  | 
| 721 |  |  |  |  |  |  | 0.01 Sat Dec  9 22:29:51 2006 | 
| 722 |  |  |  |  |  |  | - original version; created by ExtUtils::ModuleMaker 0.47 | 
| 723 |  |  |  |  |  |  |  | 
| 724 |  |  |  |  |  |  | =head1 ACKNOWLEDGEMENTS | 
| 725 |  |  |  |  |  |  |  | 
| 726 |  |  |  |  |  |  | Thanks to David Landgren for raising the question of use of | 
| 727 |  |  |  |  |  |  | List-RewriteElements with fixed-width data. | 
| 728 |  |  |  |  |  |  |  | 
| 729 |  |  |  |  |  |  | I then adapted an example from Dave Cross' I, | 
| 730 |  |  |  |  |  |  | Chapter 7.1, "Fixed-width Data," to provide a test | 
| 731 |  |  |  |  |  |  | demonstrating processing of fixed-width data. | 
| 732 |  |  |  |  |  |  |  | 
| 733 |  |  |  |  |  |  | =head1 AUTHOR | 
| 734 |  |  |  |  |  |  |  | 
| 735 |  |  |  |  |  |  | James E Keenan.  CPAN ID: JKEENAN.  jkeenan@cpan.org. | 
| 736 |  |  |  |  |  |  | http://search.cpan.org/~jkeenan/ or | 
| 737 |  |  |  |  |  |  | http://thenceforward.net/perl/modules/List-RewriteElements. | 
| 738 |  |  |  |  |  |  |  | 
| 739 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 740 |  |  |  |  |  |  |  | 
| 741 |  |  |  |  |  |  | Copyright 2006 James E Keenan (USA). | 
| 742 |  |  |  |  |  |  |  | 
| 743 |  |  |  |  |  |  | This program is free software; you can redistribute | 
| 744 |  |  |  |  |  |  | it and/or modify it under the same terms as Perl itself. | 
| 745 |  |  |  |  |  |  |  | 
| 746 |  |  |  |  |  |  | The full text of the license can be found in the | 
| 747 |  |  |  |  |  |  | LICENSE file included with this module. | 
| 748 |  |  |  |  |  |  |  | 
| 749 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 750 |  |  |  |  |  |  |  | 
| 751 |  |  |  |  |  |  | David Cross, I (Manning, 2001). | 
| 752 |  |  |  |  |  |  |  | 
| 753 |  |  |  |  |  |  | =cut | 
| 754 |  |  |  |  |  |  |  | 
| 755 |  |  |  |  |  |  |  |