/[webpac2]/trunk/lib/WebPAC/Input.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /trunk/lib/WebPAC/Input.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 652 by dpavlin, Thu Sep 7 15:01:45 2006 UTC revision 1221 by dpavlin, Tue Jun 9 21:37:32 2009 UTC
# Line 3  package WebPAC::Input; Line 3  package WebPAC::Input;
3  use warnings;  use warnings;
4  use strict;  use strict;
5    
6  use blib;  use lib 'lib';
7    
8  use WebPAC::Common;  use WebPAC::Common;
9  use base qw/WebPAC::Common/;  use base qw/WebPAC::Common/;
10  use Data::Dumper;  use Data::Dump qw/dump/;
11  use Encode qw/from_to/;  use Encode qw/decode from_to/;
12    use YAML;
13    
14  =head1 NAME  =head1 NAME
15    
16  WebPAC::Input - read different file formats into WebPAC  WebPAC::Input - read different file formats into WebPAC
17    
 =head1 VERSION  
   
 Version 0.12  
   
18  =cut  =cut
19    
20  our $VERSION = '0.12';  our $VERSION = '0.19';
21    
22  =head1 SYNOPSIS  =head1 SYNOPSIS
23    
# Line 43  Perhaps a little code snippet. Line 40  Perhaps a little code snippet.
40    
41          my $db = WebPAC::Input->new(          my $db = WebPAC::Input->new(
42                  module => 'WebPAC::Input::ISIS',                  module => 'WebPAC::Input::ISIS',
                 low_mem => 1,  
43          );          );
44    
45          $db->open( path => '/path/to/database' );          $db->open( path => '/path/to/database' );
# Line 62  Create new input database object. Line 58  Create new input database object.
58    
59    my $db = new WebPAC::Input(    my $db = new WebPAC::Input(
60          module => 'WebPAC::Input::MARC',          module => 'WebPAC::Input::MARC',
         encoding => 'ISO-8859-2',  
         low_mem => 1,  
61          recode => 'char pairs',          recode => 'char pairs',
62          no_progress_bar => 1,          no_progress_bar => 1,
63            input_config => {
64                    mapping => [ 'foo', 'bar', 'baz' ],
65            },
66    );    );
67    
68  C<module> is low-level file format module. See L<WebPAC::Input::ISIS> and  C<module> is low-level file format module. See L<WebPAC::Input::ISIS> and
69  L<WebPAC::Input::MARC>.  L<WebPAC::Input::MARC>.
70    
 Optional parametar C<encoding> specify application code page (which will be  
 used internally). This should probably be your terminal encoding, and by  
 default, it C<ISO-8859-2>.  
   
 Default is not to use C<low_mem> options (see L<MEMORY USAGE> below).  
   
