File Coverage

blib/lib/POE/Sugar/Attributes.pm
Criterion Covered Total %
statement 219 230 95.2
branch 52 64 81.2
condition 14 28 50.0
subroutine 47 49 95.9
pod 10 11 90.9
total 342 382 89.5


line stmt bran cond sub pod time code
1             package POE::Sugar::Attributes::SymInfo;
2 5     5   913527 use strict;
  5         11  
  5         161  
3 5     5   24 use warnings;
  5         8  
  5         146  
4 5     5   5140 use Class::Struct;
  5         11498  
  5         31  
5 5     5   6141 use Attribute::Handlers;
  5         29527  
  5         41  
6              
7             struct (__PACKAGE__,
8             [
9             'flags' => '$',
10             'coderef' => '$',
11             '_name' => '$',
12             'package' => '$',
13             'events' => '@',
14             'data' => '*@'
15             ]);
16              
17             sub name {
18 35     35   62 my ($self,$pkg) = @_;
19 35 100       792 if($self->_name) {
20 10         283 return $self->_name;
21             }
22            
23 25   33     972 $pkg ||= $self->package;
24 25         717 my $glob = Attribute::Handlers::findsym($pkg, $self->coderef);
25 25 50       408 if(!$glob) {
26 0         0 warn("Can't find symbol for " . $self->coderef);
27 0         0 return;
28             }
29 25         57 my $symname = *$glob{NAME};
30 25         571 $self->_name($symname);
31 25         186 return $symname;
32             }
33              
34             package POE::Sugar::Attributes::PkgInfo;
35 5     5   854 use strict;
  5         11  
  5         152  
36 5     5   26 use warnings;
  5         9  
  5         158  
37 5     5   26 use Class::Struct;
  5         8  
  5         34  
38              
39             struct (__PACKAGE__,
40             [
41             'ctor' => '$',
42             'dtor' => '$',
43             'events' => '%',
44             'catcher' => '$',
45             'reaper' => '$',
46             'sigs' => '%',
47             'syms' => '%'
48             ]);
49              
50              
51             package POE::Sugar::Attributes;
52 5     5   839 use strict;
  5         10  
  5         144  
53 5     5   27 use warnings;
  5         10  
  5         123  
54              
55 5     5   60 use POE;
  5         12  
  5         38  
56 5     5   1784 use POE::Session;
  5         9  
  5         15  
57              
58             our $VERSION = 0.02;
59              
60 5     5   394 use base qw(Exporter);
  5         7  
  5         469  
61 5     5   5200 use Log::Fu { level => "info" };
  5         207838  
  5         44  
62 5     5   1848 use Data::Dumper;
  5         11  
  5         410  
63             our @EXPORT_OK;
64             BEGIN {
65 5     5   118 @EXPORT_OK = qw(
66             wire_new_session
67             wire_current_session
68             );
69             }
70              
71 5     5   26 use Attribute::Handlers;
  5         9  
  5         51  
72              
73 5         41 use Constant::Generate [qw(
74             PA_EVENT
75             PA_CTOR
76             PA_DTOR
77             PA_TIMER
78             PA_CATCHER
79             PA_REAPER
80             PA_SIGHANDLER
81            
82             PA_NAME_RESOLVE
83             )], -type => 'bitfield',
84 5     5   246 -start_at => 1;
  5         11  
85              
86 5         24 use Constant::Generate [qw(
87             FLD_FLAGS
88             FLD_DATA
89             FLD_CODE
90 5     5   1729 )];
  5         10  
