File Coverage

blib/lib/POE/Component/DebugShell.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package POE::Component::DebugShell;
2              
3 1     1   29167 use warnings;
  1         2  
  1         35  
4 1     1   5 use strict;
  1         2  
  1         34  
5              
6 1     1   31 use 5.006;
  1         7  
  1         39  
7              
8 1     1   5 use Carp;
  1         2  
  1         93  
9              
10 1     1   914 use POE;
  1         60884  
  1         6  
11 1     1   140141 use POE::Wheel::ReadLine;
  1         52831  
  1         61  
12 1     1   553 use POE::API::Peek;
  0            
  0            
13              
14             our $VERSION = '1.412';
15             our $RUNNING = 0;
16             our %COMMANDS;
17             our $SPAWN_TIME;
18              
19             sub spawn { #{{{
20             my $class = shift;
21              
22             # Singleton check {{{
23             if($RUNNING) {
24             carp "A ".__PACKAGE__." session is already running. Will not start a second.";
25             return undef;
26             } else {
27             $RUNNING = 1;
28             }
29             # }}}
30              
31             my $api = POE::API::Peek->new() or croak "Unable to create POE::API::Peek object";
32              
33              
34             # Session creation {{{
35             my $sess = POE::Session->create(
36             inline_states => {
37             _start => \&_start,
38             _stop => \&_stop,
39              
40             term_input => \&term_input,
41             },
42             heap => {
43             api => $api,
44             },
45             );
46             # }}}
47              
48             if($sess) {
49             $SPAWN_TIME = time();
50             return $sess;
51             } else {
52             return undef;
53             }
54             } #}}}
55              
56              
57              
58             sub _start { #{{{
59             $_[KERNEL]->alias_set(__PACKAGE__." controller");
60              
61             $_[HEAP]->{rl} = POE::Wheel::ReadLine->new( InputEvent => 'term_input' );
62             $_[HEAP]->{prompt} = 'debug> ';
63              
64             tie *STDOUT, "POE::Component::DebugShell::Output", 'stdout', \&_output;
65             tie *STDERR, "POE::Component::DebugShell::Output", 'stderr', \&_output;
66              
67             $_[HEAP]->{rl}->clear();
68             _output("Welcome to POE Debug Shell v$VERSION");
69              
70             $_[HEAP]->{rl}->get($_[HEAP]->{prompt});
71              
72             } #}}}
73              
74              
75              
76             sub _stop { #{{{
77             # Shut things down
78             $_[HEAP]->{vt} && $_[HEAP]->{vt}->delete_window($_[HEAP]->{main_window});
79             } #}}}
80              
81              
82              
83             sub term_input { #{{{
84             my ($input, $exception) = @_[ARG0, ARG1];
85              
86             unless (defined $input) {
87             croak("Received exception from UI: $exception");
88             }
89              
90             $_[HEAP]->{rl}->addhistory($input);
91              
92             if($input =~ /^help (.*?)$/) {
93             my $cmd = $1;
94             if($COMMANDS{$cmd}) {
95             if($COMMANDS{$cmd}{help}) {
96             _output("Help for $cmd:");
97             _output($COMMANDS{$cmd}{help});
98             } else {
99             _output("Error: '$cmd' has no help.");
100             }
101             } else {
102             _output("Error: '$cmd' is not a known command");
103             }
104             } elsif ( ($input eq 'help') or ($input eq '?') ) {
105             my $text;
106             _output(' ');
107             _output("General help for POE::Component::DebugShell v$VERSION");
108             _output("The following commands are available:");
109             foreach my $cmd (sort keys %COMMANDS) {
110             no warnings;
111             my $short_help = $COMMANDS{$cmd}{short_help} || '[ No short help provided ]';
112             _output("\t* $cmd - $short_help");
113             }
114             _output(' ');
115              
116             } else {
117             my ($cmd, @args);
118             if($input =~ /^(.+?)\s+(.*)$/) {
119             $cmd = $1;
120             my $args = $2;
121             @args = split('\s+',$args) if $args;
122             } else {
123             $cmd = $input;
124             }
125              
126             if($COMMANDS{$cmd}) {
127             my $txt = eval { $COMMANDS{$cmd}{cmd}->( api => $_[HEAP]->{api}, args => \@args); };
128             if($@) {
129             _output("Error running $cmd: $@");
130             } else {
131             my @lines = split(/\n/, $txt);
132             _output($_) for @lines;
133             }
134             } else {
135             _output("Error: '$cmd' is not a known command");
136             }
137             }
138              
139             $_[HEAP]->{rl}->get($_[HEAP]->{prompt});
140              
141             } #}}}
142              
143              
144              
145             sub _output { #{{{
146             my $msg = shift || ' ';
147             my $heap = $poe_kernel->alias_resolve(__PACKAGE__." controller")->get_heap();
148             $heap->{rl}->put($msg);
149             } #}}}
150              
151             sub _raw_commands { #{{{
152             return \%COMMANDS;
153             } #}}}
154              
155             # ____ _
156             # / ___|___ _ __ ___ _ __ ___ __ _ _ __ __| |___
157             # | | / _ \| '_ ` _ \| '_ ` _ \ / _` | '_ \ / _` / __|
158             # | |__| (_) | | | | | | | | | | | (_| | | | | (_| \__ \
159             # \____\___/|_| |_| |_|_| |_| |_|\__,_|_| |_|\__,_|___/
160             #
161             # {{{
162              
163             %COMMANDS = ( #{{{
164              
165             'reload' => {
166             help => "Reload the shell to catch updates.",
167             short_help => "Reload the shell to catch updates.",
168             cmd => \&cmd_reload,
169             },
170              
171             show_sessions => {
172             help => 'Show a list of all sessions in the system. The output format is in the form of loggable session ids.',
173             short_help => 'Show a list of all sessions',
174             cmd => \&cmd_show_sessions,
175             },
176              
177             'list_aliases' => {
178             help => 'List aliases for a given session id. Provide one session id as a parameter.',
179             short_help => 'List aliases for a given session id.',
180             cmd => \&cmd_list_aliases,
181             },
182              
183             'session_stats' => {
184             help => 'Display various statistics for a given session id. Provide one session id as a parameter.',
185             short_help => 'Display various statistics for a given session id.',
186             cmd => \&cmd_session_stats,
187             },
188              
189             'queue_dump' => {
190             help => 'Dump the contents of the event queue.',
191             short_help => 'Dump the contents of the event queue.',
192             cmd => \&cmd_queue_dump,
193             },
194              
195             'status' => {
196             help => 'General shell status.',
197             short_help => 'General shell status.',
198             cmd => \&cmd_status,
199             },
200             ); #}}}
201              
202             ###############
203              
204             sub cmd_reload { #{{{
205             my $ret;
206             $ret .= "Reloading....\n";
207             eval q|
208             no warnings qw(redefine);
209             $SIG{__WARN__} = sub { };
210              
211             foreach my $key (keys %INC) {
212             if($key =~ m#POE/Component/DebugShell#) {
213             delete $INC{$key};
214             } elsif ($key =~ m#POE/API/Peek#) {
215             delete $INC{$key};
216             }
217             }
218             require POE::Component::DebugShell;
219             |;
220             $ret .= "Error: $@\n" if $@;
221              
222             return $ret;
223             } #}}}
224              
225             sub cmd_show_sessions { #{{{
226             my %args = @_;
227             my $api = $args{api};
228              
229             my $ret;
230             $ret .= "Session List:\n";
231             my @sessions = $api->session_list;
232             foreach my $sess (@sessions) {
233             my $id = $sess->ID. " [ ".$api->session_id_loggable($sess)." ]";
234             $ret .= "\t* $id\n";
235             }
236              
237             return $ret;
238             } #}}}
239              
240             sub cmd_list_aliases { #{{{
241             my %args = @_;
242             my $user_args = $args{args};
243             my $api = $args{api};
244              
245             my $ret;
246              
247             if(my $id = shift @$user_args) {
248             if(my $sess = $api->resolve_session_to_ref($id)) {
249             my @aliases = $api->session_alias_list($sess);
250             if(@aliases) {
251             $ret .= "Alias list for session $id\n";
252             foreach my $alias (sort @aliases) {
253             $ret .= "\t* $alias\n";
254             }
255             } else {
256             $ret .= "No aliases found for session $id\n";
257             }
258             } else {
259             $ret .= "** Error: ID $id does not resolve to a session. Sorry.\n";
260             }
261              
262             } else {
263             $ret .= "** Error: Please provide a session id\n";
264             }
265             return $ret;
266             }
267              
268             # }}}
269              
270             sub cmd_session_stats { #{{{
271             my %args = @_;
272             my $user_args = $args{args};
273             my $api = $args{api};
274              
275             my $ret;
276              
277             if(my $id = shift @$user_args) {
278             if(my $sess = $api->resolve_session_to_ref($id)) {
279             my $to = $api->event_count_to($sess);
280             my $from = $api->event_count_from($sess);
281             $ret .= "Statistics for Session $id\n";
282             $ret .= "\tEvents coming from: $from\n";
283             $ret .= "\tEvents going to: $to\n";
284              
285             } else {
286             $ret .= "** Error: ID $id does not resolve to a session. Sorry.\n";
287             }
288              
289              
290             } else {
291             $ret .= "** Error: Please provide a session id\n";
292             }
293              
294             return $ret;
295             } #}}}
296              
297             sub cmd_queue_dump { #{{{
298             my %args = @_;
299             my $api = $args{api};
300             my $verbose;
301              
302             my $ret;
303              
304             if($args{args} && defined $args{args}) {
305             if(ref $args{args} eq 'ARRAY') {
306             if(@{$args{args}}[0] eq '-v') {
307             $verbose = 1;
308             }
309             }
310             }
311              
312             my @queue = $api->event_queue_dump();
313              
314             $ret .= "Event Queue:\n";
315              
316             foreach my $item (@queue) {
317             $ret .= "\t* ID: ". $item->{ID}." - Index: ".$item->{index}."\n";
318             $ret .= "\t\tPriority: ".$item->{priority}."\n";
319             $ret .= "\t\tEvent: ".$item->{event}."\n";
320              
321             if($verbose) {
322             $ret .= "\t\tSource: ".
323             $api->session_id_loggable($item->{source}).
324             "\n";
325             $ret .= "\t\tDestination: ".
326             $api->session_id_loggable($item->{destination}).
327             "\n";
328             $ret .= "\t\tType: ".$item->{type}."\n";
329             $ret .= "\n";
330             }
331             }
332             return $ret;
333             } #}}}
334              
335             sub cmd_status { #{{{
336             my %args = @_;
337             my $api = $args{api};
338             my $sess_count = $api->session_count;
339             my $ret = "\n";
340             $ret .= "This is ".__PACKAGE__." v".$VERSION."\n";
341             $ret .= "running inside $0."."\n";
342             $ret .= "This console was spawned at ".localtime($SPAWN_TIME).".\n";
343             $ret .= "There are $sess_count known sessions (including the kernel).\n";
344             $ret .= "\n";
345             return $ret;
346             } # }}}
347              
348             # }}}
349              
350             1;
351              
352             package POE::Component::DebugShell::Output;
353              
354             use strict;
355             #use warnings FATAL => "all";
356              
357             sub PRINT {
358             my $self = shift;
359              
360             my $txt = join('',@_);
361             $txt =~ s/\r?\n$//;
362             $self->{print}->($self->{type}."> $txt");
363             }
364              
365             sub TIEHANDLE {
366             my $class = shift;
367             bless({
368             type => shift,
369             print => shift,
370             }, $class);
371             }
372              
373             1;
374             __END__