File Coverage

blib/lib/Term/ReadLine/Event.pm
Criterion Covered Total %
statement 17 108 15.7
branch 0 10 0.0
condition n/a
subroutine 6 33 18.1
pod 7 7 100.0
total 30 158 18.9


line stmt bran cond sub pod time code
1             package Term::ReadLine::Event;
2             $Term::ReadLine::Event::VERSION = '0.05';
3 8     8   188746 use 5.006;
  8         30  
4 8     8   41 use strict;
  8         17  
  8         164  
5 8     8   41 use warnings;
  8         21  
  8         248  
6              
7 8     8   875 use Term::ReadLine 1.09;
  8         3524  
  8         188  
8 8     8   41 use Scalar::Util qw(blessed);
  8         12  
  8         10892  
9              
10             # ABSTRACT: Wrappers for Term::ReadLine's new event_loop model.
11              
12              
13             sub _new {
14 0     0     my $class = shift;
15 0           my $app = shift;
16              
17 0           my $self = bless {@_}, $class;
18              
19 0 0         $self->{_term} = blessed $app ? $app :
    0          
20             Term::ReadLine->new(ref $app ? @$app : $app);
21 0           $self;
22             }
23              
24              
25             sub with_AnyEvent {
26 0     0 1   my $self = _new(@_);
27              
28             $self->trl->event_loop(
29             sub {
30 0     0     my $data = shift;
31 0           $data->[0] = AE::cv();
32 0           $data->[0]->recv();
33             }, sub {
34 0     0     my $fh = shift;
35              
36             # The data for AE are: the file event watcher (which
37             # cannot be garbage collected until we're done) and
38             # a placeholder for the condvar we're sharing between
39             # the AE::io callback created here and the wait
40             # callback above.
41 0           my $data = [];
42 0           $data->[1] = AE::io($fh, 0, sub { $data->[0]->send() });
  0            
43 0           $data;
44             }
45 0           );
46              
47 0           $self;
48             }
49              
50              
51             sub with_Coro {
52 0     0 1   my $self = _new(@_);
53 0           require Coro::Handle;
54              
55             $self->trl->event_loop(
56             sub {
57             # Tell Coro to wait until we have something to read,
58             # and then we can return.
59 0     0     shift->readable();
60             }, sub {
61             # in Coro, we just need to unblock the filehandle,
62             # and save the unblocked filehandle.
63 0     0     my $fh = shift;
64 0           Coro::Handle::unblock $fh;
65             }
66 0           );
67 0           $self;
68             }
69              
70              
71             sub with_IO_Async {
72 0     0 1   my $self = _new(@_);
73              
74             $self->trl->event_loop(
75             sub {
76 0     0     my $ready = shift;
77 0           $$ready = 0;
78 0           $self->{loop}->loop_once while !$$ready;
79             },
80             sub {
81 0     0     my $fh = shift;
82              
83             # The data for IO::Async is just the ready flag. To
84             # ensure we're referring to the same value, this is
85             # a SCALAR ref.
86 0           my $ready = \ do{my $dummy};
  0            
87              
88             # Term::ReadLine::Gnu::XS relies on the fh being
89             # able to do fileno.
90 0 0         unless($fh->can('fileno')) {
91 0           require IO::Handle;
92 0           my $h = IO::Handle->new;
93 0 0         $h->fdopen($fh, 'r') or die "could not fdopen - $!";
94 0           $fh = $h
95             }
96              
97             $self->{loop}->add(
98             $self->{watcher} =
99             IO::Async::Handle->new(
100             read_handle => $fh,
101 0           on_read_ready => sub { $$ready = 1 },
102             )
103 0           );
104 0           $ready;
105             }
106 0           );
107              
108             $self->{_cleanup} = sub {
109 0     0     my $s = shift;
110 0           $s->{loop}->remove($s->{watcher});
111 0           };
112              
113 0           $self;
114             }
115              
116              
117             sub with_POE
118             {
119 0     0 1   my $self = _new(@_);
120              
121 0           my $waiting_for_input;
122              
123             POE::Session->create(
124             inline_states => {
125              
126             # Initialize the session that will drive Term::ReadLine.
127             # Tell Term::ReadLine to invoke a couple POE event handlers when
128             # it's ready to wait for input, and when it needs to register an
129             # I/O watcher.
130              
131             _start => sub {
132 0     0     $self->trl->event_loop(
133             $_[POE::Session->SESSION]->callback('term_readline_waitfunc'),
134             $_[POE::Session->SESSION]->callback('term_readline_regfunc'),
135             );
136             },
137              
138             # This callback is invoked every time Term::ReadLine wants to
139             # read something from its input file handle. It blocks
140             # Term::ReadLine until input is seen.
141             #
142             # It sets a flag indicating that input hasn't arrived yet.
143             # It watches Term::ReadLine's input filehandle for input.
144             # It runs while it's waiting for input.
145             # It turns off the input watcher when it's no longer needed.
146             #
147             # POE::Kernel's run_while() dispatches other events (including
148             # "term_readline_readable" below) until $waiting_for_input goes
149             # to zero.
150              
151             term_readline_waitfunc => sub {
152 0     0     my $input_handle = $_[POE::Session->ARG1][0];
153 0           $waiting_for_input = 1;
154 0           $_[POE::Session->KERNEL]->select_read($input_handle => 'term_readline_readable');
155 0           $_[POE::Session->KERNEL]->run_while(\$waiting_for_input);
156 0           $_[POE::Session->KERNEL]->select_read($input_handle => undef);
157             },
158              
159             # This callback is invoked as Term::ReadLine is starting up for
160             # the first time. It saves the exposed input filehandle where
161             # the "term_readline_waitfunc" callback can see it.
162              
163             term_readline_regfunc => sub {
164 0     0     my $input_handle = $_[POE::Session->ARG1][0];
165 0           return $input_handle;
166             },
167              
168             # This callback is invoked when data is seen on Term::ReadLine's
169             # input filehandle. It clears the $waiting_for_input flag.
170             # This causes run_while() to return in "term_readline_waitfunc".
171              
172             term_readline_readable => sub {
173 0     0     $waiting_for_input = 0;
174             },
175             },
176 0           );
177 0           $self;
178             }
179              
180              
181             sub with_Reflex
182             {
183 0     0 1   my $self = _new(@_);
184              
185             $self->trl->event_loop(
186             sub {
187 0     0     my $input_watcher = shift();
188 0           $input_watcher->next();
189             },
190             sub {
191 0     0     my $input_handle = shift();
192 0           my $input_watcher = Reflex::Filehandle->new(
193             handle => $input_handle,
194             rd => 1,
195             );
196 0           return $input_watcher;
197             },
198 0           );
199              
200              
201 0           $self;
202             }
203              
204              
205             sub with_Tk
206             {
207 0     0 1   my $self = _new(@_);
208              
209             $self->trl->event_loop(
210             sub {
211 0     0     my $data = shift;
212 0           Tk::DoOneEvent(0) until $$data;
213 0           $$data = 0;
214             },
215             sub {
216             # save filehandle for unhooking later.
217 0     0     $self->{tkFH} = shift;
218 0           my $data;
219 0           $$data = 0;
220 0           Tk->fileevent($self->{tkFH}, 'readable', sub { $$data = 1 });
  0            
221 0           $data;
222             },
223 0           );
224              
225             $self->{_cleanup} = sub {
226 0     0     my $s = shift;
227 0           Tk->fileevent($s->{tkFH}, 'readable', "");
228 0           };
229              
230 0           $self;
231             }
232              
233              
234             sub DESTROY
235             {
236 0     0     my $self = shift;
237              
238 0           local $@;
239 0           eval {
240 0           $self->trl->event_loop(undef);
241              
242 0 0         $self->{_cleanup}->($self) if $self->{_cleanup};
243             };
244             }
245              
246              
247             sub trl
248             {
249 0     0 1   my $self = shift;
250 0           $self->{_term};
251             }
252              
253             our $AUTOLOAD;
254             sub AUTOLOAD
255             {
256 0     0     (my $f = $AUTOLOAD) =~ s/.*:://;
257              
258 8     8   47 no strict 'refs';
  8         13  
  8         818  
259 0           *{$f} = sub {
260 0     0     shift->trl()->$f(@_);
261 0           };
262              
263 0           goto &$f;
264             }
265              
266              
267             1; # End of Term::ReadLine::Event
268              
269             __END__