71  C<recode> is optional string constisting of character or words pairs that  C<recode> is optional string constisting of character or words pairs that
72  should be replaced in input stream.  should be replaced in input stream.
73    
# Line 94  sub new { Line 85  sub new {
85    
86          my $log = $self->_get_logger;          my $log = $self->_get_logger;
87    
88          $log->logconfess("code_page argument is not suppored any more. change it to encoding") if ($self->{lookup});          $log->logconfess("code_page argument is not suppored any more.") if $self->{code_page};
89          $log->logconfess("lookup argument is not suppored any more. rewrite call to lookup_ref") if ($self->{lookup});          $log->logconfess("encoding argument is not suppored any more.") if $self->{encoding};
90            $log->logconfess("lookup argument is not suppored any more. rewrite call to lookup_ref") if $self->{lookup};
91            $log->logconfess("low_mem argument is not suppored any more. rewrite it to load_row and save_row") if $self->{low_mem};
92    
93          $log->logconfess("specify low-level file format module") unless ($self->{module});          $log->logconfess("specify low-level file format module") unless ($self->{module});
94          my $module = $self->{module};          my $module_path = $self->{module};
95          $module =~ s#::#/#g;          $module_path =~ s#::#/#g;
96          $module .= '.pm';          $module_path .= '.pm';
97          $log->debug("require low-level module $self->{module} from $module");          $log->debug("require low-level module $self->{module} from $module_path");
   
         require $module;  
         #eval $self->{module} .'->import';  
   
         # check if required subclasses are implemented  
         foreach my $subclass (qw/open_db fetch_rec init dump_rec/) {  
                 my $n = $self->{module} . '::' . $subclass;  
                 if (! defined &{ $n }) {  
                         my $missing = "missing $subclass in $self->{module}";  
                         $self->{$subclass} = sub { $log->logwarn($missing) };  
                 } else {  
                         $self->{$subclass} = \&{ $n };  
                 }  
         }  
   
         if ($self->{init}) {  
                 $log->debug("calling init");  
                 $self->{init}->($self, @_);  
         }  
   
         $self->{'encoding'} ||= 'ISO-8859-2';  
   
         # running with low_mem flag? well, use DBM::Deep then.  
         if ($self->{'low_mem'}) {  
                 $log->info("running with low_mem which impacts performance (<32 Mb memory usage)");  
98    
99                  my $db_file = "data.db";          require $module_path;
   
                 if (-e $db_file) {  
                         unlink $db_file or $log->logdie("can't remove '$db_file' from last run");  
                         $log->debug("removed '$db_file' from last run");  
                 }  
   
                 require DBM::Deep;  
   
                 my $db = new DBM::Deep $db_file;  
   
                 $log->logdie("DBM::Deep error: $!") unless ($db);  
   
                 if ($db->error()) {  
                         $log->logdie("can't open '$db_file' under low_mem: ",$db->error());  
                 } else {  
                         $log->debug("using file '$db_file' for DBM::Deep");  
                 }  
   
                 $self->{'db'} = $db;  
         }  
100    
101          $self ? return $self : return undef;          $self ? return $self : return undef;
102  }  }
# Line 157  sub new { Line 105  sub new {
105    
106  This function will read whole database in memory and produce lookups.  This function will read whole database in memory and produce lookups.
107    
108     my $store;     # simple in-memory hash
109    
110   $input->open(   $input->open(
111          path => '/path/to/database/file',          path => '/path/to/database/file',
112          code_page => 'cp852',          input_encoding => 'cp852',
113            strict_encoding => 0,
114          limit => 500,          limit => 500,
115          offset => 6000,          offset => 6000,
         lookup => $lookup_obj,  
116          stats => 1,          stats => 1,
117          lookup_ref => sub {          lookup_coderef => sub {
118                  my ($k,$v) = @_;                  my $rec = shift;
119                  # store lookup $k => $v                  # store lookups
120          },          },
121          modify_records => {          modify_records => {
122                  900 => { '^a' => { ' : ' => '^b' } },                  900 => { '^a' => { ' : ' => '^b' } },
123                  901 => { '*' => { '^b' => ' ; ' } },                  901 => { '*' => { '^b' => ' ; ' } },
124          },          },
125          modify_file => 'conf/modify/mapping.map',          modify_file => 'conf/modify/mapping.map',
126            save_row => sub {
127                    my $a = shift;
128                    $store->{ $a->{id} } = $a->{row};
129            },
130            load_row => sub {
131                    my $a = shift;
132                    return defined($store->{ $a->{id} }) &&
133                            $store->{ $a->{id} };
134            },
135    
136   );   );
137    
138  By default, C<code_page> is assumed to be C<cp852>.  By default, C<input_encoding> is assumed to be C<cp852>.
139    
140  C<offset> is optional parametar to position at some offset before reading from database.  C<offset> is optional parametar to position at some offset before reading from database.
141    
# Line 183  C<limit> is optional parametar to read j Line 143  C<limit> is optional parametar to read j
143    
144  C<stats> create optional report about usage of fields and subfields  C<stats> create optional report about usage of fields and subfields
145    
146  C<lookup_coderef> is closure to call when adding C<< key => 'value' >> combinations to  C<lookup_coderef> is closure to called to save data into lookups
 lookup.  
