File Coverage

blib/lib/IO/WrapOutput.pm
Criterion Covered Total %
statement 34 34 100.0
branch 6 12 50.0
condition n/a
subroutine 10 10 100.0
pod 2 2 100.0
total 52 58 89.6


line stmt bran cond sub pod time code
1             package IO::WrapOutput;
2             BEGIN {
3 1     1   26711 $IO::WrapOutput::AUTHORITY = 'cpan:HINRIK';
4             }
5             BEGIN {
6 1     1   20 $IO::WrapOutput::VERSION = '0.07';
7             }
8              
9 1     1   10 use strict;
  1         2  
  1         42  
10 1     1   5 use warnings FATAL => 'all';
  1         2  
  1         43  
11 1     1   6 use Carp 'croak';
  1         8  
  1         77  
12 1     1   1007 use IO::Handle;
  1         9131  
  1         57  
13 1     1   7 use Symbol 'gensym';
  1         2  
  1         59  
14 1     1   7 use base 'Exporter';
  1         2  
  1         544  
15              
16             our @EXPORT = qw(wrap_output unwrap_output);
17             our @EXPORT_OK = @EXPORT;
18             our %EXPORT_TAGS = (ALL => [@EXPORT]);
19              
20             my ($orig_stderr, $orig_stdout);
21              
22             sub wrap_output {
23             # 2-arg open() here because Perl 5.6 doesn't understand the '>&' mode
24             # with a 3-arg open
25 1 50   1 1 114 open $orig_stdout, '>&'.fileno(STDOUT) or croak("Can't dup STDOUT: $!");
26 1 50       21 open $orig_stderr, '>&'.fileno(STDERR) or croak("Can't dup STDERR: $!");
27              
28 1         8 my ($read_stdout, $write_stdout) = (gensym(), gensym());
29 1         70 pipe $read_stdout, $write_stdout;
30 1 50       26 open STDOUT, '>&'.fileno($write_stdout) or croak("Can't pipe STDOUT: $!");
31              
32 1         5 my ($read_stderr, $write_stderr) = (gensym(), gensym());
33 1         39 pipe $read_stderr, $write_stderr;
34 1 50       3234 open STDERR, '>&'.fileno($write_stderr) or croak("Can't pipe STDERR: $!");
35 1         178 STDERR->autoflush(1);
36              
37 1         2148 return $read_stdout, $read_stderr;
38             }
39              
40             sub unwrap_output {
41 1 50   1 1 289 open STDOUT, '>&'.fileno($orig_stdout) or croak("Can't dup STDOUT: $!");
42 1 50       21 open STDERR, '>&'.fileno($orig_stderr) or croak("Can't dup STDERR: $!");
43 1         5 STDERR->autoflush(1);
44 1         31 return;
45             }
46              
47             1;
48              
49             =encoding utf8
50              
51             =head1 NAME
52              
53             IO::WrapOutput - Wrap your output filehandles with minimal fuss
54              
55             =head1 SYNOPSIS
56              
57             use IO::WrapOutput;
58             use Module::Which::Hogs::STDOUT::And::STDERR;
59              
60             my $foo = Module::Which::Hogs::STDOUT::And::STDERR->new();
61             my ($stdout, $stderr) = wrap_output();
62              
63             # read from $stdout and $stderr
64              
65             # then, later, restore the original handles
66             $foo->shutdown;
67             unwrap_output();
68              
69              
70             # example using POE::Wheel::ReadLine
71             use strict;
72             use warnings;
73             use IO::WrapOutput;
74             use POE;
75             use POE::Wheel::ReadLine;
76             use POE::Wheel::ReadWrite;
77              
78             POE::Session->create(
79             package_states => [main => [qw(_start got_output got_input)]],
80             );
81              
82             $poe_kernel->run();
83              
84             sub _start {
85             my ($heap) = $_[HEAP];
86              
87             $heap->{console} = POE::Wheel::ReadLine->new(
88             InputEvent => 'got_input',
89             );
90              
91             my ($stdout, $stderr) = wrap_output();
92             $heap->{stdout_reader} = POE::Wheel::ReadWrite->new(
93             Handle => $stdout,
94             InputEvent => 'got_output',
95             );
96             $heap->{stderr_reader} = POE::Wheel::ReadWrite->new(
97             Handle => $stderr,
98             InputEvent => 'got_output',
99             );
100              
101             # request the first line
102             $heap->{console}->get('>');
103             }
104              
105             sub got_output {
106             my ($heap, $line) = @_[HEAP, ARG0];
107             $heap->{console}->put($line);
108             }
109              
110             sub got_input {
111             my ($heap, $line, $exception) = @_[HEAP, ARG0, ARG1];
112              
113             if (defined $exception && $exception eq 'interrupt') {
114             # terminate the console
115             unwrap_output();
116             delete $heap->{console};
117             delete $heap->{stdout_reader};
118             delete $heap->{stderr_reader};
119             print "Terminated\n";
120             return;
121             }
122              
123             # do something with $line ...
124              
125             # request the next line
126             $heap->{console}->get();
127             }
128              
129             =head1 DESCRIPTION
130              
131             When you have a module (e.g. POE::Wheel::ReadLine) which needs all output
132             to go through a method that it provides, it can be cumbersome (or even
133             impossible) to change all the code in an asynchronous/event-driven program
134             to do that instead of printing directly to STDOUT/STDERR. That's
135             where C comes in.
136              
137             You just do the setup work for the output-hogging module in question, then
138             call C which will return filehandles that you can read from.
139             Then you take what you get from those filehandles and feed it into your
140             output-hogging module's output method. After you stop using the
141             output-hogging module, you can restore your original STDOUT/STDERR handles
142             with C.
143              
144             =head1 FUNCTIONS
145              
146             =head2 C
147              
148             Takes no arguments. Replaces the current STDOUT and STDERR handles with
149             pipes, and returns the read ends of those pipes back to you. Any copies
150             made of the STDOUT/STDERR handles before calling this function will still
151             be attached to the process' terminal.
152              
153             my ($stdout, $stderr) = wrap_output();
154              
155             =head2 C
156              
157             Takes no arguments. Restores the original STDOUT and STDERR handles.
158              
159             =head1 AUTHOR
160              
161             Hinrik Ern SigurEsson, hinrik.sig@gmail.com
162              
163             =head1 LICENSE AND COPYRIGHT
164              
165             Copyright 2011 Hinrik Ern SigurEsson
166              
167             This program is free software, you can redistribute it and/or modify
168             it under the same terms as Perl itself.
169              
170             =cut