91              
92             my %map_names;
93             my %pkgcache;
94             my %const2hkey;
95              
96             my ($HKEY_CATCHER,
97             $HKEY_REAPER,
98             $HKEY_SIGHANDLERS
99             );
100              
101             %map_names = (
102             Start => PA_CTOR,
103             Stop => PA_DTOR,
104             Event => PA_EVENT,
105             State => PA_EVENT,
106             Recurring=> PA_TIMER,
107             Catcher => PA_CATCHER,
108             Reaper => PA_REAPER,
109             SigHandler=> PA_SIGHANDLER,
110             );
111              
112             #Ensure we don't actually collide with a real symbol
113             $HKEY_CATCHER = __PACKAGE__ . "__HKEY_CATCHER__";
114             $HKEY_REAPER = __PACKAGE__ . "__HKEY_REAPER__";
115             $HKEY_SIGHANDLERS = __PACKAGE__ . "__HKEY_SIGHANDLERS__";
116              
117             %const2hkey = (
118             PA_CATCHER, $HKEY_CATCHER,
119             PA_REAPER, $HKEY_REAPER
120             );
121              
122              
123              
124             sub _get_infos {
125 25     25   43 my ($pkg,$cv) = @_;
126 25   66     340 my $pkginfo = ($pkgcache{$pkg} ||=
127             POE::Sugar::Attributes::PkgInfo->new());
128 25   33     1597 my $syminfo = ($pkginfo->syms->{$cv+0} ||=
129             POE::Sugar::Attributes::SymInfo->new(
130             package => $pkg,
131             coderef => $cv)
132             );
133            
134 25         2327 return ($pkginfo,$syminfo);
135             }
136              
137             sub _poe_attr_handler :ATTR(CODE) {
138 25     25   154 my ($pkg,undef,$cv,$attr,$data) = @_;
139            
140 25         36 my @opt_array;
141             my %opt_hash;
142            
143 25 100       145 if(ref $data) {
144 10 100       46 if(@$data % 2 == 0) {
145 6         21 %opt_hash = @$data;
146             }
147 10         27 @opt_array = @$data;
148             }
149            
150 25         86 my $flag = $map_names{$attr};
151 25 50       108 die "No such attribute: $attr" unless defined $flag;
152            
153 25         69 my ($pkg_info,$sym_info) = _get_infos($pkg, $cv);
154            
155 25   50     581 $sym_info->flags( ($sym_info->flags || 0) | $flag );
156            
157 25 100 100     981 if($flag == PA_EVENT)
    100          
    100          
    50          
    100          
    50          
158             {
159 6 100       22 if(@opt_array) {
160 3         6 push @{$sym_info->events}, @opt_array;
  3         60  
161             } else {
162 3         6 push @{$sym_info->events}, \undef;
  3         67  
163             }
164             }
165            
166             elsif ($flag == PA_TIMER)
167             {
168 5         18 my $interval = delete $opt_hash{Interval};
169 5 50       405 die("Timer must have interval ()") unless $interval;
170 5         15 my $evname = $opt_hash{Name};
171 5   100     29 $evname ||= \undef;
172 5         221 $sym_info->data->[PA_TIMER] = [ $evname, $interval ];
173             }
174            
175             elsif ($flag == PA_CTOR || $flag == PA_DTOR)
176             {
177 10 100       115 if($flag == PA_CTOR) {
    50          
178 5         125 $pkg_info->ctor($sym_info);
179             }
180             elsif($flag == PA_DTOR) {
181 5         114 $pkg_info->dtor($sym_info);
182             }
183             #Do we have anything to do here?
184             }
185            
186            
187             elsif ($flag == PA_CATCHER) {
188 0         0 $pkg_info->catcher($sym_info);
189             }
190            
191             elsif ($flag == PA_REAPER) {
192 2         52 $pkg_info->reaper($sym_info);
193             }
194            
195             elsif ($flag == PA_SIGHANDLER) {
196 2 50       16 if(!ref $data) {
197 0         0 push @opt_array, $data;
198             }
199 2         6 foreach my $signame (@opt_array) {
200 2         16 log_debug("Found signal $signame");
201 2         118 $pkg_info->sigs->{$signame} = $sym_info;
202             }
203             }
204 5     5   4528 }
  5         16  
  5         60  