147    
148  C<modify_records> specify mapping from subfields to delimiters or from  C<modify_records> specify mapping from subfields to delimiters or from
149  delimiters to subfields, as well as oprations on fields (if subfield is  delimiters to subfields, as well as oprations on fields (if subfield is
# Line 194  C<modify_file> is alternative for C<modi Line 153  C<modify_file> is alternative for C<modi
153  (hopefully) simplier sintax than YAML or perl (see L</modify_file_regex>). This option  (hopefully) simplier sintax than YAML or perl (see L</modify_file_regex>). This option
154  overrides C<modify_records> if both exists for same input.  overrides C<modify_records> if both exists for same input.
155    
156    C<save_row> and C<load_row> are low-level implementation of store engine. Calling convention
157    is documented in example above.
158    
159    C<strict_encoding> should really default to 1, but it doesn't for now.
160    
161  Returns size of database, regardless of C<offset> and C<limit>  Returns size of database, regardless of C<offset> and C<limit>
162  parametars, see also C<size>.  parametars, see also C<size>.
163    
# Line 204  sub open { Line 168  sub open {
168          my $arg = {@_};          my $arg = {@_};
169    
170          my $log = $self->_get_logger();          my $log = $self->_get_logger();
171            $log->debug( "arguments: ",dump( $arg ));
172    
173            $log->logconfess("encoding argument is not suppored any more.") if $self->{encoding};
174            $log->logconfess("code_page argument is not suppored any more.") if $self->{code_page};
175          $log->logconfess("lookup argument is not suppored any more. rewrite call to lookup_coderef") if ($arg->{lookup});          $log->logconfess("lookup argument is not suppored any more. rewrite call to lookup_coderef") if ($arg->{lookup});
176          $log->logconfess("lookup_coderef must be CODE, not ",ref($arg->{lookup_coderef}))          $log->logconfess("lookup_coderef must be CODE, not ",ref($arg->{lookup_coderef}))
177                  if ($arg->{lookup_coderef} && ref($arg->{lookup_coderef}) ne 'CODE');                  if ($arg->{lookup_coderef} && ref($arg->{lookup_coderef}) ne 'CODE');
178    
179            $log->debug( $arg->{lookup_coderef} ? '' : 'not ', "using lookup_coderef");
180    
181          $log->logcroak("need path") if (! $arg->{'path'});          $log->logcroak("need path") if (! $arg->{'path'});
182          my $code_page = $arg->{'code_page'} || 'cp852';          my $input_encoding = $arg->{'input_encoding'} || $self->{'input_encoding'} || 'cp852';
183    
184          # store data in object          # store data in object
         $self->{'input_code_page'} = $code_page;  
185          foreach my $v (qw/path offset limit/) {          foreach my $v (qw/path offset limit/) {
186                  $self->{$v} = $arg->{$v} if ($arg->{$v});                  $self->{$v} = $arg->{$v} if ($arg->{$v});
187          }          }
188    
189            if ($arg->{load_row} || $arg->{save_row}) {
190                    $log->logconfess("save_row and load_row must be defined in pair and be CODE") unless (
191                            ref($arg->{load_row}) eq 'CODE' &&
192                            ref($arg->{save_row}) eq 'CODE'
193                    );
194                    $self->{load_row} = $arg->{load_row};
195                    $self->{save_row} = $arg->{save_row};
196                    $log->debug("using load_row and save_row instead of in-memory hash");
197            }
198    
199          my $filter_ref;          my $filter_ref;
200          my $recode_regex;          my $recode_regex;
201          my $recode_map;          my $recode_map;
# Line 245  sub open { Line 223  sub open {
223                  $log->debug("using modify_file $p");                  $log->debug("using modify_file $p");
224                  $rec_regex = $self->modify_file_regexps( $p );                  $rec_regex = $self->modify_file_regexps( $p );
225          } elsif (my $h = $arg->{modify_records}) {          } elsif (my $h = $arg->{modify_records}) {
226                  $log->debug("using modify_records ", Dumper( $h ));                  $log->debug("using modify_records ", sub { dump( $h ) });
227                  $rec_regex = $self->modify_record_regexps(%{ $h });                  $rec_regex = $self->modify_record_regexps(%{ $h });
228          }          }
229          $log->debug("rec_regex: ", Dumper($rec_regex)) if ($rec_regex);          $log->debug("rec_regex: ", sub { dump($rec_regex) }) if ($rec_regex);
230    
231          my ($db, $size) = $self->{open_db}->( $self,          my $class = $self->{module} || $log->logconfess("can't get low-level module name!");
232    
233            my $ll_db = $class->new(
234                  path => $arg->{path},                  path => $arg->{path},
235                    input_config => $arg->{input_config} || $self->{input_config},
236  #               filter => sub {  #               filter => sub {
237  #                       my ($l,$f_nr) = @_;  #                       my ($l,$f_nr) = @_;
238  #                       return unless defined($l);  #                       return unless defined($l);
239  #                       from_to($l, $code_page, $self->{'encoding'});  #                       $l = decode($input_encoding, $l);
240  #                       $l =~ s/($recode_regex)/$recode_map->{$1}/g if ($recode_regex && $recode_map);  #                       $l =~ s/($recode_regex)/$recode_map->{$1}/g if ($recode_regex && $recode_map);
241  #                       return $l;  #                       return $l;
242  #               },  #               },
243                  %{ $arg },                  %{ $arg },
244          );          );
245    
246          unless (defined($db)) {          unless (defined($ll_db)) {
247                  $log->logwarn("can't open database $arg->{path}, skipping...");                  $log->logwarn("can't open database $arg->{path}, skipping...");
248                  return;                  return;
249          }          }
250    
251            my $size = $ll_db->size;
252    
253          unless ($size) {          unless ($size) {
254                  $log->logwarn("no records in database $arg->{path}, skipping...");                  $log->logwarn("no records in database $arg->{path}, skipping...");
255                  return;                  return;
# Line 291  sub open { Line 274  sub open {
274          # store size for later          # store size for later
275          $self->{size} = ($to_rec - $from_rec) ? ($to_rec - $from_rec + 1) : 0;          $self->{size} = ($to_rec - $from_rec) ? ($to_rec - $from_rec + 1) : 0;
276    
277          $log->info("processing $self->{size}/$size records [$from_rec-$to_rec] convert $code_page -> $self->{encoding}", $self->{stats} ? ' [stats]' : '');          my $strict_encoding = $arg->{strict_encoding} || $self->{strict_encoding}; ## FIXME should be 1 really
278    
279            $log->info("processing $self->{size}/$size records [$from_rec-$to_rec]",
280                    " encoding $input_encoding ", $strict_encoding ? ' [strict]' : '',
281                    $self->{stats} ? ' [stats]' : '',
282            );
283    
284          # read database          # read database
285          for (my $pos = $from_rec; $pos <= $to_rec; $pos++) {          for (my $pos = $from_rec; $pos <= $to_rec; $pos++) {
286    
287                  $log->debug("position: $pos\n");                  $log->debug("position: $pos\n");
288    
289                  my $rec = $self->{fetch_rec}->($self, $pos, sub {                  my $rec = $ll_db->fetch_rec($pos, sub {
290                                  my ($l,$f_nr) = @_;                                  my ($l,$f_nr,$debug) = @_;
291  #                               return unless defined($l);  #                               return unless defined($l);
292  #                               return $l unless ($rec_regex && $f_nr);  #                               return $l unless ($rec_regex && $f_nr);
293    
294                                    return unless ( defined($l) && defined($f_nr) );
295    
296                                    warn "-=> $f_nr ## |$l|\n" if ($debug);
297                                  $log->debug("-=> $f_nr ## $l");                                  $log->debug("-=> $f_nr ## $l");
298    
299                                  # codepage conversion and recode_regex                                  # codepage conversion and recode_regex
300                                  from_to($l, $code_page, $self->{'encoding'});                                  $l = decode($input_encoding, $l, 1);
301                                  $l =~ s/($recode_regex)/$recode_map->{$1}/g if ($recode_regex && $recode_map);                                  $l =~ s/($recode_regex)/$recode_map->{$1}/g if ($recode_regex && $recode_map);
302    
303                                  # apply regexps                                  # apply regexps
# Line 315  sub open { Line 306  sub open {
306                                          my $c = 0;                                          my $c = 0;
307                                          foreach my $r (@{ $rec_regex->{$f_nr} }) {                                          foreach my $r (@{ $rec_regex->{$f_nr} }) {
308                                                  my $old_l = $l;                                                  my $old_l = $l;
309                                                  eval '$l =~ ' . $r;                                                  $log->logconfess("expected regex in ", dump( $r )) unless defined($r->{regex});
310                                                    eval '$l =~ ' . $r->{regex};
311                                                  if ($old_l ne $l) {                                                  if ($old_l ne $l) {
312                                                          $log->debug("REGEX on $f_nr eval \$l =~ $r\n## old l: [$old_l]\n## new l: [$l]");                                                          my $d = "|$old_l| -> |$l| "; # . $r->{regex};
313                                                            $d .= ' +' . $r->{line} . ' ' . $r->{file} if defined($r->{line});
314                                                            $d .= ' ' . $r->{debug} if defined($r->{debug});
315                                                            $log->debug("MODIFY $d");
316                                                            warn "*** $d\n" if ($debug);
317    
318                                                  }                                                  }
319                                                  $log->error("error applying regex: $r") if ($@);                                                  $log->error("error applying regex: ",dump($r), $@) if $@;
320                                          }                                          }
321                                  }                                  }
322    
323                                  $log->debug("<=- $f_nr ## $l");                                  $log->debug("<=- $f_nr ## |$l|");
324                                    warn "<=- $f_nr ## $l\n" if ($debug);
325                                  return $l;                                  return $l;
326                  });                  });
327    
328                  $log->debug(sub { Dumper($rec) });                  $log->debug(sub { dump($rec) });
329    
330                  if (! $rec) {                  if (! $rec) {
331                          $log->warn("record $pos empty? skipping...");                          $log->warn("record $pos empty? skipping...");
# Line 335  sub open { Line 333  sub open {
333                  }                  }
334    
335                  # store                  # store
336                  if ($self->{low_mem}) {                  if ($self->{save_row}) {
337                          $self->{db}->put($pos, $rec);                          $self->{save_row}->({
338                                    id => $pos,
339                                    row => $rec,
340                            });
341                  } else {                  } else {
342                          $self->{data}->{$pos} = $rec;                          $self->{data}->{$pos} = $rec;
343                  }                  }
# Line 348  sub open { Line 349  sub open {
349                  if ($self->{stats}) {                  if ($self->{stats}) {
350    
351                          # fetch clean record with regexpes applied for statistics                          # fetch clean record with regexpes applied for statistics
352                          my $rec = $self->{fetch_rec}->($self, $pos);                          my $rec = $ll_db->fetch_rec($pos);
353    
354                          foreach my $fld (keys %{ $rec }) {                          foreach my $fld (keys %{ $rec }) {
355                                  $self->{_stats}->{fld}->{ $fld }++;                                  $self->{_stats}->{fld}->{ $fld }++;
356    
357                                  $log->logdie("invalid record fild $fld, not ARRAY")                                  #$log->logdie("invalid record fild $fld, not ARRAY")
358                                          unless (ref($rec->{ $fld }) eq 'ARRAY');                                  next unless (ref($rec->{ $fld }) eq 'ARRAY');
359                    
360                                  foreach my $row (@{ $rec->{$fld} }) {                                  foreach my $row (@{ $rec->{$fld} }) {
361    
# Line 385  sub open { Line 386  sub open {
386          $self->{max_pos} = $to_rec;          $self->{max_pos} = $to_rec;
387          $log->debug("max_pos: $to_rec");          $log->debug("max_pos: $to_rec");
388    
389            # save for dump
390            $self->{ll_db} = $ll_db;
391    
392          return $size;          return $size;
393  }  }
394    
395    sub input_module { $_[0]->{ll_db} }
396    
397  =head2 fetch  =head2 fetch
398    
399  Fetch next record from database. It will also displays progress bar.  Fetch next record from database. It will also displays progress bar.
# Line 424  sub fetch { Line 430  sub fetch {
430    
431          my $rec;          my $rec;
432    
433          if ($self->{low_mem}) {          if ($self->{load_row}) {
434                  $rec = $self->{db}->get($mfn);                  $rec = $self->{load_row}->({ id => $mfn });
435          } else {          } else {
436                  $rec = $self->{data}->{$mfn};                  $rec = $self->{data}->{$mfn};
437          }          }
# Line 480  First record in database has position 1. Line 486  First record in database has position 1.
486    
487  sub seek {  sub seek {
488          my $self = shift;          my $self = shift;
489          my $pos = shift || return;          my $pos = shift;
490    
491          my $log = $self->_get_logger();          my $log = $self->_get_logger();
492    
493            $log->logconfess("called without pos") unless defined($pos);
494    
495          if ($pos < 1) {          if ($pos < 1) {
496                  $log->warn("seek before first record");                  $log->warn("seek before first record");
497                  $pos = 1;                  $pos = 1;
# Line 518  sub stats { Line 526  sub stats {
526    
527          my $out = join("\n",          my $out = join("\n",
528                  map {                  map {
529                          my $f = $_ || die "no field";                          my $f = $_;
530                            die "no field in ", dump( $s->{fld} ) unless defined( $f );
531                          my $v = $s->{fld}->{$f} || die "no s->{fld}->{$f}";                          my $v = $s->{fld}->{$f} || die "no s->{fld}->{$f}";
532                          $max_fld = $v if ($v > $max_fld);                          $max_fld = $v if ($v > $max_fld);
533    
534                          my $o = sprintf("%4s %d ~", $f, $v);                          my $o = sprintf("%4s %d ~", $f, $v);
535    
536                          if (defined($s->{sf}->{$f})) {                          if (defined($s->{sf}->{$f})) {
537                                    my @subfields = keys %{ $s->{sf}->{$f} };
538                                  map {                                  map {
539                                          $o .= sprintf(" %s:%d%s", $_,                                          $o .= sprintf(" %s:%d%s", $_,
540                                                  $s->{sf}->{$f}->{$_}->{count},                                                  $s->{sf}->{$f}->{$_}->{count},
541                                                  $s->{sf}->{$f}->{$_}->{repeatable} ? '*' : '',                                                  $s->{sf}->{$f}->{$_}->{repeatable} ? '*' : '',
542                                          );                                          );
543                                  } sort keys %{ $s->{sf}->{$f} };                                  } (
544                                            # first indicators and other special subfields
545                                            sort( grep { length($_)  > 1 } @subfields ),
546                                            # then subfileds (single char)
547                                            sort( grep { length($_) == 1 } @subfields ),
548                                    );
549                          }                          }
550    
551                          if (my $v_r = $s->{repeatable}->{$f}) {                          if (my $v_r = $s->{repeatable}->{$f}) {
# Line 538  sub stats { Line 553  sub stats {
553                          }                          }
554    
555                          $o;                          $o;
556                  } sort { $a cmp $b } keys %{ $s->{fld} }                  } sort {
557                            if ( $a =~ m/^\d+$/ && $b =~ m/^\d+$/ ) {
558                                    $a <=> $b
559                            } else {
560                                    $a cmp $b
561                            }
562                    } keys %{ $s->{fld} }
563          );          );
564    
565          $log->debug( sub { Dumper($s) } );          $log->debug( sub { dump($s) } );
566    
567            my $path = 'var/stats.yml';
568            YAML::DumpFile( $path, $s );
569            $log->info( 'created ', $path, ' with ', -s $path, ' bytes' );
570    
571          return $out;          return $out;
572  }  }
573    
574  =head2 dump  =head2 dump_ascii
575    
576  Display humanly readable dump of record  Display humanly readable dump of record
577    
578  =cut  =cut
579    
580  sub dump {  sub dump_ascii {
581          my $self = shift;          my $self = shift;
582    
583          return $self->{dump_rec}->($self, $self->{pos});          return unless $self->{ll_db};
584    
585            if ($self->{ll_db}->can('dump_ascii')) {
586                    return $self->{ll_db}->dump_ascii( $self->{pos} );
587            } else {
588                    return dump( $self->{ll_db}->fetch_rec( $self->{pos} ) );
589            }
590  }  }
591    
592  =head2 modify_record_regexps  =head2 _get_regex
593    
594  Generate hash with regexpes to be applied using l<filter>.  Helper function called which create regexps to be execute on code.
595    
596    my $regexpes = $input->modify_record_regexps(    _get_regex( 900, 'regex:[0-9]+' ,'numbers' );
597                  900 => { '^a' => { ' : ' => '^b' } },    _get_regex( 900, '^b', ' : ^b' );
598                  901 => { '*' => { '^b' => ' ; ' } },  
599    );  It supports perl regexps with C<regex:> prefix to from value and has
600    additional logic to skip empty subfields.
601    
602  =cut  =cut
603    
604  sub _get_regex {  sub _get_regex {
605          my ($sf,$from,$to) = @_;          my ($sf,$from,$to) = @_;
606    
607            # protect /
608            $from =~ s!/!\\/!gs;
609            $to =~ s!/!\\/!gs;
610    
611            if ($from =~ m/^regex:(.+)$/) {
612                    $from = $1;
613            } else {
614                    $from = '\Q' . $from . '\E';
615            }
616          if ($sf =~ /^\^/) {          if ($sf =~ /^\^/) {
617                    my $need_subfield_data = '*';   # no
618                    # if from is also subfield, require some data in between
619                    # to correctly skip empty subfields
620                    $need_subfield_data = '+' if ($from =~ m/^\\Q\^/);
621                  return                  return
622                          's/\Q'. $sf .'\E([^\^]*?)\Q'. $from .'\E([^\^]*?)/'. $sf .'$1'. $to .'$2/';                          's/\Q'. $sf .'\E([^\^]' . $need_subfield_data . '?)'. $from .'([^\^]*?)/'. $sf .'$1'. $to .'$2/';
623          } else {          } else {
624                  return                  return
625                          's/\Q'. $from .'\E/'. $to .'/g';                          's/'. $from .'/'. $to .'/g';
626          }          }
627  }  }
628    
629    
630    =head2 modify_record_regexps
631    
632    Generate hash with regexpes to be applied using L<filter>.
633    
634      my $regexpes = $input->modify_record_regexps(
635                    900 => { '^a' => { ' : ' => '^b' } },
636                    901 => { '*' => { '^b' => ' ; ' } },
637      );
638    
639    =cut
640    
641  sub modify_record_regexps {  sub modify_record_regexps {
642          my $self = shift;          my $self = shift;
643          my $modify_record = {@_};          my $modify_record = {@_};
# Line 598  sub modify_record_regexps { Line 655  sub modify_record_regexps {
655                          foreach my $from (keys %{ $modify_record->{$f}->{$sf} }) {                          foreach my $from (keys %{ $modify_record->{$f}->{$sf} }) {
656                                  my $to = $modify_record->{$f}->{$sf}->{$from};                                  my $to = $modify_record->{$f}->{$sf}->{$from};
657                                  #die "no field?" unless defined($to);                                  #die "no field?" unless defined($to);
658                                  $log->debug("transform: |$from| -> |$to|");                                  my $d = "|$from| -> |$to|";
659                                    $log->debug("transform: $d");
660    
661                                  my $regex = _get_regex($sf,$from,$to);                                  my $regex = _get_regex($sf,$from,$to);
662                                  push @{ $regexpes->{$f} }, $regex;                                  push @{ $regexpes->{$f} }, { regex => $regex, debug => $d };
663                                  $log->debug("regex: $regex");                                  $log->debug("regex: $regex");
664                          }                          }
665                  }                  }
# Line 612  sub modify_record_regexps { Line 670  sub modify_record_regexps {
670    
671  =head2 modify_file_regexps  =head2 modify_file_regexps
672    
673  Generate hash with regexpes to be applied using l<filter> from  Generate hash with regexpes to be applied using L<filter> from
674  pseudo hash/yaml format for regex mappings.  pseudo hash/yaml format for regex mappings.
675    
676  It should be obvious:  It should be obvious:
# Line 640  sub modify_file_regexps { Line 698  sub modify_file_regexps {
698    
699          my $regexpes;          my $regexpes;
700    
701          CORE::open(my $fh, $modify_path) || $log->die("can't open modify file $modify_path: $!");          CORE::open(my $fh, $modify_path) || $log->logdie("can't open modify file $modify_path: $!");
702    
703          my ($f,$sf);          my ($f,$sf);
704    
# Line 662  sub modify_file_regexps { Line 720  sub modify_file_regexps {
720                          $log->debug("transform: |$from| -> |$to|");                          $log->debug("transform: |$from| -> |$to|");
721    
722                          my $regex = _get_regex($sf,$from,$to);                          my $regex = _get_regex($sf,$from,$to);
723                          push @{ $regexpes->{$f} }, $regex;                          push @{ $regexpes->{$f} }, {
724                                    regex => $regex,
725                                    file => $modify_path,
726                                    line => $.,
727                            };
728                          $log->debug("regex: $regex");                          $log->debug("regex: $regex");
729                    } else {
730                            die "can't parse: $_";
731                  }                  }
732          }          }
733    
734          return $regexpes;          return $regexpes;
735  }  }
736    
 =head1 MEMORY USAGE  
   
 C<low_mem> options is double-edged sword. If enabled, WebPAC  
 will run on memory constraint machines (which doesn't have enough  
 physical RAM to create memory structure for whole source database).  
   
 If your machine has 512Mb or more of RAM and database is around 10000 records,  
 memory shouldn't be an issue. If you don't have enough physical RAM, you  
 might consider using virtual memory (if your operating system is handling it  
 well, like on FreeBSD or Linux) instead of dropping to L<DBM::Deep> to handle  
 parsed structure of ISIS database (this is what C<low_mem> option does).  
   
 Hitting swap at end of reading source database is probably o.k. However,  
 hitting swap before 90% will dramatically decrease performance and you will  
 be better off with C<low_mem> and using rest of availble memory for  
 operating system disk cache (Linux is particuallary good about this).  
 However, every access to database record will require disk access, so  
 generation phase will be slower 10-100 times.  
   
 Parsed structures are essential - you just have option to trade RAM memory  
 (which is fast) for disk space (which is slow). Be sure to have planty of  
 disk space if you are using C<low_mem> and thus L<DBM::Deep>.  
   
 However, when WebPAC is running on desktop machines (or laptops :-), it's  
 highly undesireable for system to start swapping. Using C<low_mem> option can  
 reduce WecPAC memory usage to around 64Mb for same database with lookup  
 fields and sorted indexes which stay in RAM. Performance will suffer, but  
 memory usage will really be minimal. It might be also more confortable to  
 run WebPAC reniced on those machines.  
   
   
737  =head1 AUTHOR  =head1 AUTHOR
738    
739  Dobrica Pavlinusic, C<< <dpavlin@rot13.org> >>  Dobrica Pavlinusic, C<< <dpavlin@rot13.org> >>

Legend:
Removed from v.652  
changed lines
  Added in v.1221

  ViewVC Help
Powered by ViewVC 1.1.26