File Coverage

blib/lib/POE/Session/Multiplex.pm
Criterion Covered Total %
statement 167 182 91.7
branch 67 90 74.4
condition 11 18 61.1
subroutine 26 27 96.3
pod 13 15 86.6
total 284 332 85.5


line stmt bran cond sub pod time code
1             package POE::Session::Multiplex;
2              
3 5     5   1775026 use strict;
  5         16  
  5         211  
4 5     5   27 use warnings;
  5         11  
  5         187  
5              
6 5     5   25 use Carp;
  5         15  
  5         415  
7 5     5   1270 use POE;
  5         1157080  
  5         31  
8 5     5   118358 use POE::Session;
  5         10  
  5         23  
9              
10 5     5   299 use Scalar::Util qw( blessed );
  5         11  
  5         2505  
11              
12             require Exporter;
13              
14             our @ISA = qw(Exporter POE::Session);
15             our %EXPORT_TAGS = ( 'all' => [ qw( ev evs rsvp evo evos ) ] );
16              
17             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
18              
19             our @EXPORT = qw( ev evo evs rsvp );
20              
21             our $VERSION = '0.0600';
22             our $CURRENTOBJ;
23              
24             our $START;
25              
26             #######################################
27             our $LAST_OFF;
28             BEGIN {
29 5     5   15 *SE_NAMESPACE = \&POE::Session::SE_NAMESPACE;
30 5         18 *SE_OPTIONS = \&POE::Session::SE_OPTIONS;
31 5         23 *SE_STATES = \&POE::Session::SE_STATES;
32              
33             ## +1 and +2 used by PlainCall
34 5 50       79 if( POE::Session->can( 'SE_ID' ) ) {
35             # POE 1.300 +
36 5         12 *SE_ID = \&POE::Session::SE_ID;
37 5         634 eval '
38             *SE_OBJECTS = sub () { POE::Session::SE_ID+3 };
39             *SE_STATERE = sub () { POE::Session::SE_ID+4 };
40             *SE_ISAPLAIN = sub () { POE::Session::SE_ID+5 };
41             ';
42             }
43             else {
44             # POE 1.299-
45 0         0 *SE_OBJECTS = sub () { POE::Session::SE_STATES+3 };
46 0         0 *SE_STATERE = sub () { POE::Session::SE_STATES+4 };
47 0         0 *SE_ISAPLAIN = sub () { POE::Session::SE_STATES+5 };
48             }
49              
50 5         53 *EN_SIGNAL = \&POE::Session::EN_SIGNAL;
51 5         15 *EN_DEFAULT = \&POE::Session::EN_DEFAULT;
52              
53 5         11 *OPT_TRACE = \&POE::Session::OPT_TRACE;
54 5         11 *OPT_DEBUG = \&POE::Session::OPT_DEBUG;
55 5         12173 *OPT_DEFAULT = \&POE::Session::OPT_DEFAULT;
56             }
57              
58             sub OH_OBJECT () { 0 }
59             sub OH_NAME () { 1 }
60             sub OH_OURS () { 2 }
61              
62             ############################################################################
63             sub _loggable
64             {
65 0     0   0 my( $self ) = @_;
66 0         0 $POE::Kernel::poe_kernel->_data_alias_loggable( $_[0] );
67             }
68              
69             sub create
70             {
71 6     6 1 226408 my $package = shift;
72              
73 6 100       93 if( $package->isa( 'POE::Session::PlainCall' ) ) {
74 2         19 return $package->POE::Session::PlainCall::create( @_ );
75             }
76             else {
77 4         57 return $package->SUPER::create( @_ );
78             }
79             }
80              
81             sub instantiate
82             {
83 6     6 1 227 my $package = shift;
84 6         47 my $self = $package->SUPER::instantiate( @_ );
85 6         83 $self->__init;
86 6         48 return $self;
87             }
88              
89             sub __init
90             {
91 6     6   17 my( $self ) = @_;
92 6 50       56 if( $self->[SE_OBJECTS] ) {
93 0         0 die "Definition of POE::Session changed! $self needs to be modified.\n";
94             }
95 6         18 $self->[SE_OBJECTS] = {};
96 6         42 $self->[SE_ISAPLAIN] = $self->isa( 'POE::Session::PlainCall' );
97 6 100       26 if( $self->[SE_ISAPLAIN] ) {
98 2         11 $self->POE::Session::PlainCall::__init;
99             }
100             }
101              
102             #######################################
103             sub _invoke_state
104             {
105 59     59   37324 my( $self, $source_session, $state, $etc, $file, $line, $fromstate ) = @_;
106              
107 59 100       228 $self->set_objectre unless $self->[SE_STATERE];
108              
109 59 50       234 if ($self->[SE_OPTIONS]->{+OPT_TRACE}) {
110 0         0 POE::Kernel::_warn( $self->_loggable,
111             " -> $state (from $file at $line)\n"
112             );
113             }
114            
115 59 100       1927 if( $state =~ /^$self->[SE_STATERE]$/ ) {
116 34         89 my $obj_name = $1;
117 34         73 my $obj_state = $2;
118              
119 34         119 my( $object, $method ) = $self->handler_for( $obj_name, $obj_state, $state );
120 34 50       96 if( $method ) {
121 34 50       92 if ($self->[SE_OPTIONS]->{+OPT_TRACE}) {
122 0         0 POE::Kernel::_warn( $self->_loggable,
123             " OBJECT=$obj_name METHOD=$method\n"
124             );
125             }
126              
127 34         71 local $CURRENTOBJ = $obj_name;
128 34 100       96 if( $self->[SE_ISAPLAIN] ) {
129 10         95 local $POE::Session::PlainCall::POE_HOLDER = bless [
130             ( $object, # object
131             $self, # session
132             $POE::Kernel::poe_kernel, # kernel
133             $self->[SE_NAMESPACE], # heap
134             $obj_state, # state
135             $source_session, # sender
136             undef, # unused #6
137             $file, # caller file name
138             $line, # caller file line
139             $fromstate, # caller state
140             $method, # method
141             $etc, # args
142             ) ], 'POE::Session::PlainCall::Holder';
143 10         52 return $object->$method( @$etc );
144             }
145 24         120 return $object->$method(
146             $self, # SESSION
147             $POE::Kernel::poe_kernel, # KERNEL
148             $self->[SE_NAMESPACE], # HEAP
149             $obj_state, # STATE
150             $source_session, # SENDER
151             undef, # unused #6
152             $file, # CALLER_FILE
153             $line, # CALLER_LINE
154             $fromstate, # CALLER_STATE
155             @$etc, # ARG0, ARG1, ARG2
156             );
157             }
158             }
159              
160 25 50       83 if ($self->[SE_OPTIONS]->{+OPT_TRACE}) {
161 0         0 POE::Kernel::_warn( $self->_loggable,
162             " -> $state (not an multiplex state)\n"
163             );
164             }
165              
166             ## Skip out if it's not our state
167 25 100       74 if( $self->[SE_ISAPLAIN] ) {
168 11         84 return shift->POE::Session::PlainCall::_invoke_state( @_ );
169             }
170             else {
171 14         85 return shift->SUPER::_invoke_state( @_ );
172             }
173             }
174              
175              
176             #######################################
177             sub evo ($$)
178             {
179 35     35 1 528 my( $obj_name, $event ) = @_;
180 35         271 return "$obj_name->$event";
181             }
182              
183             #######################################
184             sub ev ($)
185             {
186 19     19 1 44151 my( $event ) = @_;
187 19 50       71 croak "Can't call ev outside of a multiplexed object" unless $CURRENTOBJ;
188 19         61 return evo $CURRENTOBJ, $event;
189             }
190              
191             #######################################
192             sub evs ($)
193             {
194 3     3 1 7 my( $event ) = @_;
195 3 50       11 croak "Can't call evs outside of a multiplexed object" unless $CURRENTOBJ;
196 3         15 my $self = $poe_kernel->get_active_session;
197 3 50       15 croak "Can't call evs outside of a multiplexed session" unless $self;
198 3         10 return $self->ID, ev$event;
199             }
200              
201             #######################################
202             sub rsvp ($)
203             {
204 3     3 1 1751 return [ evs $_[0] ];
205             }
206              
207             #######################################
208             sub evos ($$$)
209             {
210 3     3 1 5 my( $session, $obj_name, $event ) = @_;
211 3 50       18 $session = $session->ID if blessed $session;
212 3         14 return $session, evo $obj_name, $event;
213             }
214              
215              
216             #######################################
217             sub set_objectre
218             {
219 13     13 0 65 my( $self ) = @_;
220 13         100 my $re = '(' . join( '|', map quotemeta, keys %{$self->[SE_OBJECTS]} ).
  13         102  
221             ')->(.+)';
222 13         74 $self->[SE_STATERE] = $re;
223             }
224              
225             #######################################
226             # $obj_name => name of object
227             # $obj_state => clean state name
228             # $state => raw state name ($obj_name->$obj_state)
229             sub handler_for
230             {
231 34     34 0 71 my( $self, $obj_name, $obj_state, $state ) = @_;
232              
233 34         92 my @ret = $self->_handler_for( $obj_name, $state );
234 34 50       87 return @ret if @ret;
235              
236 34         80 return $self->_handler_for( $obj_name, $obj_state );
237             }
238              
239             sub _handler_for
240             {
241 102     102   173 my( $self, $obj_name, $state ) = @_;
242 102         202 my $handler = $self->[SE_STATES]->{$state};
243 102 100       285 return unless $handler;
244 39 50       97 return if 'CODE' eq ref $handler;
245              
246 39         154 my $def = $self->[SE_OBJECTS]->{$obj_name};
247 39 50       97 return unless $def;
248              
249             # Same object?
250 39 50 33     446 if( blessed $handler->[0] and $handler->[0] eq $def->[OH_OBJECT] ) {
    50          
251 0         0 return $def->[OH_OBJECT], $handler->[1];
252             }
253             # Object of the right package?
254             elsif( $def->[OH_OBJECT]->isa( $handler->[0] ) ) {
255 39         149 return $def->[OH_OBJECT], $handler->[1];
256             }
257 0         0 return;
258             }
259              
260             #######################################
261             sub object
262             {
263 25     25 1 7095 my( $self, $obj_name, @def ) = @_;
264 25 100       60 if( @def ) {
265 22 50       230 if( blessed $obj_name ) {
266 0         0 @def = ( $obj_name, @def );
267 0         0 $obj_name = $self->_obj_name( $def[0] );
268             }
269 22         88 $self->object_register( name => $obj_name,
270             object => $def[0],
271             states => $def[1]
272             );
273             }
274             else {
275 3         12 $self->object_unregister( $obj_name );
276             }
277             }
278              
279             sub _obj_name
280             {
281 4     4   5 my( $package, $obj ) = @_;
282 4 100       29 return "$obj" unless $obj->can( '__name' );
283 2         6 my $name = $obj->__name;
284 2 50       9 die ref( $obj ), "->__name must return the object's name" unless $name;
285 2         6 return $name;
286             }
287              
288              
289             #######################################
290             sub object_get
291             {
292 8     8 1 2499 my( $self, $obj_name ) = @_;
293 8 100       24 return unless exists $self->[SE_OBJECTS]->{ $obj_name };
294 5         12 return $self->[SE_OBJECTS]->{ $obj_name }[OH_OBJECT];
295             }
296              
297             #######################################
298             sub object_register
299             {
300 24     24 1 578 my( $self, @def ) = @_;
301              
302 24 100       57 if( 1==@def ) {
303 1         2 @def = ( object => $def[0] );
304             }
305 24         66 my %def = ( @def );
306              
307 24         37 my $object = $def{object};
308 24 50       78 croak "You must supply an object" unless $object;
309              
310 24   66     65 my $obj_name = $def{name} || $self->_obj_name( $object );
311 24 50       61 croak "You may not include -> in an object name" if $obj_name =~ /->/;
312 24   66     159 my $states = $def{states} || $def{events};
313              
314 24 100       71 if( $self->[SE_OBJECTS]->{ $obj_name } ) {
315 3         6 $self->object_unregister( $obj_name );
316             }
317 24         32 $self->[SE_STATERE] = '';
318              
319 24         66 my $hold = $self->[SE_OBJECTS]->{ $obj_name } = [];
320 24 100       61 if( $states ) {
321             # Tediously define states for this object
322 3         4 my $ours = $hold->[OH_OURS] = [];
323 3         6 local $CURRENTOBJ = $obj_name;
324 3 50       7 $states = [ $states ] unless ref $states;
325 3 100       8 if( 'HASH' eq ref $states ) {
326 2         9 while( my( $event, $method ) = each %$states ) {
327 2         5 push @$ours, ev$event;
328 2         8 $self->_register_state( $ours->[-1], $object, $method );
329             }
330             }
331             else {
332 1         2 foreach my $event ( @$states ) {
333 1         4 push @$ours, ev$event;
334 1         7 $self->_register_state( $ours->[-1], $object, $event );
335             }
336             }
337             }
338             else {
339             # Make sure there are some states defined for this object's class
340 21         35 my $ok = 0;
341 21         289 my $package = ref $object;
342 21         25 foreach my $handler ( values %{ $self->[SE_STATES] } ) {
  21         71  
343 32 100       80 next unless 'ARRAY' eq ref $handler;
344 26 50 33     100 next if blessed $handler->[0] and $handler->[0] ne $object;
345 26 100 100     109 next unless $package eq $handler->[0] or
346             $object->isa( $handler->[0] );
347 20         31 $ok = 1;
348 20         32 last;
349             }
350 21 100       294 croak "No package_states defined for package $package" unless $ok;
351             }
352 23         83 $hold->[OH_NAME] = $obj_name;
353 23         41 $hold->[OH_OBJECT] = $object;
354            
355             # greet the object
356 23 100       71 if( $self->_handler_for( $obj_name, "_psm_begin" ) ) {
357 4         10 $poe_kernel->call( $self, evo( $obj_name, "_psm_begin" ) );
358             }
359              
360 23         2283 return 1;
361             }
362              
363             #######################################
364             sub object_unregister
365             {
366 11     11 1 1711 my( $self, $obj_name ) = @_;
367              
368 11 100       35 if( blessed $obj_name ) {
369 2         5 $obj_name = $self->_obj_name( $obj_name );
370             }
371             # say good bye the object
372 11 100       22 if( $self->_handler_for( $obj_name, "_psm_end" ) ) {
373 1         3 $poe_kernel->call( $self, evo( $obj_name, "_psm_end" ) );
374             }
375 11         580 my $def = delete $self->[SE_OBJECTS]->{ $obj_name };
376 11 50       165 unless( $def ) {
377 0         0 carp "Attempt to unregister unknown object $obj_name";
378 0         0 return;
379             }
380              
381 11         62 $self->[SE_STATERE] = '';
382 11 100 66     110 return unless $def and $def->[OH_OURS];
383              
384 3         4 foreach my $event ( @{ $def->[OH_OURS] } ) {
  3         7  
385 3         9 $self->_register_state( $event );
386             }
387 3         29 return 1;
388             }
389              
390             #######################################
391             sub object_list
392             {
393 2     2 1 197 my( $self ) = @_;
394 2         2 return keys %{ $self->[SE_OBJECTS] };
  2         8  
395             }
396              
397              
398             #######################################
399             sub package_register
400             {
401 2     2 1 39 my( $self, $package, $states ) = @_;
402              
403 2 50       8 $states = [ $states ] unless ref $states;
404 2 100       25 if( 'HASH' eq ref $states ) {
405 1         4 while( my( $event, $method ) = each %$states ) {
406 1         4 $self->_register_state( $event, $package, $method );
407             }
408             }
409             else {
410 1         3 foreach my $event ( @$states ) {
411 5         58 $self->_register_state( $event, $package, $event );
412             }
413             }
414 2         29 return 1;
415             }
416              
417             1;
418              
419              
420             __END__