File Coverage

blib/lib/POE/Session/PlainCall.pm
Criterion Covered Total %
statement 129 150 86.0
branch 37 60 61.6
condition 12 19 63.1
subroutine 34 36 94.4
pod 4 4 100.0
total 216 269 80.3


line stmt bran cond sub pod time code
1             package POE::Session::PlainCall;
2              
3 5     5   1649505 use strict;
  5         13  
  5         227  
4 5     5   29 use warnings;
  5         10  
  5         164  
5              
6 5     5   28 use Carp;
  5         14  
  5         422  
7 5     5   1058 use POE;
  5         71558  
  5         36  
8 5     5   122533 use Scalar::Util qw( blessed );
  5         11  
  5         996  
9              
10             require Exporter;
11              
12             our $VERSION = '0.0301';
13              
14              
15             #######################################
16             our $POE_HOLDER;
17             $POE_HOLDER = bless [], 'POE::Session::PlainCall::Holder';
18              
19 131     131 1 42953 sub poe () { $POE_HOLDER }
20              
21             #######################################
22 5     5   35 use base qw( Exporter POE::Session );
  5         9  
  5         2618  
23              
24             our %EXPORT_TAGS = ( 'all' => [ qw( $poe_kernel poe ) ] );
25              
26             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
27              
28             our @EXPORT = qw( $poe_kernel poe );
29              
30             #######################################
31             BEGIN {
32 5     5   17 *SE_NAMESPACE = \&POE::Session::SE_NAMESPACE;
33 5         11 *SE_OPTIONS = \&POE::Session::SE_OPTIONS;
34 5         10 *SE_STATES = \&POE::Session::SE_STATES;
35 5 50       52 if( POE::Session->can( 'SE_ID' ) ) {
36             # POE 1.300 +
37 5         11 *SE_ID = \&POE::Session::SE_ID;
38 5         542 eval '
39             *SE_RUNNING = sub () { POE::Session::SE_ID+1 };
40             *SE_MYSTATES = sub () { POE::Session::SE_ID+2 };
41             ';
42             }
43             else {
44             # POE 1.299-
45 0         0 *SE_RUNNING = sub () { POE::Session::SE_STATES+1 };
46 0         0 *SE_MYSTATES = sub () { POE::Session::SE_STATES+2 };
47             }
48              
49 5         19 *EN_SIGNAL = \&POE::Session::EN_SIGNAL;
50 5         47 *EN_DEFAULT = \&POE::Session::EN_DEFAULT;
51              
52 5         12 *OPT_TRACE = \&POE::Session::OPT_TRACE;
53 5         11 *OPT_DEBUG = \&POE::Session::OPT_DEBUG;
54 5         7955 *OPT_DEFAULT = \&POE::Session::OPT_DEFAULT;
55             }
56              
57             ############################################################################
58             sub _loggable
59             {
60 0     0   0 my( $self ) = @_;
61 0 0       0 return $self unless $self->[SE_RUNNING];
62 0         0 $POE::Kernel::poe_kernel->_data_alias_loggable( $_[0] );
63             }
64              
65             our %OURS;
66             sub instantiate
67             {
68 6     6 1 109 my $self = shift->SUPER::instantiate( @_ );
69 6 50 33     117 if( $self->[SE_RUNNING] or $self->[SE_MYSTATES] ) {
70 0         0 die "Definition of POE::Session changed! $self needs to be modified.\n";
71             }
72 6         21 $self->__init;
73 6         17 return $self;
74             }
75              
76             sub __init
77             {
78 6     6   22 my( $self ) = @_;
79 6         14 $self->[SE_RUNNING] = 0;
80             # warn "keys = ", join ', ', keys %OURS;
81 6         28 $self->[SE_MYSTATES] = {%OURS};
82 6 50       64 $self->SUPER::__init if $self->can( 'SUPER::__init' );
83             }
84              
85             #######################################
86             sub create
87             {
88 6     6 1 1100 my( $package, @params ) = @_;
89              
90 6 50       27 if (@params & 1) {
91 0         0 croak "odd number of events/handlers (missing one or the other?)";
92             }
93 6         34 my %args = @params;
94              
95 6         19 my $obj = delete $args{object};
96 6 50       34 unless( $obj ) {
97 6         20 my $package = delete $args{package};
98 6 100       35 if( $package ) {
99 5         20 my $args = delete $args{ctor_args};
100 5   100     98 $args ||= [];
101 5         34 $obj = $package->new( @$args );
102             }
103             }
104              
105 6 100       930 if( $obj ) {
106 5   66     31 my $events = delete $args{events} ||
107             delete $args{states};
108 5 50       18 croak "Parameters 'events' or 'states' are required if you supply 'object' or 'package'"
109             unless defined $events;
110 5   100     27 $args{object_states} ||= [];
111 5         8 push @{ $args{object_states} }, $obj, $events;
  5         22  
112             }
113              
114 6         38 local %OURS;
115 6         24 $package->__process_args( \%args );
116             # warn "keys = ", join ', ', keys %OURS;
117              
118 6         66 my $self = $package->SUPER::create( %args );
119 6         2094 $self->[SE_RUNNING] = 1;
120 6         103 return $self;
121             }
122              
123             #######################################
124             ## Find all state names and mark them as ours
125             sub __process_args
126             {
127 6     6   13 my( $package, $args ) = @_;
128 6         13 foreach my $f ( qw( package_states object_states ) ) {
129 12 100       38 next unless $args->{$f};
130 7         12 my $L = $args->{$f};
131 7         26 for(my $off = 1; $off <= $#$L ; $off+=2 ) {
132 8         20 my $states = $args->{$f}[$off];
133 8 100       53 if( 'HASH' eq ref $states ) {
    50          
134 4         20 $package->__process_ours( keys %$states );
135             }
136             elsif( 'ARRAY' eq ref $states ) {
137 4         26 $package->__process_ours( @$states );
138             }
139             }
140             }
141 6 100       24 return unless $args->{inline_states};
142 1         1 $package->__process_ours( keys %{ $args->{inline_states} } );
  1         4  
143             }
144              
145             #######################################
146             sub __process_ours
147             {
148 9     9   23 my( $package, @list ) = @_;
149             # warn "OURS=", join ', ', @list;
150 9         72 @OURS{ @list } = (1) x (0+@list);
151             }
152              
153             #######################################
154             sub _invoke_state
155             {
156 37     37   6271755 my( $self, $source_session, $state, $etc, $file, $line, $fromstate ) = @_;
157              
158 37 100 100     210 unless( $self->[SE_MYSTATES]{$state} or $self->[SE_MYSTATES]{+EN_DEFAULT} ) {
159 5 50       21 if ($self->[SE_OPTIONS]->{+OPT_TRACE}) {
160 0         0 POE::Kernel::_warn( $self->_loggable,
161             " -> $state (external event)\n"
162             );
163             }
164             ## Skip out if it's not our state
165 5         44 return shift->SUPER::_invoke_state( @_ );
166             }
167              
168             # Most of the following was lifted AS IS from POE::Session::_invoke_state
169             # and simply reformated because I'm a tad anal.
170             # But then, POE::Session overloading is like that...
171            
172             # Trace the state invocation if tracing is enabled.
173              
174 32 50       107 if ($self->[SE_OPTIONS]->{+OPT_TRACE}) {
175 0         0 POE::Kernel::_warn( $self->_loggable,
176             " -> $state (from $file at $line)\n"
177             );
178             }
179              
180             # The desired destination state doesn't exist in this session.
181             # Attempt to redirect the state transition to _default.
182              
183 32 100       97 unless (exists $self->[SE_STATES]->{$state}) {
184              
185             # There's no _default either; redirection's not happening today.
186             # Drop the state transition event on the floor, and optionally
187             # make some noise about it.
188              
189 2 50       9 unless (exists $self->[SE_STATES]->{+EN_DEFAULT}) {
190 0 0       0 $! = exists &Errno::ENOSYS ? &Errno::ENOSYS : &Errno::EIO;
191 0 0 0     0 if ($self->[SE_OPTIONS]->{+OPT_DEFAULT} and $state ne EN_SIGNAL) {
192 0         0 my $loggable_self = $self->_loggable;
193 0         0 POE::Kernel::_warn(
194             "a '$state' event was sent from $file at $line to $loggable_self ",
195             "but $loggable_self has neither a handler for it ",
196             "nor one for _default\n"
197             );
198             }
199 0         0 return undef;
200             }
201              
202             # If we get this far, then there's a _default state to redirect
203             # the transition to. Trace the redirection.
204              
205 2 50       8 if ($self->[SE_OPTIONS]->{+OPT_TRACE}) {
206 0         0 POE::Kernel::_warn( $self->_loggable,
207             " -> $state redirected to _default\n"
208             );
209             }
210              
211             # Transmogrify the original state transition into a corresponding
212             # _default invocation. ARG1 is copied from $etc so it can't be
213             # altered from a distance.
214              
215 2         7 $etc = [ $state, [@$etc] ];
216 2         4 $state = EN_DEFAULT;
217             }
218              
219             #####
220             ## The following is unique/specific to POE::Session::PlainCall
221              
222             # If we get this far, then the state can be invoked. So invoke it
223             # already!
224              
225             # Inline states are invoked this way.
226              
227 32         68 my $handler = $self->[SE_STATES]->{$state};
228 32 100       97 if( ref $handler eq 'CODE') {
229 1         158 local $POE_HOLDER = bless [
230             ( undef, # object
231             $self, # session
232             $POE::Kernel::poe_kernel, # kernel
233             $self->[SE_NAMESPACE], # heap
234             $state, # state
235             $source_session, # sender
236             undef, # unused #6
237             $file, # caller file name
238             $line, # caller file line
239             $fromstate, # caller state
240             undef, # method
241             $etc # args
242             ) ], 'POE::Session::PlainCall::Holder';
243 1         6 return $handler->( @$etc );
244             }
245              
246             # Package and object states are invoked this way.
247 31         40 my ($object, $method) = @{$handler};
  31         62  
248 31         244 local $POE_HOLDER = bless [
249             ( $object, # object
250             $self, # session
251             $POE::Kernel::poe_kernel, # kernel
252             $self->[SE_NAMESPACE], # heap
253             $state, # state
254             $source_session, # sender
255             undef, # unused #6
256             $file, # caller file name
257             $line, # caller file line
258             $fromstate, # caller state
259             $method, # method
260             $etc, # args
261             ) ], 'POE::Session::PlainCall::Holder';
262              
263 31         163 return $object->$method( @$etc );
264              
265             }
266              
267             #######################################
268             sub _register_state
269             {
270 34     34   18435 my( $self, $name, @definition ) = @_;
271 34 100       109 if( $self->[SE_MYSTATES]{$name} ) {
272 28         81 return $self->state( $name, @definition );
273             }
274             else {
275             # states created via POE::Kernel->state
276 6 50       53 if( @definition ) {
277 6 50       44 if ($self->[SE_OPTIONS]->{+OPT_TRACE}) {
278 0         0 POE::Kernel::_warn( $self->_loggable, " -> $name is external\n" );
279             }
280             }
281             else {
282             # just in case something strange happened
283 0         0 delete $self->[SE_MYSTATES]{$name};
284             }
285 6         110 $self->SUPER::_register_state( $name, @definition );
286             }
287             }
288              
289             #######################################
290             sub state
291             {
292 30     30 1 57 my( $self, $name, @definition ) = @_;
293 30 50 66     107 if( ! $self->[SE_MYSTATES]{$name} and
294             $self->[SE_STATES]{$name} ) {
295 0         0 croak "You may not redefine an event handler defined by a wheel or other external module.";
296             }
297              
298 30 50       60 if( @definition ) {
299 30 50       71 if ($self->[SE_OPTIONS]->{+OPT_TRACE}) {
300 0         0 POE::Kernel::_warn( $self->_loggable, " -> $name is ours\n" );
301             }
302 30         55 $self->[SE_MYSTATES]{$name} = 1;
303             }
304             else {
305 0         0 delete $self->[SE_MYSTATES]{$name};
306             }
307 30         138 $self->SUPER::_register_state( $name, @definition );
308             }
309              
310             ############################################################################
311             package POE::Session::PlainCall::Holder;
312              
313 5     5   44 use strict;
  5         8  
  5         221  
