File Coverage

blib/lib/WARC/Record/Replay.pm
Criterion Covered Total %
statement 73 73 100.0
branch 30 34 88.2
condition 9 9 100.0
subroutine 8 8 100.0
pod 2 2 100.0
total 122 126 96.8


line stmt bran cond sub pod time code
1             package WARC::Record::Replay; # -*- CPerl -*-
2              
3 26     26   59605 use strict;
  26         85  
  26         719  
4 26     26   144 use warnings;
  26         51  
  26         621  
5              
6 26     26   448 use WARC; *WARC::Record::Replay::VERSION = \$WARC::VERSION;
  26         63  
  26         814  
7              
8             =head1 NAME
9              
10             WARC::Record::Replay - WARC record replay registry and autoloading
11              
12             =head1 SYNOPSIS
13              
14             use WARC::Record;
15              
16             $object = $record->replay;
17              
18             =cut
19              
20 26     26   155 use Carp;
  26         60  
  26         1360  
21 26     26   154 use File::Spec;
  26         57  
  26         24491  
22              
23             # array of arrays:
24             # ( [ predicate coderef, handler coderef ]... )
25             our @Handlers = ();
26             # Each predicate is called with the record object locally stored in $_ and
27             # must return false to reject the record or true to accept the record.
28             #
29             # Each handler for which the predicate returns true is tried in order.
30              
31             =head1 DESCRIPTION
32              
33             This is an internal module that provides a registry of protocol replay
34             support modules and an autoloading facility.
35              
36             =over
37              
38             =cut
39              
40             # Scan @INC for autoload descriptors and see if any available modules can
41             # be autoloaded for this record.
42              
43             sub _try_autoload_for ($) {
44 5     5   7 my $record = shift;
45 5         6 my $loaded = 0;
46              
47 5         8 local *_;
48 5         9 foreach my $area (@INC) {
49 9         32 my $vol; my $dirpath; my $tail;
  9         0  
50 9         104 ($vol, $dirpath, $tail) = File::Spec->splitpath($area);
51 9         41 my @dirs = File::Spec->splitdir($dirpath);
52 9         94 my $dirname = File::Spec->catpath
53             ($vol, File::Spec->catdir(@dirs, $tail, qw/WARC Record Replay/));
54              
55 9 100       166 next unless -d $dirname;
56              
57 5 50       152 opendir my $dir, $dirname or die "autoload dirscan $dirname: $!";
58 5         147 my @modules = grep defined, map {/^([[:alnum:]_]+[.]pm)$/; $1} # untaint
  25         69  
  25         57  
59             grep /[.]pm$/, readdir $dir;
60 5 50       69 closedir $dir or die "autoload dirscan close $dirname: $!";
61              
62             FILE:
63 5         14 foreach my $module (@modules) {
64 21         254 my $filename = File::Spec->catpath
65             ($vol, File::Spec->catdir(@dirs, $tail, qw/WARC Record Replay/),
66             $module);
67 21         105 my $modfilename = File::Spec::Unix->catfile
68             (qw/WARC Record Replay/, $module);
69 21 100       54 next FILE if $INC{$modfilename};
70              
71 17 50       494 open my $file, '<', $filename or die "autoload scan $filename: $!";
72 17         44 my $descriptor_found = 0;
73             LINE:
74 17         186 while (<$file>) {
75 167 100       317 if (m/^=(?:for|begin)\s+autoload(?:\s+|$)/)
76 17         24 { $descriptor_found = 1; next LINE; }
  17         59  
77 150 100       264 next LINE unless $descriptor_found;
78 65 100       105 last LINE if m/^=/;
79              
80 55 100       87 if (m/^\[WARC::Record::Replay\]$/)
81 17         19 { $descriptor_found = 2; next LINE; }
  17         34  
82 38 100       60 next LINE if $descriptor_found < 2;
83 30 100       47 last LINE if m/^\[/;
84              
85             # ... parse and test conditional; load if matched
86 27 100       82 if (m/^([[:alpha:]][_[:alnum:]]*)\(([-_[:alnum:]]*)\)\s*=\s*(.+)$/) {
87             # $1: method $2: argument $3: match
88 17         24 my $match_valid = 0; my $match_value;
  17         17  
89 17         21 eval {$match_value = $record->$1($2); $match_valid = 1};
  17         99  
  13         61  
90 17 100 100     210 if ($match_valid and $match_value =~ $3)
91 4         1200 { require $modfilename; $loaded++; last LINE }
  3         95  
  3         6  
92             }
93             }
94 16 50       217 close $file or die "autoload scan close $file: $!";
95             }
96             }
97              
98 4         19 return $loaded
99             }
100              
101             =item WARC::Record::Replay::register { predicate } $handler
102              
103             Add a handler to the internal list of replay handlers. The given handler
104             will be used for records on which the given predicate returns true.
105              
106             The predicate will be evaluated with $_ locally set to the record object to
107             be replayed and @_ empty each time a record is replayed.
108              
109             =cut
110              
111             sub register (&$) {
112 17 100 100 17 1 3002 croak "attempt to register invalid handler"
113             unless (ref $_[0] eq 'CODE') && (ref $_[1] eq 'CODE');
114              
115 15         40 push @Handlers, [ @_[0, 1] ];
116              
117             return # nothing
118 15         27 }
119              
120             =item WARC::Record::Replay::find_handlers( $record )
121              
122             Return a list of handlers that can replay the protocol message in $record.
123              
124             =cut
125              
126             sub find_handlers ($) {
127 62     62 1 3714 my $record = shift;
128 62         93 my @handlers = ();
129              
130             {
131 62         75 local *_; $_ = $record;
  62         125  
  62         78  
132 62         98 foreach my $handler (@Handlers)
133 218 100       523 { push @handlers, $handler->[1] if $handler->[0]->() }
134             }
135              
136 62 100 100     186 if (scalar @handlers == 0 and _try_autoload_for $record)
137             # repeat the search now that a module has been loaded
138 3         7 { unshift @_, $record; goto &find_handlers }
  3         15  
139              
140             return @handlers
141 58         150 }
142              
143             =back
144              
145             =cut
146              
147             1;
148             __END__