1package TAP::Parser::SourceHandler::Executable; 2 3use strict; 4use vars qw($VERSION @ISA); 5 6use TAP::Parser::SourceHandler (); 7use TAP::Parser::IteratorFactory (); 8use TAP::Parser::Iterator::Process (); 9 10@ISA = qw(TAP::Parser::SourceHandler); 11 12TAP::Parser::IteratorFactory->register_handler(__PACKAGE__); 13 14=head1 NAME 15 16TAP::Parser::SourceHandler::Executable - Stream output from an executable TAP source 17 18=head1 VERSION 19 20Version 3.23 21 22=cut 23 24$VERSION = '3.23'; 25 26=head1 SYNOPSIS 27 28 use TAP::Parser::Source; 29 use TAP::Parser::SourceHandler::Executable; 30 31 my $source = TAP::Parser::Source->new->raw(['/usr/bin/ruby', 'mytest.rb']); 32 $source->assemble_meta; 33 34 my $class = 'TAP::Parser::SourceHandler::Executable'; 35 my $vote = $class->can_handle( $source ); 36 my $iter = $class->make_iterator( $source ); 37 38=head1 DESCRIPTION 39 40This is an I<executable> L<TAP::Parser::SourceHandler> - it has 2 jobs: 41 421. Figure out if the L<TAP::Parser::Source> it's given is an executable 43 command (L</can_handle>). 44 452. Creates an iterator for executable commands (L</make_iterator>). 46 47Unless you're writing a plugin or subclassing L<TAP::Parser>, you 48probably won't need to use this module directly. 49 50=head1 METHODS 51 52=head2 Class Methods 53 54=head3 C<can_handle> 55 56 my $vote = $class->can_handle( $source ); 57 58Only votes if $source looks like an executable file. Casts the 59following votes: 60 61 0.9 if it's a hash with an 'exec' key 62 0.8 if it's a .bat file 63 0.75 if it's got an execute bit set 64 65=cut 66 67sub can_handle { 68 my ( $class, $src ) = @_; 69 my $meta = $src->meta; 70 71 if ( $meta->{is_file} ) { 72 my $file = $meta->{file}; 73 74 return 0.85 if $file->{execute} && $file->{binary}; 75 return 0.8 if $file->{lc_ext} eq '.bat'; 76 return 0.25 if $file->{execute}; 77 } 78 elsif ( $meta->{is_hash} ) { 79 return 0.9 if $src->raw->{exec}; 80 } 81 82 return 0; 83} 84 85=head3 C<make_iterator> 86 87 my $iterator = $class->make_iterator( $source ); 88 89Returns a new L<TAP::Parser::Iterator::Process> for the source. 90C<$source-E<gt>raw> must be in one of the following forms: 91 92 { exec => [ @exec ] } 93 94 [ @exec ] 95 96 $file 97 98C<croak>s on error. 99 100=cut 101 102sub make_iterator { 103 my ( $class, $source ) = @_; 104 my $meta = $source->meta; 105 106 my @command; 107 if ( $meta->{is_hash} ) { 108 @command = @{ $source->raw->{exec} || [] }; 109 } 110 elsif ( $meta->{is_scalar} ) { 111 @command = ${ $source->raw }; 112 } 113 elsif ( $meta->{is_array} ) { 114 @command = @{ $source->raw }; 115 } 116 117 $class->_croak('No command found in $source->raw!') unless @command; 118 119 $class->_autoflush( \*STDOUT ); 120 $class->_autoflush( \*STDERR ); 121 122 push @command, @{ $source->test_args || [] }; 123 124 return $class->iterator_class->new( 125 { command => \@command, 126 merge => $source->merge 127 } 128 ); 129} 130 131=head3 C<iterator_class> 132 133The class of iterator to use, override if you're sub-classing. Defaults 134to L<TAP::Parser::Iterator::Process>. 135 136=cut 137 138use constant iterator_class => 'TAP::Parser::Iterator::Process'; 139 140# Turns on autoflush for the handle passed 141sub _autoflush { 142 my ( $class, $flushed ) = @_; 143 my $old_fh = select $flushed; 144 $| = 1; 145 select $old_fh; 146} 147 1481; 149 150=head1 SUBCLASSING 151 152Please see L<TAP::Parser/SUBCLASSING> for a subclassing overview. 153 154=head2 Example 155 156 package MyRubySourceHandler; 157 158 use strict; 159 use vars '@ISA'; 160 161 use Carp qw( croak ); 162 use TAP::Parser::SourceHandler::Executable; 163 164 @ISA = qw( TAP::Parser::SourceHandler::Executable ); 165 166 # expect $handler->(['mytest.rb', 'cmdline', 'args']); 167 sub make_iterator { 168 my ($self, $source) = @_; 169 my @test_args = @{ $source->test_args }; 170 my $rb_file = $test_args[0]; 171 croak("error: Ruby file '$rb_file' not found!") unless (-f $rb_file); 172 return $self->SUPER::raw_source(['/usr/bin/ruby', @test_args]); 173 } 174 175=head1 SEE ALSO 176 177L<TAP::Object>, 178L<TAP::Parser>, 179L<TAP::Parser::IteratorFactory>, 180L<TAP::Parser::SourceHandler>, 181L<TAP::Parser::SourceHandler::Perl>, 182L<TAP::Parser::SourceHandler::File>, 183L<TAP::Parser::SourceHandler::Handle>, 184L<TAP::Parser::SourceHandler::RawTAP> 185 186=cut 187