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 28     28   59130 use strict;
  28         58  
  28         781  
4 28     28   130 use warnings;
  28         48  
  28         605  
5              
6 28     28   454 use WARC; *WARC::Record::Replay::VERSION = \$WARC::VERSION;
  28         47  
  28         925  
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 28     28   135 use Carp;
  28         58  
  28         1498  
21 28     28   180 use File::Spec;
  28         49  
  28         25953  
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 25     25   42 my $record = shift;
45 25         35 my $loaded = 0;
46              
47 25         44 local *_;
48 25         44 foreach my $area (@INC) {
49 229         658 my $vol; my $dirpath; my $tail;
  229         0  
50 229         2431 ($vol, $dirpath, $tail) = File::Spec->splitpath($area);
51 229         1025 my @dirs = File::Spec->splitdir($dirpath);
52 229         2010 my $dirname = File::Spec->catpath
53             ($vol, File::Spec->catdir(@dirs, $tail, qw/WARC Record Replay/));
54              
55 229 100       3547 next unless -d $dirname;
56              
57 65 50       1981 opendir my $dir, $dirname or die "autoload dirscan $dirname: $!";
58 65         1482 my @modules = grep defined, map {/^([[:alnum:]_]+[.]pm)$/; $1} # untaint
  85         343  
  85         389  
59             grep /[.]pm$/, readdir $dir;
60 65 50       773 closedir $dir or die "autoload dirscan close $dirname: $!";
61              
62             FILE:
63 65         191 foreach my $module (@modules) {
64 81         1144 my $filename = File::Spec->catpath
65             ($vol, File::Spec->catdir(@dirs, $tail, qw/WARC Record Replay/),
66             $module);
67 81         453 my $modfilename = File::Spec::Unix->catfile
68             (qw/WARC Record Replay/, $module);
69 81 100       289 next FILE if $INC{$modfilename};
70              
71 64 50       2219 open my $file, '<', $filename or die "autoload scan $filename: $!";
72 64         174 my $descriptor_found = 0;
73             LINE:
74 64         839 while (<$file>) {
75 727 100       1559 if (m/^=(?:for|begin)\s+autoload(?:\s+|$)/)
76 64         115 { $descriptor_found = 1; next LINE; }
  64         194  
77 663 100       1280 next LINE unless $descriptor_found;
78 249 100       513 last LINE if m/^=/;
79              
80 194 100       424 if (m/^\[WARC::Record::Replay\]$/)
81 64         89 { $descriptor_found = 2; next LINE; }
  64         164  
82 130 100       221 next LINE if $descriptor_found < 2;
83 122 100       231 last LINE if m/^\[/;
84              
85             # ... parse and test conditional; load if matched
86 119 100       400 if (m/^([[:alpha:]][_[:alnum:]]*)\(([-_[:alnum:]]*)\)\s*=\s*(.+)$/) {
87             # $1: method $2: argument $3: match
88 64         93 my $match_valid = 0; my $match_value;
  64         83  
89 64         101 eval {$match_value = $record->$1($2); $match_valid = 1};
  64         327  
  60         158  
90 64 100 100     530 if ($match_valid and $match_value =~ $3)
91 6         2173 { require $modfilename; $loaded++; last LINE }
  5         101  
  5         15  
92             }
93             }
94 63 50       1258 close $file or die "autoload scan close $file: $!";
95             }
96             }
97              
98 24         148 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 25 100 100 25 1 3018 croak "attempt to register invalid handler"
113             unless (ref $_[0] eq 'CODE') && (ref $_[1] eq 'CODE');
114              
115 23         60 push @Handlers, [ @_[0, 1] ];
116              
117             return # nothing
118 23         45 }
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 159     159 1 3880 my $record = shift;
128 159         250 my @handlers = ();
129              
130             {
131 159         214 local *_; $_ = $record;
  159         386  
  159         259  
132 159         337 foreach my $handler (@Handlers)
133 538 100       1345 { push @handlers, $handler->[1] if $handler->[0]->() }
134             }
135              
136 159 100 100     512 if (scalar @handlers == 0 and _try_autoload_for $record)
137             # repeat the search now that a module has been loaded
138 5         15 { unshift @_, $record; goto &find_handlers }
  5         22  
139              
140             return @handlers
141 153         422 }
142              
143             =back
144              
145             =cut
146              
147             1;
148             __END__