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__ |