File Coverage

blib/lib/App/Pocoirc/ReadLine.pm
Criterion Covered Total %
statement 17 19 89.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 24 26 92.3


line stmt bran cond sub pod time code
1             package App::Pocoirc::ReadLine;
2             BEGIN {
3 1     1   2358 $App::Pocoirc::ReadLine::AUTHORITY = 'cpan:HINRIK';
4             }
5             {
6             $App::Pocoirc::ReadLine::VERSION = '0.47';
7             }
8              
9 1     1   8 use strict;
  1         2  
  1         37  
10 1     1   5 use warnings FATAL => 'all';
  1         2  
  1         45  
11 1     1   5 use Carp;
  1         2  
  1         75  
12 1     1   930 use Data::Dump 'dump';
  1         7548  
  1         103  
13 1     1   975 use IO::WrapOutput;
  1         990  
  1         73  
14 1     1   514 use POE;
  0            
  0            
15             use POE::Component::IRC::Plugin qw(PCI_EAT_NONE);
16             use POE::Wheel::ReadLine;
17             use POE::Wheel::ReadWrite;
18             use Symbol qw(gensym);
19              
20             sub new {
21             my ($package) = shift;
22             croak "$package requires an even number of arguments" if @_ & 1;
23             my $self = bless { @_ }, $package;
24              
25             return $self;
26             }
27              
28             sub PCI_register {
29             my ($self, $irc, %args) = @_;
30              
31             $self->{registered}++;
32              
33             if ($self->{registered} == 1) {
34             POE::Session->create(
35             object_states => [
36             $self => [qw(
37             _start
38             got_user_input
39             got_output
40             restore_stdio
41             )],
42             ],
43             args => [$args{network}],
44             );
45             }
46              
47             if (!defined $self->{ui_irc}) {
48             $self->{ui_irc} = $irc;
49             }
50              
51             $self->{ircs}{$args{network}} = $irc;
52             $irc->plugin_register($self, 'SERVER', 'network');
53             return 1;
54             }
55              
56             sub PCI_unregister {
57             my ($self, $irc, %args) = @_;
58             $self->{registered}--;
59             if ($self->{registered} == 0) {
60             $poe_kernel->call($self->{session_id}, 'restore_stdio');
61             }
62             return 1;
63             }
64              
65             sub _start {
66             my ($kernel, $session, $self, $network) = @_[KERNEL, SESSION, OBJECT, ARG0];
67              
68             $self->{session_id} = $session->ID();
69             $self->{console} = POE::Wheel::ReadLine->new(
70             InputEvent => 'got_user_input',
71             PutMode => 'immediate',
72             AppName => 'pocoirc',
73             );
74              
75             my ($stdout, $stderr) = wrap_output();
76             $self->{stderr_reader} = POE::Wheel::ReadWrite->new(
77             Handle => $stderr,
78             InputEvent => 'got_output',
79             );
80             $self->{stdout_reader} = POE::Wheel::ReadWrite->new(
81             Handle => $stdout,
82             InputEvent => 'got_output',
83             );
84              
85             $self->{console}->get("$network> ");
86             return;
87             }
88              
89             sub got_output {
90             my ($self, $line) = @_[OBJECT, ARG0];
91             $self->{console}->put($line);
92             return;
93             }
94              
95             sub got_user_input {
96             my ($self, $line, $ex) = @_[OBJECT, ARG0, ARG1];
97              
98             if (defined $ex && $ex eq 'interrupt') {
99             $self->{Pocoirc}->shutdown('Exiting due to user interruption');
100             return;
101             }
102              
103             if (defined $line && length $line) {
104             $self->{console}->add_history($line);
105              
106             if (my ($new_network) = $line =~ /^network\s*(.+)/) {
107             my $found;
108             while (my ($network, $irc) = each %{ $self->{ircs} }) {
109             if ($network =~ /^\Q$new_network\E$/i) {
110             $self->{ui_irc} = $irc;
111             $self->{console}->get("$network> ");
112             $found = 1;
113             last;
114             }
115             }
116             $self->_print_networks() if !$found;
117             }
118             elsif ($line =~ /^networks\s*$/) {
119             $self->_print_networks();
120             }
121             elsif (my ($feature) = $line =~ /^(verbose|trace)\s*$/) {
122             if ($self->{Pocoirc}->$feature()) {
123             $self->{Pocoirc}->$feature(0);
124             print "Disabled '$feature'\n";
125             }
126             else {
127             $self->{Pocoirc}->$feature(1);
128             print "Enabled '$feature'\n";
129             }
130             }
131             elsif (my ($cmd, $args) = $line =~ m{^/([a-z_]+)\s*(.+)?}) {
132             my @args = defined $args ? eval $args : ();
133             $self->{ui_irc}->yield($cmd, @args);
134             }
135             elsif (my ($method, $params) = $line =~ m{^\.([a-z_]+)\s*(.+)?}) {
136             my @params = defined $params ? eval $params : ();
137              
138             local ($@, $!);
139             eval {
140             print dump($self->{ui_irc}->$method(@params)), "\n";
141             };
142             if (my $err = $@) {
143             chomp $err;
144             my $our_file = __FILE__;
145             $err =~ s{ at \Q$our_file\E line [0-9]+\.$}{};
146             warn $err, "\n";
147             }
148             }
149             else {
150             $self->_print_help();
151             }
152             }
153              
154             $self->{console}->get();
155             return;
156             }
157              
158             sub _print_help {
159             my ($self) = @_;
160              
161             print <<'EOF';
162             Type "network foo" to switch networks, or "networks" for a list of networks.
163              
164             Type ".foo 'bar', 'baz'" to call the method "foo" with the arguments 'bar'
165             and 'baz' on the IRC component. You must quote your arguments since they
166             will be eval'd, and don't forget to use commas between arguments.
167              
168             Type "/foo 'bar', 'baz'" to call the POE::Component::IRC command foo with the
169             arguments 'bar' and 'baz'. This is equivalent to: .yield 'foo', 'bar', 'baz'
170              
171             Type "verbose" and "trace" to flip those features on/off.
172             EOF
173              
174             return;
175             }
176              
177             sub _print_networks {
178             my ($self) = @_;
179             print "Available networks: ", join(', ', keys %{ $self->{ircs} }), "\n";
180             return;
181             }
182              
183             sub S_network {
184             my ($self, $irc) = splice @_, 0, 2;
185             my $network = ${ $_[0] };
186              
187             $self->{console}->get("$network> ");
188             for my $net (keys %{ $self->{ircs} }) {
189             if ($self->{ircs}{$net} == $irc) {
190             delete $self->{ircs}{$net};
191             $self->{ircs}{$network} = $irc;
192             }
193             }
194             return PCI_EAT_NONE;
195             }
196              
197             sub restore_stdio {
198             my ($self) = $_[OBJECT];
199              
200             unwrap_output();
201             delete $self->{console};
202             delete $self->{stderr_reader};
203             delete $self->{stdout_reader};
204             return;
205             }
206              
207             1;
208              
209             =encoding utf8
210              
211             =head1 NAME
212              
213             App::Pocoirc::ReadLine - A PoCo-IRC plugin which provides a ReadLine UI
214              
215             =head1 DESCRIPTION
216              
217             This plugin is used internally by L. No need for
218             you to use it.
219              
220             =head1 AUTHOR
221              
222             Hinrik Ern SigurEsson, hinrik.sig@gmail.com
223              
224             =cut