File Coverage

blib/lib/POE/Component/Sequence.pm
Criterion Covered Total %
statement 190 198 95.9
branch 70 82 85.3
condition 9 21 42.8
subroutine 34 34 100.0
pod 18 25 72.0
total 321 360 89.1


line stmt bran cond sub pod time code
1             package POE::Component::Sequence;
2              
3             =head1 NAME
4              
5             POE::Component::Sequence - Asynchronous sequences with multiple callbacks
6              
7             =head1 SYNOPSIS
8              
9             use POE qw(Component::Sequence);
10              
11             POE::Component::Sequence
12             ->new(
13             sub {
14             my $sequence = shift;
15             $sequence->heap_set(
16             a => 5,
17             b => 9,
18             op => '*'
19             );
20             },
21             sub {
22             my $sequence = shift;
23             my $math = join ' ', map { $sequence->heap_index($_) } qw(a op b);
24             $sequence->heap_set(result => eval $math);
25             }
26             )
27             ->add_callback(sub {
28             my ($sequence, $result) = @_;
29             print "Answer was " . $sequence->heap_index('result') . "\n";
30             })
31             ->run();
32              
33             $poe_kernel->run();
34              
35             =head1 DESCRIPTION
36              
37             A Sequence is a series of code blocks (actions) that are executed (handled) within the same context, in series. Each action has access to the sequence object, can pause it, finish the sequence, add additional actions to be performed later, or store variables in the context (the heap).
38              
39             If we had the following action in the above example sequence:
40              
41             sub {
42             my $sequence = shift;
43             $sequence->pause;
44             ...
45             }
46              
47             ...the sequence would pause, waiting for something to call either $sequence->failed, $sequence->finished or $sequence->resume.
48              
49             =head2 Reasoning
50              
51             =over 4
52              
53             Normally, in Perl when I would create a series of asynchronous steps I needed to complete, I would chain them together using a bunch of hardcoded callbacks. So, say I needed to login to a remote server using a custom protocol, I would perhaps do this:
54              
55             =over 4
56              
57             =item 1.
58              
59             Using POE, yield to a state named 'login' with my params
60              
61             =item 2.
62              
63             'login' would send a packet along a TCP socket, assigning the state 'login_callback' as the recipient of the response to this packet.
64              
65             =item 3.
66              
67             'login_callback' would run with the response
68              
69             =back
70              
71             If I wanted to do something after I was done logging in, I have a number of ways to do this:
72              
73             =over 4
74              
75             =item 1.
76              
77             Pass an arbitrary callback to 'login' (which would somehow have to carry to 'login_callback')
78              
79             =item 2.
80              
81             Hard code the next step in 'login_callback'
82              
83             =item 3.
84              
85             Have 'login_callback' publish to some sort of event watcher (PubSub) that it had logged in
86              
87             =back
88              
89             The first two mechanisms are cludgy, and don't allow for the potential for more than one thing being done upon completion of the task. While the third idea, the PubSub announce, is a good one, it wouldn't (without cludgly coding) contain contextual information that we wanted carried through the process at the outset. Additionally, if the login process failed at some point in the process, keeping track of who wants to be notified about this failure becomes very difficult to manage.
90              
91             The elegant solution, in my opinion, was to encapsulate all the actions necessary for a process into a discrete sequence that can be paused/resumed, can have multiple callbacks, and carry with it a shared heap where I could store and retrieve data from, passing around as a reference to whomever wanted to access it.
92              
93             =back
94              
95             =cut
96              
97 7     7   500309 use strict;
  7         15  
  7         310  
98 7     7   38 use warnings;
  7         13  
  7         249  
99 7     7   6323 use POE;
  7         535021  
  7         66  
100             use Class::MethodMaker [
101 7         149 array => [qw(
102             actions
103             callbacks
104             handlers
105             on_run
106             active_action_path
107             )],
108             hash => [qw(
109             heap
110             options
111             delays
112             )],
113             scalar => [qw(
114             alias
115             pause_state
116             running
117             result
118             is_error
119             is_finished
120             )],
121 7     7   807292 ];
  7         397892  
