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.44
20
21=cut
22
23our $VERSION = '3.44';
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