314 5     5   26 use warnings;
  5         25  
  5         275  
315              
316             # use POE::Session;
317 5     5   39 use Scalar::Util qw( blessed );
  5         7  
  5         2944  
318              
319 12     12   57 sub METHOD { POE::Session::ARG0 }
320 2     2   7 sub ETC { POE::Session::ARG1 }
321              
322 1     1   15 sub object { $_[0]->[POE::Session::OBJECT] }
323 1 50   1   2 sub package { my $r=$_[0]->[POE::Session::OBJECT]; blessed $r ? ref $r : $r }
  1         8  
324 3     3   14 sub session { $_[0]->[POE::Session::SESSION] }
325 37     37   207 sub kernel { $POE::Kernel::poe_kernel }
326 0     0   0 sub heap { $_[0]->[POE::Session::HEAP] }
327 14     14   90 sub state { $_[0]->[POE::Session::STATE] }
328 2     2   14 sub event { $_[0]->[POE::Session::STATE] }
329 12     12   43 sub method { $_[0]->[METHOD] }
330 18     18   72 sub sender { $_[0]->[POE::Session::SENDER]->ID }
331 10     10   74 sub SENDER () { $_[0]->[POE::Session::SENDER] }
332 20     20   87 sub caller_file { $_[0]->[POE::Session::CALLER_FILE] }
333 10     10   50 sub caller_line { $_[0]->[POE::Session::CALLER_LINE] }
334 1     1   5 sub caller_state { $_[0]->[POE::Session::CALLER_STATE] }
335             sub args {
336 2     2   6 my $etc = $_[0]->[ETC];
337 2 100       11 return wantarray ? @$etc : [ @$etc ]
338             }
339              
340              
341             # These would mess up ->caller_file and ->caller_line :-/
342             # sub post { shift->[KERNEL]->post( @_ ) }
343             # sub call { shift->[KERNEL]->call( @_ ) }
344             # sub yield { shift->[KERNEL]->yield( @_ ) }
345              
346              
347             1;
348             __END__