122              
123             our $VERSION = '0.02';
124              
125             my $_session_count = 0;
126              
127             # Provide a globla attach point for plugins
128             our @_plugin_handlers;
129              
130             our $RUN_AGAIN = '__poco_sequence_run_again__';
131              
132             =head1 USAGE
133              
134             =head2 Class Methods
135              
136             =head3 new( ... )
137              
138             =over 4
139              
140             Creates a new Sequence object. Provide a list of actions to be handled in sequence by the handlers.
141              
142             If the first argument to new() is a HASHREF, it will be treated as arguments that modify the behavior as follows:
143              
144             =over 4
145              
146             =item *
147              
148             Any method that can be chained on the sequence (add_callback, add_error_callback, and add_finally_callback, for example) can be specified in this arguments hash, but obviously only once, as it's a hash and has unique keys.
149              
150             =item *
151              
152             Aside from this, the arguments hash is thrown into the $sequence->options and modifies the way the actions are handled (see L).
153              
154             =back
155              
156             =back
157              
158             =cut
159              
160             sub new {
161 26     26 1 3175 my $class = shift;
162 26         91 my $self = bless {}, $class;
163              
164 26         1020 $self->alias(__PACKAGE__ . '_' . $_session_count++);
165              
166             POE::Session->create(
167             object_states => [
168             $self => [qw(
169             next
170             fail
171             finish
172             finally
173             delay_add
174             delay_complete
175             delay_adjust
176             delay_remove
177             )],
178             ],
179             inline_states => {
180             _start => sub {
181 26     26   8308 my ($kernel) = $_[KERNEL];
182 26         965 $kernel->alias_set($self->alias);
183             },
184             },
185 26         562 );
186              
187 26 100       4774 if (my @actions = @_) {
188 4         10 foreach my $action (@actions) {
189 7         21 $self->add_action($action);
190             }
191             }
192              
193 26         92 $self->add_handler(\&default_handler);
194 26         87 $self->add_handler($_) foreach @_plugin_handlers;
195              
196 26         93 return $self;
197             }
198              
199             =head2 Object Methods, Chained
200              
201             All these methods return $self, so you can chain them together.
202              
203             =head3 add_callback( $subref )
204              
205             =over 4
206              
207             Callbacks are FIFO. Adds the subref onto the list of normal callbacks. See C for how and when the normal callbacks are called. Subref signature is ($sequence, @args || ()) where @args is what was passed to the C call (if the sequence completes without C called, this will be an empty array).
208              
209             Dying inside a normal callback will be caught, and will move execution to the error callbacks, passing the error message to the error callbacks.
210              
211             =back
212              
213             =cut
214              
215             sub add_callback {
216 9     9 1 574 my ($self, $callback) = @_;
217 9         306 $self->callbacks_push({
218             code => $callback,
219             type => 'normal',
220             });
221 9         78 return $self;
222             }
223              
224             =head3 add_error_callback( $subref )
225              
226             =over 4
227              
228             Error callbacks are FIFO. Adds the subref onto the list of error callbacks. See C for how and when the error callbacks are called. Subref signature is ($sequence, @args || ()) where @args is what was passed to the C call (usually a caught 'die' error message).
229              
230             Return value is not used.
231              
232             Dying inside an error callback won't be caught by the sequence.
233              
234             =back
235              
236             =cut
237              
238             sub add_error_callback {
239 6     6 1 593 my ($self, $callback) = @_;
240 6         225 $self->callbacks_push({
241             code => $callback,
242             type => 'error',
243             });
244 6         47 return $self;
245             }
246              
247             =head3 add_finally_callback( $subref )
248              
249             =over 4
250              
251             Adds the subref onto the list of 'finally' callbacks. See C for how and when the 'finally' callbacks are called. This is effectively the same as a normal callback (C) but is called even if the sequence ended in failure.
252              
253             Dying inside a 'finally' callback will not be caught.
254              
255             =back
256              
257             =cut
258              
259             sub add_finally_callback {
260 33     33 1 9473 my ($self, $callback) = @_;
261 33         1208 $self->callbacks_push({
262             code => $callback,
263             type => 'finally',
264             });
265 33         284 return $self;
266             }
267              
268             =head3 add_action( $subref || )
269              
270             =over 4
271              
272             Actions are FIFO. Enqueues the given action.
273              
274             =back
275              
276             =cut
277              
278             sub add_action {
279 62     62 1 1510 my ($self, $action) = @_;
280              
281 62         78 my $stack;
282 62 100       2221 if ($self->active_action_path_isset) {
283 2         110 for (my $i = 0; $i < $self->active_action_path_count; $i++) {
284 2         82 my $idx = $self->active_action_path_index($i);
285 2 50       86 $stack = $stack ? $stack->[$idx] : $self->actions_index($idx - 1);
286             }
287             }
288              
289 62 100       522 if (! $stack) {
290 60         2359 $self->actions_push([ $action ]);
291             }
292             else {
293 2         7 push @$stack, [ $action ];
294             }
295              
296 62         521 return $self;
297             }
298              
299             =head3 add_handler( $subref )
300              
301             =over 4
302              
303             Handlers are LIFO. Enqueues the given handler. See L for more information on this.
304              
305             =back
306              
307             =cut
308              
309             sub add_handler {
310 34     34 1 762 my ($self, $handler) = @_;
311             # Unshift as it's LIFO
312 34         1189 $self->handlers_unshift($handler);
313 34         273 return $self;
314             }
315              
316             =head3 add_delay
317              
318             $sequence->add_delay(
319             5,
320             sub {
321             my $seq = shift;
322             $seq->failed("Took longer than 5 seconds to process");
323             # or you can just die and it'll do the same thing
324             die "Took longer than 5 seconds to process\n";
325             },
326             'timeout',
327             );
328              
329             Takes $delay, $action and optionally $name. If $name is given and another delay was set with the same name, that delay will be removed and replaced with this new delay. The $action is a subref which will take receive the sequence as it's only argument. The subref will be executed in an eval { }, with errors causing the failure of the sequence.
330              
331             The return value of the $action subref is usually ignored, but as a special case, if the subref returns [ $POE::Component::Sequence::RUN_AGAIN, $delay ], the same action will be run again after the indicated delay with the same name. This allows you to setup a regular delay without having to do a complex recursive algorithm.
332              
333             =cut
334              
335             sub add_delay {
336 5     5 1 555 my ($self, @args) = @_;
337             $self->do_on_run(sub {
338 5     5   182 $poe_kernel->post($self->alias, 'delay_add', @args);
339 5         37 });
340 5         431 return $self;
341             }
342              
343             =head3 adjust_delay
344              
345             $sequence->adjust_delay('timeout', 10);
346              
347             =cut
348              
349             sub adjust_delay {
350 2     2 1 486 my ($self, @args) = @_;
351             $self->do_on_run(sub {
352 2     2   64 $poe_kernel->post($self->alias, 'delay_adjust', @args);
353 2         12 });
354 2         83 return $self;
355             }
356              
357             =head3 remove_delay
358              
359             $sequence->remove_delay('timeout');
360              
361             =cut
362              
363             sub remove_delay {
364 2     2 1 555 my ($self, @args) = @_;
365             $self->do_on_run(sub {
366 2     2   63 $poe_kernel->post($self->alias, 'delay_remove', @args);
367 2         17 });
368 2         86 return $self;
369             }
370              
371             sub delay_add {
372 4     4 0 710 my ($self, $kernel, $delay, $action, $name) = @_[OBJECT, KERNEL, ARG0 .. $#_];
373 4         23 my $delay_id = $kernel->delay_set('delay_complete', $delay, $action, $name);
374 4 100       271 if (defined $name) {
375             #print STDERR "added delay $name\n";
376 2 50       94 if (my $existing_delay_id = $self->delays_index($name)) {
377 0         0 $kernel->alarm_remove($existing_delay_id);
378             }
379 2         79 $self->delays_set($name => $delay_id);
380             }
381             }
382              
383             sub delay_complete {
384 3     3 0 109984 my ($self, $kernel, $action, $name) = @_[OBJECT, KERNEL, ARG0, ARG1];
385 3         7 my $return = eval { $action->($self) };
  3         15  
386 3 50       172 if ($@) {
387 0         0 $kernel->yield('fail', $@);
388             }
389 3 100       27 if ($name) {
390 1         38 $self->delays_reset($name);
391             }
392 3 50 33     52 if ($return && ref $return eq 'ARRAY' && $return->[0] eq $RUN_AGAIN) {
      33        
393 0         0 $kernel->call($_[SESSION], 'delay_add', $return->[1], $action, $name);
394             }
395             }
396              
397             sub delay_adjust {
398 1     1 0 86 my ($self, $kernel, $name, $seconds) = @_[OBJECT, KERNEL, ARG0, ARG1];
399 1 50 33     39 return unless $name && $self->delays_isset($name);
400 1         46 my $delay_id = $self->delays_index($name);
401 1         11 $kernel->delay_adjust($delay_id, $seconds);
402             }
403              
404             sub delay_remove {
405 1     1 0 90 my ($self, $kernel, $name) = @_[OBJECT, KERNEL, ARG0];
406 1 50 33     39 return unless $name && $self->delays_isset($name);
407 1         40 my $delay_id = $self->delays_index($name);
408 1         19 $kernel->alarm_remove($delay_id);
409 1         116 $self->delays_reset($name);
410             #print STDERR "removed delay $name\n";
411             }
412              
413             sub do_on_run {
414 9     9 0 14 my ($self, $subref) = @_;
415              
416 9 100       299 if ($self->running) {
417 6         52 $subref->();
418             }
419             else {
420 3         108 $self->on_run_push($subref);
421             }
422             }
423              
424             =head3 run()
425              
426             =over 4
427              
428             Starts the sequence. This is mandatory - if you never call C, the sequence will never start.
429              
430             =back
431              
432             =cut
433              
434             sub run {
435 24     24 1 654 my $self = shift;
436              
437 24         1251 $self->running(1);
438 24         1132 $self->pause_state(0);
439 24         892 $poe_kernel->post($self->alias, 'next');
440              
441 24 100       3698 if ($self->on_run_count) {
442 1         40 while (my $subref = $self->on_run_shift) {
443 3         239 $subref->();
444             }
445             }
446              
447 24         374 return $self
448             }
449              
450             =head2 Object Accessors, public
451              
452             =head3 heap(), heap_index(), heap_set(), etc.
453              
454             =over 4
455              
456             Think of C like the POE::Session heap - it is simply a hashref where you may store and retrieve data from while inside an action. See L for all the various heap_* calls that are available to you. The most important are:
457              
458             =over 4
459              
460             =item * heap_index( $key )
461              
462             Returns the value at index $key
463              
464             =item * heap_set( $key1 => $value1, $key2 => $value2, ... )
465              
466             Sets the given key/value pairs, overriding previous values
467              
468             =item * heap( )
469              
470             Returns all the key/value pairs of the heap in no particular order
471              
472             =back
473              
474             =back
475              
476             =head3 options_*()
477              
478             =over 4
479              
480             In usage identical to C above, this is another object hashref. Its values are intended to modify how the handlers perform their actions. See L for more info.
481              
482             =back
483              
484             =head3 alias()
485              
486             =over 4
487              
488             Returns the L alias for this sequence.
489              
490             =back
491              
492             =head3 result()
493              
494             =over 4
495              
496             Stores the return value of the last action that was executed. See L.
497              
498             =back
499              
500             =head2 Object Methods, public
501              
502             =head3 pause()
503              
504             =over 4
505              
506             Pauses the sequence
507              
508             =back
509              
510             =cut
511              
512             sub pause {
513 27     27 1 1097 my $self = shift;
514              
515 27         1615 $self->pause_state( $self->pause_state + 1 );
516             }
517              
518             =head3 resume()
519              
520             =over 4
521              
522             Resumes the sequence. You must call resume() as many times as pause() was called, as they are cumulative.
523              
524             =back
525              
526             =cut
527              
528             sub resume {
529 20     20 1 1346 my $self = shift;
530              
531 20 50       697 if ($self->pause_state > 0) {
532 20         841 $self->pause_state( $self->pause_state - 1 );
533             }
534 20 100       1010 if ($self->pause_state == 0) {
535 19         773 $poe_kernel->post($self->alias, 'next');
536             }
537             }
538              
539             =head3 finished( @args )
540              
541             =over 4
542              
543             Marks the sequence as finished, preventing further actions to be handled. The normal callbacks are called one by one, receiving ($sequence, @args) as arguments. If the normal callbacks die, execution is handed to C, and then to C.
544              
545             =back
546              
547             =cut
548              
549             sub finished {
550 2     2 1 710 my $self = shift;
551 2         71 $poe_kernel->post($self->alias, 'finish', @_);
552             #$self->is_finished(1);
553             }
554              
555             sub finish {
556 18     18 1 3831 my ($self, @args) = @_[OBJECT, ARG0 .. $#_];
557 18 100       635 return if $self->is_finished();
558 17         673 $self->is_finished(1);
559              
560 17         1875 foreach my $callback ($self->callbacks) {
561 30 100       381 next unless $callback->{type} eq 'normal';
562 6         9 eval {
563 6         25 $callback->{code}($self, @args);
564             };
565 6 100       3164 if ($@) {
566 1         4 $self->failed($@);
567 1         114 return;
568             }
569              
570             # Callback can redirect to 'fail'; if so, stop execution
571 5 50       187 if ($self->is_error) {
572 0         0 return;
573             }
574             }
575              
576 16         567 $poe_kernel->post($self->alias, 'finally', @_);
577             }
578              
579             =head3 failed( @args )
580              
581             =over 4
582              
583             Marks the sequence as failed, finishing the sequence. This will happen if an action dies, if C is explicitly called by the user, or if a normal callback dies. The error callbacks are called one by one, receiving ($sequence, @args) as arguments. Afterwards, execution moves to C.
584              
585             =back
586              
587             =cut
588              
589             sub failed {
590 5     5 1 16 my $self = shift;
591 5         222 $poe_kernel->post($self->alias, 'fail', @_);
592             #$self->is_error(1);
593             #$self->is_finished(1);
594             }
595              
596             sub fail {
597 5     5 1 791 my ($self, @args) = @_[OBJECT, ARG0 .. $#_];
598 5         235 $self->is_error(1);
599 5         198 $self->is_finished(1);
600              
601 5         192 foreach my $callback ($self->callbacks) {
602 14 100       4557 next unless $callback->{type} eq 'error';
603             # Don't catch exceptions here
604 5         32 $callback->{code}($self, @args);
605             }
606              
607 5         298 $poe_kernel->post($self->alias, 'finally', @_);
608             }
609              
610             =head2 Object Methods, private, POE states
611              
612             These methods can't be called directly, but instead can be 'yield'ed or 'post'ed to via POE:
613              
614             $poe_kernel->post( $sequence->alias, 'finally', @args );
615              
616             =head3 finish( @args )
617              
618             =over 4
619              
620             See C.
621              
622             =back
623              
624             =head3 fail( @args )
625              
626             =over 4
627              
628             See C.
629              
630             =back
631              
632             =head3 finally( @args )
633              
634             =over 4
635              
636             Walks through each 'finally' callback, passing ($sequence, @args) to each.
637              
638             =back
639              
640             =cut
641              
642             sub finally {
643 21     21 1 4606 my ($self, $kernel, @args) = @_[OBJECT, KERNEL, ARG0 .. $#_];
644              
645 21         765 foreach my $callback ($self->callbacks) {
646 43 100       8464 next unless $callback->{type} eq 'finally';
647             # Don't catch exceptions here
648 30         239 $callback->{code}($self, @args);
649             }
650              
651             # Remove any alarms that were set; these can't live past the life of the session
652 21         3637 $kernel->alarm_remove_all();
653             }
654              
655             =head3 next()
656              
657             =over 4
658              
659             The main loop of the code, C steps through each action on the stack, handling each in turn. See L for more info on this.
660              
661             =back
662              
663             =cut
664              
665             sub _next_recurse {
666 173     173   1792 my ($stack, $path, $path_index) = @_;
667 173 100       380 $path_index = 0 unless defined $path_index;
668              
669 173         580 my $stack_index = $path->[$path_index];
670              
671             # We're guessing here; if there's a value, return it, otherwise undef
672 173 100       406 if (! defined $stack_index) {
673 97         147 my $new_stack = $stack->[1];
674 97 100       209 if (defined $new_stack) {
675 27         53 $path->[$path_index] = 1;
676 27         83 return $new_stack->[0];
677             }
678 70         148 return undef;
679             }
680              
681             # Depth search first
682 76         230 my $value = _next_recurse($stack->[$stack_index], $path, $path_index + 1);
683 76 100       175 if (defined $value) {
684 3         19 return $value;
685             }
686              
687             # Breadth search next; return the next value in my stack
688 73 100       204 if ($stack_index + 1 >= int @$stack) {
689 25         70 return undef;
690             }
691              
692             # Adjust the path and return the next value
693 48         67 $path->[$path_index]++;
694 48         93 splice @$path, $path_index + 1;
695 48         298 return $stack->[$stack_index + 1][0];
696             }
697              
698             sub get_next_action {
699 63     63 0 124 my $self = shift;
700              
701             ## Find the next action to execute
702              
703             # Start with the last action path we know
704 63         90 my @active_action_path;
705 63 100       2016 if ($self->active_action_path_isset) {
706 39         2585 @active_action_path = $self->active_action_path;
707             }
708              
709             # Find the next action, and modify the @active_action_path in place
710 63         2685 my $action = _next_recurse([ undef, $self->actions ], \@active_action_path);
711              
712             # Store the newly changed action path
713 63         2037 $self->active_action_path(@active_action_path);
714              
715 63         1159 return $action;
716             }
717              
718             sub next {
719 60     60 1 11578 my $self = $_[OBJECT];
720              
721 60 100 66     2218 return if $self->pause_state || $self->is_finished;
722              
723 58         3430 my $action = $self->get_next_action();
724              
725 58 100       145 if (defined $action) {
726             # Create request to pass to handlers
727 53         1677 my $request = {
728             action => $action,
729             options => { $self->options },
730             };
731              
732             # Perform auto_pause
733 53 100       871 if ($request->{options}{auto_pause}) {
734 7         22 $self->pause();
735             }
736              
737             # Iterate over handlers
738 53         140 my $handled;
739 53         1720 foreach my $handler ($self->handlers) {
740              
741 53         665 my $handler_result;
742 53         88 eval {
743 53         128 $handler_result = &$handler($self, $request);
744             };
745 53 100       410 if ($@) {
746 2         10 $self->failed($@);
747 2         195 return;
748             }
749            
750 51 50       184 if ($handler_result->{deferred}) {
    100          
751 0         0 next;
752             }
753             elsif ($handler_result->{skip}) {
754             # Handler wants to skip to the next action
755             # Make sure we're unpaused
756 1         33 $self->pause_state(0);
757 1         21 $handled = 1;
758 1         3 last;
759             }
760             else {
761 50         1709 $self->result($handler_result->{value});
762 50         384 $handled = 1;
763 50         133 last;
764             }
765             }
766 51 50       156 if (! $handled) {
767 0         0 die "No handler handled action '$action'";
768             }
769              
770             # Perform auto_resume; this refers to the request's copy of the options
771             # as the handler may have performed the auto_resume, negating my need to
772             # do so.
773 51 100 66     350 if ($request->{options}{auto_resume} && $self->pause_state) {
774 4         162 $self->pause_state( $self->pause_state - 1 );
775             }
776             }
777              
778 56 100       1901 if ($self->pause_state) {
779 22         242 return;
780             }
781              
782             # Pull the next action, but don't modify my path (a look ahead, so to speak)
783 34 100       1330 if (! defined _next_recurse([ undef, $self->actions ], [ $self->active_action_path ])) {
784 16         557 $poe_kernel->post($self->alias, 'finish');
785             }
786             else {
787 18         571 $poe_kernel->post($self->alias, 'next');
788             }
789             }
790              
791             =head1 OPTIONS
792              
793             Some options affect the default handler. Other options may be intended for plugin handlers.
794              
795             =head2 auto_pause
796              
797             =over 4
798              
799             Before each action is performed, the sequence is paused.
800              
801             =back
802              
803             =head2 auto_resume
804              
805             =over 4
806              
807             After each action is performed, the sequence is resumed.
808              
809             =back
810              
811             =cut
812              
813             =head1 HANDLERS
814              
815             To make the sequence a flexible object, it's not actually mandatory that you use CODEREFs as your actions. If you wanted to provide the name of a POE session and state to be posted to, you could write a handler that does what you need given the action passed. For example:
816              
817             POE::Component::Sequence
818             ->new(
819             [ 'my_session', 'my_state', @args ],
820             )
821             ->add_handler(sub {
822             my ($sequence, $request) = @_;
823              
824             my $action = $request->{action};
825             if (! ref $action || ref $action ne 'ARRAY') {
826             return { deferred => 1 };
827             }
828              
829             my $session = shift @$action;
830             my $state = shift @$action;
831             my @args = @$action;
832              
833             $sequence->pause;
834             $poe_kernel->post($session, $state, $sequence, \@args);
835              
836             # Let's just hope $state will unpause the sequence when it's done...
837             })
838             ->run;
839              
840             When an action is being handled, a shared request object is created:
841              
842             my $request = {
843             action => $action,
844             options => \%sequence_options,
845             }
846              
847             This request is handed to each handler in turn (LIFO), with the signature ($sequence, $request). The handler is expected to return either a HASHREF in response or throw an exception.
848              
849             If a handler returns the key 'deferred', the next handler is tried. If the handler returns the key 'skip', the action is skipped. Otherwise, the handler is expected to return the key 'value', which is the optional return value of the $action. This return value is stored in $sequence->result. This value will be overwritten upon each action.
850              
851             The default handler handles actions only of type CODEREFs, passing to the action the arg $self.
852              
853             If you'd like to add default handlers globally rather than calling C for each sequence, push the handler onto @POE::Component::Sequence::_plugin_handlers. See for an example of this.
854              
855             =cut
856              
857             sub default_handler {
858 38     38 0 62 my ($self, $request) = @_;
859              
860 38         64 my $action = $request->{action};
861              
862 38 50 33     291 if (! defined $action || ! ref $action) {
    100          
    50          
863 0         0 return { deferred => 1 };
864             }
865             elsif (ref $action eq 'HASH') {
866             ## Options
867              
868             # Allow to bypass normal chaining calls
869 1         3 foreach my $method (qw(
870             add_callback add_error_callback add_finally_callback
871             add_action add_handler run
872             )) {
873 6 100       18 next unless $action->{$method};
874 2         9 $self->$method(delete $action->{$method});
875             }
876              
877             # Store the remainder in options
878 1         35 $self->options_set( %$action );
879              
880 1         14 return { skip => 1 };
881             }
882             elsif (ref $action eq 'CODE') {
883             ## Normal code ref
884              
885 37         144 my $value = &$action($self);
886 36         25718 return { value => $value };
887             }
888             else {
889 0           return { deferred => 1 };
890             }
891             }
892              
893             1;
894              
895             __END__