1package IO::NestedCapture;
2
3use strict;
4use warnings;
5
6use Carp;
7use File::Temp;
8
9require Exporter;
10our @ISA = qw/ Exporter /;
11
12our @EXPORT_OK = qw/
13	CAPTURE_NONE
14	CAPTURE_STDIN
15	CAPTURE_STDOUT
16	CAPTURE_IN_OUT
17	CAPTURE_STDERR
18	CAPTURE_IN_ERR
19	CAPTURE_OUT_ERR
20	CAPTURE_ALL
21
22	capture_in
23	capture_out
24	capture_err
25	capture_in_out
26	capture_out_err
27	capture_in_err
28	capture_all
29/;
30
31our %EXPORT_TAGS = (
32	'constants' => [ qw/
33		CAPTURE_NONE
34		CAPTURE_STDIN
35		CAPTURE_STDOUT
36		CAPTURE_IN_OUT
37		CAPTURE_STDERR
38		CAPTURE_IN_ERR
39		CAPTURE_OUT_ERR
40		CAPTURE_ALL
41	/ ],
42
43	'subroutines' => [ qw/
44		capture_in
45		capture_out
46		capture_err
47		capture_in_out
48		capture_out_err
49		capture_in_err
50		capture_all
51	/ ],
52);
53
54our $VERSION = '1.03';
55
56use constant CAPTURE_NONE    => 0;
57use constant CAPTURE_STDIN   => 1;
58use constant CAPTURE_STDOUT  => 2;
59use constant CAPTURE_IN_OUT  => 3;
60use constant CAPTURE_STDERR  => 4;
61use constant CAPTURE_IN_ERR  => 5;
62use constant CAPTURE_OUT_ERR => 6;
63use constant CAPTURE_ALL     => 7;
64
65=head1 NAME
66
67IO::NestedCapture - module for performing nested STD* handle captures
68
69=head1 SYNOPSIS
70
71  use IO::NestedCapture qw/ :subroutines /;
72
73  my $in = IO::NestedCapture->get_next_in;
74  print $in "Harry\n";
75  print $in "Ron\n";
76  print $in "Hermione\n";
77
78  capture_in_out {
79      my @profs = qw( Dumbledore Flitwick McGonagall );
80      while (<STDIN>) {
81    	  my $prof = shift @prof;
82    	  print STDOUT "$_ favors $prof";
83      }
84  };
85
86  my $out = IO::NestedCapture->get_last_out;
87  while (<$out>) {
88	  print;
89  }
90
91  # This program will output:
92  # Harry favors Dumbledore
93  # Ron favors Flitwick
94  # Hermione favors McGonagall
95
96=head1 DESCRIPTION
97
98This module was partially inspired by L<IO::Capture>, but is intended for a very different purpose and is not otherwise related to that package. In particular, I have a need for some pretty aggressive output/input redirection in a web project I'm working on. I'd like to be able to pipe input into a subroutine and then capture that subroutines output to be used as input on the next.
99
100I was using a fairly clumsy, fragile, and brute force method for doing this. If you're interested, you can take a look at the code on PerlMonks.org:
101
102  http://perlmonks.org/?node_id=459275
103
104This module implements a much saner approach that involves only a single tie per file handle (regardless of what you want to tie it to). It works by tying the STDIN, STDOUT, and STDERR file handles. Then, uses internal tied class logic to handle any nested use or other work.
105
106With this module you can capture any combination of STDIN, STDOUT, and STDERR. In the case of STDIN, you may feed any input into capture you want (or even set it to use another file handle). For STDOUT and STDERR you may review the full output of these or prior to capture set a file handle that will receive all the data during the capture.
107
108As of version 1.02 of this library, there are two different interfaces to the library. The object-oriented version was first, but the new subroutine interface is a little less verbose and a little safer.
109
110=head2 OBJECT-ORIENTED INTERFACE
111
112The object-oriented interface is available either through the  C<IO::NestedCapture> class directly or through a single instance of the class available through the C<instance> method.
113
114  my $capture = IO::NestedCapture->instance;
115  $capture->start(CAPTURE_STDOUT);
116
117  # Is the same as...
118
119  IO::NestedCapture->start(CAPTURE_STDOUT);
120
121It doesn't really make much difference.
122
123You will probably want to important one, several, or all of the capture constants to use this interface.
124
125=head2 SUBROUTINE INTERFACE
126
127This interface is available via the import of one of the capture subroutines (or not if you want to fully qualify the names):
128
129  use IO::NestedCapture 'capture_out';
130  capture_out {
131      # your code to print to STDOUT here...
132  };
133
134  # Is similar to...
135  IO::NestedCapture::capture_err {
136      # your code to print to STDERR here...
137  };
138
139This interface has the advantage of being a little more concise and automatically starts and stops the capture before and after running the code block. This will help avoid typos and other mistakes in your code, such as forgetting to call C<stop> when you are done.
140
141=head2 NESTED CAPTURE SUBROUTINES
142
143These subroutines are used with the subroutine interface. (See L</"SUBROUTINE INTERFACE">.) These subroutines actually use the object-oriented interface internally, so they merely provide a convenient set of shortcuts to it that may help save you some trouble.
144
145For each subroutine, the subroutine captures one or more file handles before running the given code block and uncaptures them after. In case of an exception, the file handles will still be uncaptured properly. Make sure to put a semi-colon after each method call.
146
147To manipulate the input, output, and error handles before or after the capture, you will still need to use parts of the object-oriented interface.
148
149You will want to import the subroutines you want to use when you load the C<IO::NestedCapture> object:
150
151  use IO::NestedCapture qw/ capture_in capture_out /;
152
153or you can import all of the capture subroutines with the C<:subroutines> mnemonic:
154
155  use IO::NestedCapture ':subroutines';
156
157In place of a block, you may also give a code reference as the argument to any of these calls:
158
159  sub foo { print "bah\n" }
160
161  capture_all \&foo;
162
163This will run the subroutine foo (with no arguments) and capture the streams it reads/writes. Also, each of the capture subroutines return the return value of the block or rethrow the exceptions raised in the block after stopping the capture.
164
165=over
166
167=item capture_in { };
168
169This subroutine captures C<STDIN> for the duration of the given block.
170
171=cut
172
173sub capture_in(&) {
174	my $self = IO::NestedCapture->instance;
175	my $code = shift;
176
177	# capture input and then turn off capture, even on error
178	$self->start(CAPTURE_STDIN);
179	my $result = eval {
180		$code->();
181	};
182	my $ERROR = $@;
183	$self->stop(CAPTURE_STDIN);
184
185	# rethrow any errors or return normally
186	die $ERROR if $ERROR;
187	return $result;
188}
189
190=item capture_out { };
191
192This subroutine captures C<STDOUT> for the duration of the given block.
193
194=cut
195
196sub capture_out(&) {
197	my $self = IO::NestedCapture->instance;
198	my $code = shift;
199
200	# capture output and then turn off capture, even on error
201	$self->start(CAPTURE_STDOUT);
202	my $result = eval {
203		$code->();
204	};
205	my $ERROR = $@;
206	$self->stop(CAPTURE_STDOUT);
207
208	# rethrow any errors or return normally
209	die $ERROR if $ERROR;
210	return $result;
211}
212
213=item capture_err { };
214
215This subroutine captures C<STDERR> for the duration of the given block.
216
217=cut
218
219sub capture_err(&) {
220	my $self = IO::NestedCapture->instance;
221	my $code = shift;
222
223	# capture error output and then turn off capture, even on error
224	$self->start(CAPTURE_STDERR);
225	my $result = eval {
226		$code->();
227	};
228	my $ERROR = $@;
229	$self->stop(CAPTURE_STDERR);
230
231	# rethrow any errors or return normally
232	die $ERROR if $ERROR;
233	return $result;
234}
235
236=item capture_in_out { };
237
238This subroutine captures C<STDIN> and C<STDOUT> for the duration of the given block.
239
240=cut
241
242sub capture_in_out(&) {
243	my $self = IO::NestedCapture->instance;
244	my $code = shift;
245
246	# capture input and output and then turn off capture, even on error
247	$self->start(CAPTURE_IN_OUT);
248	my $result = eval {
249		$code->();
250	};
251	my $ERROR = $@;
252	$self->stop(CAPTURE_IN_OUT);
253
254	# rethrow any errors or return normally
255	die $ERROR if $ERROR;
256	return $result;
257}
258
259=item capture_in_err { };
260
261This subroutine captures C<STDIN> and C<STDERR> for the duration of the given block.
262
263=cut
264
265sub capture_in_err(&) {
266	my $self = IO::NestedCapture->instance;
267	my $code = shift;
268
269	# capture input and error output and then turn off capture, even on error
270	$self->start(CAPTURE_IN_ERR);
271	my $result = eval {
272		$code->();
273	};
274	my $ERROR = $@;
275	$self->stop(CAPTURE_IN_ERR);
276
277	# rethrow any errors or return normally
278	die $ERROR if $ERROR;
279	return $result;
280}
281
282=item capture_out_err { };
283
284This subroutine captures C<STDOUT> and C<STDERR> for the duration of the given block.
285
286=cut
287
288sub capture_out_err(&) {
289	my $self = IO::NestedCapture->instance;
290	my $code = shift;
291
292	# capture output and error output and then turn off capture, even on error
293	$self->start(CAPTURE_OUT_ERR);
294	my $result = eval {
295		$code->();
296	};
297	my $ERROR = $@;
298	$self->stop(CAPTURE_OUT_ERR);
299
300	# rethrow any errors or return normally
301	die $ERROR if $ERROR;
302	return $result;
303}
304
305=item capture_all { };
306
307This subroutine captures C<STDIN>, C<STDOUT>, and C<STDERR> for the duration of the given block.
308
309=cut
310
311sub capture_all(&) {
312	my $self = IO::NestedCapture->instance;
313	my $code = shift;
314
315	# capture input, output and error output and then turn off capture, even on
316	# error
317	$self->start(CAPTURE_ALL);
318	my $result = eval {
319		$code->();
320	};
321	my $ERROR = $@;
322	$self->stop(CAPTURE_ALL);
323
324	# rethrow any errors or return normally
325	die $ERROR if $ERROR;
326	return $result;
327}
328
329=back
330
331=head2 NESTED CAPTURE CONSTANTS
332
333These constants are used with the object-oriented interface. (See L</"OBJECT-ORIENTED INTERFACE">.)
334
335You will want to import the constants you want when you load the C<IO::NestedCapture> module:
336
337  use IO::NestedCapture qw/ CAPTURE_STDIN CAPTURE_STDOUT /;
338
339or you may import all of them with the C<:constants> mnemonic.:
340
341  use IO::NestedCapture ':constants';
342
343=over
344
345=item CAPTURE_STDIN
346
347Used to start or stop capture on STDIN.
348
349=item CAPTURE_STDOUT
350
351Used to start or stop capture on STDOUT.
352
353=item CAPTURE_STDERR
354
355Used to start or stop capture on STDERR.
356
357=item CAPTURE_IN_OUT
358
359Used to start or stop capture on STDIN and STDOUT. This is a shortcut for "C<CAPTURE_STDIN | CAPTURE_STDOUT>".
360
361=item CAPTURE_IN_ERR
362
363Used to start or stop cpature on STDIN and STDERR. This is a shortcut for "C<CAPTURE_STDIN | CAPTURE_STDERR>".
364
365=item CAPTURE_OUT_ERR
366
367Used to start or stop capture on STDOUT and STDERR. This is a shortcut for "C<CAPTURE_STDOUT | CAPTURE_STDERR>".)
368
369=item CAPTURE_ALL
370
371Used to start or stop capture on STDIN, STDOUT, and STDERR. This is a shortcut for "C<CAPTURE_STDIN | CAPTURE_STDOUT | CAPTURE_STDERR>".
372
373=back
374
375=head2 OBJECT-ORIENTED CAPTURE METHODS
376
377These are the methods used for the object-oriented interface. (See L</"OBJECT-ORIENTED INTERFACE">.)
378
379=over
380
381=item $capture = IO::NestedCapture-E<gt>instance;
382
383Retrieves an instance of the singleton. Use of this method is optional.
384
385=cut
386
387my $instance;
388sub instance {
389	# We've already got one...
390	return $instance if $instance;
391
392	# I told 'im we already got one...
393	my $class = shift;
394	return $instance = bless {}, $class;
395}
396
397=item IO::NestedCapture-E<gt>start($capture_what)
398
399=item $capture-E<gt>start($capture_what)
400
401The C<$capture_what> variable is a bit field that should be set to one or more of the L</"NESTED CAPTURE CONSTANTS"> bit-or'd together. Until this method is called, the STD* handles are not tied to the C<IO::NestedCapture> interface. The tie will only occur on the very first call to this method. After that, the nesting is handled with stacks internal to the C<IO::NestedCapture> singleton.
402
403If you're capturing STDIN, you should be sure to fill in the input using the C<in> method first if you want there to be any to be read.
404
405If you're capturing STDOUT or STDERR, you should be sure to set the file handles to output too, if you want to do that before calling this method.
406
407=cut
408
409my %fhs = (
410	CAPTURE_STDIN()  => 'STDIN',
411	CAPTURE_STDOUT() => 'STDOUT',
412	CAPTURE_STDERR() => 'STDERR',
413);
414
415sub start {
416	my $self = shift->instance;
417	my $capture_what = shift;
418
419	# check parameters for sanity
420	$capture_what >= CAPTURE_NONE
421		or croak "start() called without specifying which handles to capture.";
422	$capture_what <= CAPTURE_ALL
423		or croak "start() called with unknown capture parameters.";
424
425	# For each capture constant asked to start, let's make sure it's tied and
426	# then push us up onto the next level
427	for my $capcon ((CAPTURE_STDIN, CAPTURE_STDOUT, CAPTURE_STDERR)) {
428		if ($capture_what & $capcon) {
429
430			# figure out what we're checking
431			my $fh = $fhs{$capcon};
432
433			no strict 'refs';
434
435			# croak if it's tied to the wrong thingy, tie it if we're untied
436			if (tied(*$fh) && !UNIVERSAL::isa(tied(*$fh), 'IO::NestedCapture')) {
437				croak "start() failed because $fh is not tied as expected.";
438			} elsif (!tied(*$fh)) {
439				tie *$fh, 'IO::NestedCapture', $fh;
440			}
441
442			# grab the one being prepped or create it
443			my $pushed_fh;
444			my $pushed_reset = 0;
445			if ($pushed_fh = delete $self->{"${fh}_next"}) {
446
447				# if this is our own file handle, we want to go back to the top
448				# of the file before starting. if this is the user's file
449				# handle, we won't mess with it.
450				my $next_reset = delete $self->{"${fh}_next_reset"};
451				seek $pushed_fh, 0, 0 if $next_reset;
452			} else {
453				$pushed_fh = File::Temp::tempfile;
454				$pushed_reset = 1;
455			}
456
457			# put this one on top of the file handle stack
458			push @{ $self->{"${fh}_current"} }, $pushed_fh;
459			push @{ $self->{"${fh}_current_reset"} }, $pushed_reset;
460		}
461	}
462}
463
464=item IO::NestedCapture-E<gt>stop($uncapture_what)
465
466=item $capture-E<gt>stop($uncapture_what)
467
468The C<$uncapture_what> variable is a bit field that should be set to one or more of the L</"NESTED CAPTURE CONSTANTS"> bit-or'd together. If this method is called and the internal nesting state shows that this is the last layer to remove, the associated STD* handles are untied. If C<$uncapture_what> indicates that a certain handle should be uncaptured, but it isn't currently captured, an error will be thrown.
469
470=cut
471
472sub stop {
473	my $self = shift->instance;
474	my $uncapture_what = shift;
475
476	# check parameters for sanity
477	$uncapture_what > 0
478		or croak "stop() called without specifying which handles to uncapture.";
479	$uncapture_what <= CAPTURE_ALL
480		or croak "stop() called with unknown uncapture parameters.";
481
482	# For each uncapture constant asked to stop, check to make sure we're
483	# stopping after one or more starts, pop the file handle, and untie if it's
484	# the last one
485	for my $uncapcon ((CAPTURE_STDIN, CAPTURE_STDOUT, CAPTURE_STDERR)) {
486		if ($uncapture_what & $uncapcon) {
487			# figure out what we're checking
488			my $fh = $fhs{$uncapcon};
489
490			# is this in use or should we croak?
491			(defined $self->{"${fh}_current"} && @{ $self->{"${fh}_current"} })
492				or croak "stop() asked to stop $fh, but it wasn't started";
493
494			$self->{"${fh}_last"} = pop @{ $self->{"${fh}_current"} };
495			seek $self->{"${fh}_last"}, 0, 0
496				if pop @{ $self->{"${fh}_current_reset"} };
497
498			unless (@{ $self->{"${fh}_current"} }) {
499				no strict 'refs';
500				untie *$fh;
501			}
502		}
503	}
504}
505
506=item $handle = IO::NestedCapture-E<gt>get_next_in
507
508=item $handle = $capture-E<gt>get_next_in
509
510This method returns the file handle that will be used for STDIN after the next call to C<start(CAPTURE_STDIN)>. If one has not been set using C<set_next_in>, then a seekable file handle will be created. If you just use the automatically created file handle (which is created using L<File::Temp>), then C<start()> will seek to the top of the file handle before use.
511
512=cut
513
514sub get_next_in {
515	my $self = shift->instance;
516
517	unless ($self->{'STDIN_next'}) {
518		$self->{'STDIN_next'} = File::Temp::tempfile;
519		$self->{'STDIN_next_reset'} = 1;
520	}
521
522	return $self->{'STDIN_next'};
523}
524
525=item IO::NestedCapture-E<gt>set_next_in($handle)
526
527=item $capture-E<gt>in($handle)
528
529The given file handle is used as the file handle to read from after C<start(CAPTURE_STDIN)> is called. If you set a file handle yourself, you must make sure that it is ready to be read from when you call C<start()> (i.e., the file handle pointer won't be reset to the top of the file automatically).
530
531=cut
532
533sub set_next_in {
534	my $self = shift->instance;
535	my $handle = shift;
536
537	$self->{'STDIN_next'} = $handle;
538	delete $self->{'STDIN_next_reset'};
539
540	return;
541}
542
543=item $handle = IO::NestedCapture-E<gt>get_last_out
544
545=item $handle = $capture-E<gt>get_last_out
546
547Retrieve the file handle used to capture the output prior to the previous call to C<stop(CAPTURE_STDOUT)>. If this file handle was automatically generated (i.e., not set with C<set_next_out()>, then the file pointer will already be set to the top of the file and ready to read).
548
549=cut
550
551sub get_last_out {
552	my $self = shift->instance;
553	return $self->{'STDOUT_last'};
554}
555
556=item IO::NestedCapture-E<gt>set_next_out($handle)
557
558=item $capture-E<gt>set_next_out($handle)
559
560Install your own file handle to capture the output following the next call to C<start(CAPTURE_STDOUT)>. Make sure the file handle is in the exact state you want before calling C<start()>.
561
562=cut
563
564sub set_next_out {
565	my $self = shift->instance;
566	my $handle = shift;
567
568	$self->{'STDOUT_next'} = $handle;
569	delete $self->{'STDOUT_next_reset'};
570
571	return;
572}
573
574=item $handle = IO::NestedCapture-E<gt>get_last_error
575
576=item $handle = $capture-E<gt>get_last_error
577
578Retrieve the file handle used to capture the error output prior to the previous call to C<stop(CAPTURE_STDERR)>. If this file handle was automatically generated (i.e., not set with C<set_next_err()>, then the file pointer will already be set to the top of the file and ready to read).
579
580=cut
581
582sub get_last_err {
583	my $self = shift->instance;
584	return $self->{'STDERR_last'};
585}
586
587=item IO::NestedCapture-E<gt>set_next_err($handle)
588
589=item $capture-E<gt>set_next_err($handle)
590
591Install your own file handle to capture the error output following the next call to C<start(CAPTURE_STDERR)>. Make sure the file handle is in the exact state you want before calling C<start()>.
592
593=cut
594
595sub set_next_err {
596	my $self = shift->instance;
597	my $handle = shift;
598
599	$self->{'STDERR_next'} = $handle;
600	delete $self->{'STDERR_next_reset'};
601
602	return;
603}
604
605=back
606
607=cut
608
609# The rest of this is private tie stuff...
610
611# Okay, so the documentation lies. This isn't really a singleton, but the extra
612# objects are internally used as ties only.
613sub TIEHANDLE {
614	my $class = shift;
615	my $instance = $class->instance;
616
617	# Make a non-singleton tie class... shhhhhh.
618	my $self = bless {
619		instance => $instance,
620		fh       => shift,
621	}, $class;
622}
623
624sub WRITE {
625	my $self = shift;
626	my $buf  = shift;
627	my $len  = shift;
628	my $off  = shift;
629
630	# load state
631	my $capture = $self->{instance};
632	my $fh      = $self->{fh};
633
634	# write
635	syswrite $capture->{"${fh}_current"}[-1], $buf, $len, $off;
636}
637
638sub PRINT {
639	my $self = shift;
640
641	# load state
642	my $capture = $self->{instance};
643	my $fh      = $self->{fh};
644	my $handle  = $capture->{"${fh}_current"}[-1];
645
646	# write
647	print $handle @_;
648}
649
650sub PRINTF {
651	my $self = shift;
652
653	# load state
654	my $capture = $self->{instance};
655	my $fh      = $self->{fh};
656	my $handle  = $capture->{"${fh}_current"}[-1];
657
658	# write
659	printf $handle @_;
660}
661
662sub READ {
663	my $self   = shift;
664
665	# load state
666	my $capture = $self->{instance};
667	my $fh      = $self->{fh};
668	my $handle  = $capture->{"${fh}_current"}[-1];
669
670	# read
671	read $handle, $_[0], $_[1], $_[2];
672}
673
674sub READLINE {
675	my $self = shift;
676
677	# load state
678	my $capture = $self->{instance};
679	my $fh      = $self->{fh};
680	my $handle  = $capture->{"${fh}_current"}[-1];
681
682	# read
683	readline $handle;
684}
685
686sub GETC {
687	my $self = shift;
688
689	# load state
690	my $capture = $self->{instance};
691	my $fh      = $self->{fh};
692	my $handle  = $capture->{"${fh}_current"}[-1];
693
694	# read
695	getc $handle;
696}
697
698sub CLOSE {
699	my $self = shift;
700
701	# load state
702	my $capture = $self->{instance};
703	my $fh      = $self->{fh};
704	my $handle  = $capture->{"${fh}_current"}[-1];
705
706	# close
707	close $handle;
708}
709
710=head1 EXPORTS
711
712This module exports all of the constants used with the object-oriented interface and the subroutines used with the subroutine interface.
713
714See L</"NESTED CAPTURE CONSTANTS"> for the specific constant names or use C<:constants> to import all the constants.
715
716See L</"NESTED CAPTURE SUBROUTINES"> for the specific subroutine names or use C<:subroutines> to import all the subroutines.
717
718=head1 SEE ALSO
719
720L<IO::Capture>
721
722=head1 AUTHOR
723
724Andrew Sterling Hanenkamp, E<lt>hanenkamp@cpan.orgE<gt>
725
726=head1 COPYRIGHT AND LICENSE
727
728Copyright 2005 Andrew Sterling Hanenkamp.
729
730This code is licensed and distributed under the same terms as Perl itself.
731
732=cut
733
7341
735