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