File Coverage

blib/lib/WebSource/Module.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package WebSource::Module;
2 2     2   15 use strict;
  2         4  
  2         107  
3 2     2   22 use Carp;
  2         5  
  2         127  
4              
5 2     2   972 use WebSource::Envelope;
  0            
  0            
6              
7             =head1 NAME
8              
9             WebSource::Module : WebSource module - class from which all modules inherit
10             Each module needs to define a C method.
11              
12             =head1 SYNOPSIS
13              
14             {
15             package MyModule;
16             our @ISA=('WebSource::Module');
17              
18             sub handler {
19             my ($self,$val) = @_;
20             # do something with $val and produce @res
21             return @res;
22             }
23              
24             }
25              
26             my $m = MyModule->new(
27             name => "GiveMeAName",
28             queue => $queue
29             );
30              
31             ... do some stuff ...
32              
33             $m->forwardTo($queue);
34              
35             ... do some stuff ...
36              
37             $m->run->join();
38              
39             =head1 METHODS
40              
41             =over 2
42              
43             =cut
44              
45             =item C<< $mod = WebSource::Module->new( name => $name ) >>
46              
47             Create a new instance of module;
48              
49             =cut
50              
51             sub new {
52             my $class = shift;
53             my %params = @_;
54             my $self = bless \%params, $class;
55             $self->_init_;
56             return $self;
57             }
58              
59             =item C<< $mod->_init_ >>
60              
61             Does some initializations. Any operator $op inheriting from WebSource::Module
62             should call $op->SUPER::_init_ inside a redefined _init_ method.
63              
64             =cut
65              
66             sub _init_ {
67             my $self = shift;
68             if(!$self->{name}) {
69             carp("No module name given. Using default noname");
70             $self->{name} = "noname";
71             }
72             $self->{producers} = [];
73             $self->{consumers} = [];
74             $self->{results} = [];
75             $self->log(1,"new module named ", $self->{name}, " created");
76             if(my $wsd = $self->{wsdnode}) {
77             foreach my $dnode ($wsd->findnodes('data')) {
78             $self->push(WebSource::Envelope->new(
79             type => "text/string",
80             data => $dnode->textContent
81             ));
82             }
83             foreach my $dnode ($wsd->findnodes('xmldata')) {
84             if(my @enodes = $dnode->findnodes("*")) {
85             $self->push(WebSource::Envelope->new(
86             type => "object/dom-node",
87             data => $enodes[0]
88             ));
89             } else {
90             carp("Warning : xmldata without data detected\n");
91             }
92             }
93             foreach my $pnode ($wsd->findnodes('parameters/param')) {
94             my $key = $pnode->getAttribute("name");
95             my $val = $pnode->getAttribute("value");
96             $key =~ s/-/_/g;
97             $self->{$key} = $val;
98             }
99             }
100             }
101              
102             =item C<< $mod->log($level,$message) >>
103              
104             Use this modules logger to log a message $message with priority level
105             $level. This is actually used internally. Any inheriting module is encouraged
106             to use the logging facility.
107              
108             =cut
109              
110             sub log {
111             my $self = shift;
112             my $level = shift;
113             my $class = ref($self);
114             my $name = $self->{name};
115             $self->{logger} and
116             $self->{logger}->log($level, "[$class/$name] ", @_);
117             }
118              
119             =item C<< $mod->will_log($level) >>
120              
121             Use this modules logger to check if a message at level
122             $level will be logged. This is actually used internally. Any inheriting module is encouraged
123             to use the logging facility.
124              
125             =cut
126              
127             sub will_log {
128             my $self = shift;
129             my $level = shift;
130             return ($self->{logger} && $self->{logger}->will_log($level));
131             }
132              
133             =item C<< $mod->set_logger($log) >>
134              
135             Sets the logger associated to this module
136              
137             =cut
138              
139             sub set_logger {
140             my $self = shift;
141             $self->{logger} = shift;
142             $self->log(1,"logger set");
143             }
144              
145             =item C<< $mod->push($val) >>
146              
147             Push $val into the module. This handles the given value and stores
148             it onto the stock.
149              
150             =cut
151              
152             sub push {
153             my $self = shift;
154             foreach my $env (@_) {
155             UNIVERSAL::isa($env,'WebSource::Envelope') or croak("Didn't push in an envelope");
156             $self->log(3,"Handling $env : ", $env->as_string);
157             $self->log(8,"Content $env : ",$env->dataXML);
158             my @res = $self->handle($env);
159             map { defined($_) or croak("Undefined value generated") } @res;
160             $self->log(3,"Obtained ",$#res+1," results");
161             push @{$self->{results}}, @res;
162             }
163             }
164              
165             =item C<< $mod->producers(@modules) >>
166              
167             Add a list of modules from where to ask for data (ie. set @modules
168             as producers of this module).
169             Also sets this module as a consumer of each of the modules in @modules.
170              
171             B Only C or C calls should be used
172              
173             =cut
174              
175             sub producers {
176             my $self = shift;
177             foreach my $p (@_) {
178             $self->_producers($p);
179             $p->_consumers($self);
180             }
181             }
182              
183             =item C<< $mod->consumers(@modules) >>
184              
185             Add a list of modules where results should be sent to (ie. set @modules
186             as consumers of this modules production).
187             Also sets this module as a producer of each of the modules in @modules.
188              
189             B Only C or C calls should be used
190              
191             =cut
192              
193             sub consumers {
194             my $self = shift;
195             foreach my $c (@_) {
196             $self->_consumers($c);
197             $c->_producers($self);
198             }
199             }
200              
201             sub _producers {
202             my $self = shift;
203             CORE::push @{$self->{producers}}, @_;
204             }
205              
206             sub _consumers {
207             my $self = shift;
208             CORE::push @{$self->{consumers}}, @_;
209             }
210              
211             =item C<< $self->produce >>
212              
213             Ask this module to produce a result. If some are available in the
214             stock, the first is sent to its consumers. If not, more results are produced
215             by asking the producers of this module to produce more.
216              
217             If a result is produced it is forwarded to the modules consumers and
218             returned. If no results can be produced undef is returned. (However if
219             in a consumer, with an originally empty stock, a simpler test on the
220             stock after having called produce on a producer will do).
221              
222             =cut
223              
224             sub produce {
225             my $self = shift;
226             my %map = @_;
227              
228             if(!$self->{__started__}) {
229             $self->start();
230             $self->{__started__} = 1;
231             }
232              
233             defined($map{$self->{name}}) or $map{$self->{name}} = 0;
234             $map{$self->{name}} >= 1 and return ();
235             $map{$self->{name}} += 1;
236              
237             my %save = %map;
238             $self->log(3,"Producing");
239             my @prods = @{$self->{producers}};
240             my $done = 0;
241             while(!@{$self->{results}} && !$done) {
242             $self->log(1,"Asking our ",scalar(@prods)," producers to produce");
243             my @res = map {
244             $_->produce(%map)
245             } @prods;
246             $done = !@res;
247             $self->log(1,"Queue has ",scalar(@{$self->{results}})," pending items");
248             }
249             # while(!@{$self->{results}} && @prods) {
250             # my $prod = shift @prods;
251             # %map = %save;
252             # my @res = $prod->produce(%map);
253             # while(!@{$self->{results}} && @res && $res[0]) {
254             # %map = %save;
255             # @res = $prod->produce(%map);
256             # }
257             # }
258             if(@{$self->{results}}) {
259             my $res = shift @{$self->{results}};
260             $self->log(3,"Produced ",$res->as_string," (forwarding)");
261             foreach my $c (@{$self->{consumers}}) {
262             $c->push($res);
263             }
264             $self->{abortIfEmpty} = 0;
265             return $res;
266             } else {
267             if(!$self->{abortIfEmpty}) {
268             return ();
269             $self->{__started__} = 0;
270             $self->end();
271             } else {
272             die "No production for ".$self->{name}." with abort-if-empty marked yes\n";
273             }
274             }
275             }
276              
277              
278             =item C<< $mod->start() >>
279              
280             Called at before production has started
281              
282             =cut
283              
284             sub start() {}
285              
286             =item C<< $mod->end() >>
287              
288             Called when production has ended (no more producers have data)
289              
290             =cut
291              
292             sub end() {}
293              
294              
295             =item C<< $mod->handle($val) >>
296              
297             Called internally by push (ie. when data arrives).
298             When a value $val arrives, C<< $module->handle($val) >> is called
299             which produces C<@res>. The resulting values are stored onto the
300             stock;
301              
302             =cut
303              
304             sub handle {
305             my $self = shift;
306             # my $class = ref($self);
307             # $self->log(1,"Warning : $class did not define a handler method");
308             return @_;
309             }
310              
311              
312             sub print_state {
313             my $self = shift;
314             print "--- State of module ",$self->{name}," ---\n";
315             print "In stock : ",join(",",@{$self->{results}}),"\n";
316             print "Producers : ", join(",",map { $_->{name} } @{$self->{producers}}),"\n";
317             print "Consumers : ", join(",",map { $_->{name} } @{$self->{consumers}}),"\n";
318             print "---\n";
319             }
320              
321             sub as_string {
322             my $self = shift;
323             my $name = $self->{name};
324             my $class = ref($self);
325             return "$class/$name";
326             }
327              
328             =head1 SEE ALSO
329              
330             WebSource::Fetch, WebSource::Extract, etc.
331              
332             =cut
333              
334             1;