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