205              
206 5     5 1 2784 sub Start :ATTR(CODE) { goto &_poe_attr_handler }
  5     5   11  
  5         25  
  5         107427  
207 5     5 1 1747 sub Stop :ATTR(CODE) { goto &_poe_attr_handler }
  5     5   7  
  5         27  
  5         4595  
208 5     5 1 1933 sub Event :ATTR(CODE) { goto &_poe_attr_handler }
  5     6   12  
  5         18  
  6         5563  
209 5     5 0 2264 sub State :ATTR(CODE) { goto &_poe_attr_handler }
  5     0   8  
  5         22  
  0         0  
210 5     5 1 2620 sub Recurring:ATTR(CODE) { goto &_poe_attr_handler }
  5     5   9  
  5         24  
  5         39520  
211 5     5 1 1720 sub Catcher :ATTR(CODE) { goto &_poe_attr_handler }
  5     0   10  
  5         27  
  0         0  
212 5     5 1 2672 sub Reaper :ATTR(CODE) { goto &_poe_attr_handler }
  5     2   10  
  5         19  
  2         1588  
213 5     5 1 2251 sub SigHandler:ATTR(CODE){ goto &_poe_attr_handler }
  5     2   9  
  5         20  
  2         710  
214              
215             sub _get_params {
216 6     6   17 my $pkg = shift;
217 6         18 my $pkg_info = $pkgcache{$pkg};
218 6 50       41 die("Don't have anything registered for $pkg")
219             unless defined $pkg_info;
220            
221 6         10 my ($ctor,$dtor,$catcher,$reaper);
222 0         0 my @events;
223 0         0 my @timers;
224            
225 6         16 foreach my $sym_info (values %{$pkg_info->syms}) {
  6         245  
226            
227 25         259 my $symname = $sym_info->name();
228 25         63 $symname = $pkg ."::$symname";
229            
230 25 100       570 if($sym_info->flags & PA_EVENT) {
231 6         45 foreach my $evname (@{$sym_info->events}) {
  6         138  
232 7 100       62 if(ref $evname) {
233 3         39 log_debug("Event is the symbol name itself.. converting");
234 3         138 $evname = $sym_info->name();
235             }
236 7         50 push @events, [$evname, $symname ];
237             }
238             }
239            
240 25 100       697 if($sym_info->flags & PA_TIMER) {
241 5         37 my ($evname,$interval) = @{$sym_info->data->[PA_TIMER] };
  5         134  
242 5 100       51 if(ref $evname) {
243 3         14 log_debug("Timer event name is the symbol itsef.. converting");
244 3         114 $evname = $sym_info->name();
245             }
246 5         37 push @timers, [ $evname, $symname, $interval ];
247             }
248            
249 25 100       704 if($sym_info->flags & PA_CTOR) {
250 5         50 $ctor = $symname;
251             }
252 25 100       668 if($sym_info->flags & PA_DTOR) {
253 5         46 $dtor = $symname;
254             }
255             }
256            
257 6         80 my $params = {
258             Events => \@events,
259             Timers => \@timers,
260             Ctor => $ctor,
261             Dtor => $dtor
262             };
263              
264            
265 6         41 foreach (
266             ['reaper', 'Reaper'],
267             ['catcher', 'Catcher']
268             ) {
269 12         62 my ($meth,$key) = @$_;
270 12 100       338 if(my $handler = $pkg_info->can($meth)->($pkg_info)) {
271 2         24 my $symname = $handler->name;
272 2         18 push @events, [ $symname, $pkg . "::$symname" ];
273 2         8 $params->{$key} = $symname;
274             }
275             }
276            
277 6         67 while (my ($signame,$sighandler) = each %{$pkg_info->sigs} ) {
  8         182  
278 2         28 my $symname = $sighandler->name();
279 2         18 push @events, [ $symname, $pkg ."::$symname" ] ;
280 2         12 $params->{Signals}->{$signame} = $symname;
281             }
282            
283 6         69 return $params;
284             }
285              
286             sub _setup_events {
287 6     6   15 my ($events,$poe_kernel) = @_;
288 6         18 foreach (@$events) {
289 11         185 my ($evname,$subname) = @$_;
290 11         60 log_debug("$evname:$subname");
291 11     6   358 $poe_kernel->state($evname, sub { goto &{$subname} } );
  6         14182  
  6         96  
292             }
293             }
294              
295             sub _setup_timers {
296 6     6   23 my ($timers,$poe_kernel) = @_;
297 6         19 foreach (@$timers) {
298 5         208 my ($evname, $symname, $interval) = @$_;
299             my $wrap = sub {
300 56     56   493869 $poe_kernel->delay($evname, $interval);
301 56         17512 goto &{$symname};
  56         922  
302 5         26 };
303 5         26 $poe_kernel->state($evname, $wrap);
304 5         125 $poe_kernel->delay($evname, $interval);
305             }
306             }
307              
308             sub _setup_signals {
309 3     3   7 my ($signals, $poe_kernel) = @_;
310 3 100       15 return unless $signals;
311 2         12 while (my ($signame,$evname) = each %$signals) {
312 2         10 log_debugf("$signame => $evname");
313 2         54 $poe_kernel->sig($signame, $evname);
314             }
315             }
316              
317             sub inline_states {
318 5     5 1 134 my ($cls,$pkg,$alias) = @_;
319 5   33     24 $pkg ||= caller();
320 5         27 my $params = _get_params($pkg);
321             my $sess_hash = {
322             _start => sub {
323 5 100   5   1179 if($alias) {
324 1         8 $_[KERNEL]->alias_set($alias);
325             }
326 5         83 _setup_timers($params->{Timers}, $_[KERNEL]);
327 5         395 _setup_events($params->{Events}, $_[KERNEL]);
328            
329 5 50       293 if($params->{Catcher}) {
330 0         0 log_debug("Setting DIE handler: ", $params->{Catcher});
331 0         0 $_[KERNEL]->sig(DIE => $params->{Catcher});
332             }
333            
334 5 100       27 if(my $reaper = $params->{Reaper}) {
335 2         10 log_debugf("Setting CHLD handler: %s", $reaper);
336 2         64 $_[KERNEL]->sig(CHLD => $reaper);
337             }
338            
339 5 100       266 if($params->{Signals}) {
340 2         12 _setup_signals($params->{Signals}, $_[KERNEL]);
341             }
342            
343 5 50       87 if($params->{Ctor}) {
344 5         10 goto &{ $params->{Ctor} };
  5         47  
345             }
346             },
347             _stop => sub {
348 4 50   4   1617 if($params->{Dtor}) {
349 4         16 goto &{ $params->{Dtor} };
  4         39  
350             }
351             }
352 5         70 };
353            
354 5         159 return $sess_hash;
355             }
356              
357             #This is for other modules which inject themselves, in-situ, into a session
358             sub wire_current_session {
359 1     1 1 18 my ($cls,$poe_kernel,$pkg) = @_;
360 1   33     7 $pkg ||= caller();
361 1         3 my $params = _get_params($pkg);
362 1         4 _setup_timers($params->{Timers}, $poe_kernel);
363 1         145 _setup_events($params->{Events}, $poe_kernel);
364 1         7 _setup_signals($params->{Signals}, $poe_kernel);
365             }
366              
367              
368             sub wire_new_session {
369 4     4 1 256 my $alias = shift;
370 4 50 33     25 if( $alias && $alias eq __PACKAGE__) {
371 0         0 $alias = shift;
372             }
373            
374 4         11 my $pkg = shift;
375 4   33     41 $pkg ||= caller();
376            
377 4         50 POE::Session->create(
378             inline_states => __PACKAGE__->inline_states($pkg, $alias)
379             );
380             }
381              
382             1;
383              
384             __END__