1 package TAP::Parser::Source;
6 use File::Basename qw( fileparse );
7 use base 'TAP::Object';
9 use constant BLK_SIZE => 512;
13 TAP::Parser::Source - a TAP source & meta data about it
21 our $VERSION = '3.42';
25 use TAP::Parser::Source;
26 my $source = TAP::Parser::Source->new;
27 $source->raw( \'reference to raw TAP source' )
30 ->switches( \@switches )
34 do { ... } if $source->meta->{is_file};
35 # see assemble_meta for a full list of data available
39 A TAP I<source> is something that produces a stream of TAP for the parser to
40 consume, such as an executable file, a text file, an archive, an IO handle, a
41 database, etc. C<TAP::Parser::Source>s encapsulate these I<raw> sources, and
42 provide some useful meta data about them. They are used by
43 L<TAP::Parser::SourceHandler>s, which do whatever is required to produce &
44 capture a stream of TAP from the I<raw> source, and package it up in a
45 L<TAP::Parser::Iterator> for the parser to consume.
47 Unless you're writing a new L<TAP::Parser::SourceHandler>, a plugin or
48 subclassing L<TAP::Parser>, you probably won't need to use this module directly.
56 my $source = TAP::Parser::Source->new;
58 Returns a new C<TAP::Parser::Source> object.
62 # new() implementation supplied by TAP::Object
71 ##############################################################################
73 =head2 Instance Methods
77 my $raw = $source->raw;
78 $source->raw( $some_value );
80 Chaining getter/setter for the raw TAP source. This is a reference, as it may
81 contain large amounts of data (eg: raw TAP).
85 my $meta = $source->meta;
86 $source->meta({ %some_value });
88 Chaining getter/setter for meta data about the source. This defaults to an
89 empty hashref. See L</assemble_meta> for more info.
93 True if the source has meta data.
97 my $config = $source->config;
98 $source->config({ %some_value });
100 Chaining getter/setter for the source's configuration, if any has been provided
101 by the user. How it's used is up to you. This defaults to an empty hashref.
102 See L</config_for> for more info.
106 my $merge = $source->merge;
107 $source->config( $bool );
109 Chaining getter/setter for the flag that dictates whether STDOUT and STDERR
110 should be merged (where appropriate). Defaults to undef.
114 my $switches = $source->switches;
115 $source->config([ @switches ]);
117 Chaining getter/setter for the list of command-line switches that should be
118 passed to the source (where appropriate). Defaults to undef.
122 my $test_args = $source->test_args;
123 $source->config([ @test_args ]);
125 Chaining getter/setter for the list of command-line arguments that should be
126 passed to the source (where appropriate). Defaults to undef.
132 return $self->{raw} unless @_;
133 $self->{raw} = shift;
139 return $self->{meta} unless @_;
140 $self->{meta} = shift;
145 return scalar %{ shift->meta } ? 1 : 0;
150 return $self->{config} unless @_;
151 $self->{config} = shift;
157 return $self->{merge} unless @_;
158 $self->{merge} = shift;
164 return $self->{switches} unless @_;
165 $self->{switches} = shift;
171 return $self->{test_args} unless @_;
172 $self->{test_args} = shift;
176 =head3 C<assemble_meta>
178 my $meta = $source->assemble_meta;
180 Gathers meta data about the L</raw> source, stashes it in L</meta> and returns
181 it as a hashref. This is done so that the L<TAP::Parser::SourceHandler>s don't
182 have to repeat common checks. Currently this includes:
190 has_newlines => $bool
192 # only done if the scalar looks like a filename
197 # only done if the scalar looks like a filename
198 basename => $string, # including ext
204 stat => [ ... ], # perldoc -f stat
218 # only done if the file's a symlink
219 lstat => [ ... ], # perldoc -f lstat
220 # only done if the file's a readable text file
221 shebang => $first_line,
232 return $self->meta if $self->has_meta;
234 my $meta = $self->meta;
235 my $raw = $self->raw;
237 # rudimentary is object test - if it's blessed it'll
238 # inherit from UNIVERSAL
239 $meta->{is_object} = UNIVERSAL::isa( $raw, 'UNIVERSAL' ) ? 1 : 0;
241 if ( $meta->{is_object} ) {
242 $meta->{class} = ref($raw);
245 my $ref = lc( ref($raw) );
246 $meta->{"is_$ref"} = 1;
249 if ( $meta->{is_scalar} ) {
251 $meta->{length} = length($$raw);
252 $meta->{has_newlines} = $$raw =~ /\n/ ? 1 : 0;
254 # only do file checks if it looks like a filename
255 if ( !$meta->{has_newlines} and $meta->{length} < 1024 ) {
257 $file->{exists} = -e $source ? 1 : 0;
258 if ( $file->{exists} ) {
259 $meta->{file} = $file;
261 # avoid extra system calls (see `perldoc -f -X`)
262 $file->{stat} = [ stat(_) ];
263 $file->{empty} = -z _ ? 1 : 0;
264 $file->{size} = -s _;
265 $file->{text} = -T _ ? 1 : 0;
266 $file->{binary} = -B _ ? 1 : 0;
267 $file->{read} = -r _ ? 1 : 0;
268 $file->{write} = -w _ ? 1 : 0;
269 $file->{execute} = -x _ ? 1 : 0;
270 $file->{setuid} = -u _ ? 1 : 0;
271 $file->{setgid} = -g _ ? 1 : 0;
272 $file->{sticky} = -k _ ? 1 : 0;
274 $meta->{is_file} = $file->{is_file} = -f _ ? 1 : 0;
275 $meta->{is_dir} = $file->{is_dir} = -d _ ? 1 : 0;
277 # symlink check requires another system call
278 $meta->{is_symlink} = $file->{is_symlink}
279 = -l $source ? 1 : 0;
280 if ( $file->{is_symlink} ) {
281 $file->{lstat} = [ lstat(_) ];
284 # put together some common info about the file
285 ( $file->{basename}, $file->{dir}, $file->{ext} )
286 = map { defined $_ ? $_ : '' }
287 fileparse( $source, qr/\.[^.]*/ );
288 $file->{lc_ext} = lc( $file->{ext} );
289 $file->{basename} .= $file->{ext} if $file->{ext};
291 if ( !$file->{is_dir} && $file->{read} ) {
292 eval { $file->{shebang} = $self->shebang($$raw); };
300 elsif ( $meta->{is_array} ) {
301 $meta->{size} = $#$raw + 1;
303 elsif ( $meta->{is_hash} ) {
312 Get the shebang line for a script file.
314 my $shebang = TAP::Parser::Source->shebang( $some_script );
316 May be called as a class method
322 # Global shebang cache.
326 my ( $class, $file ) = @_;
327 open my $fh, '<', $file or die "Can't read $file: $!\n";
329 # Might be a binary file - so read a fixed number of bytes.
330 my $got = read $fh, my ($buf), BLK_SIZE;
331 defined $got or die "I/O error: $!\n";
332 return $1 if $buf =~ /(.*)/;
337 my ( $class, $file ) = @_;
338 $shebang_for{$file} = $class->_read_shebang($file)
339 unless exists $shebang_for{$file};
340 return $shebang_for{$file};
346 my $config = $source->config_for( $class );
348 Returns L</config> for the $class given. Class names may be fully qualified
351 # these are equivalent
352 $source->config_for( 'Perl' );
353 $source->config_for( 'TAP::Parser::SourceHandler::Perl' );
355 If a fully qualified $class is given, its abbreviated version is checked first.
360 my ( $self, $class ) = @_;
361 my ($abbrv_class) = ( $class =~ /(?:\:\:)?(\w+)$/ );
362 my $config = $self->config->{$abbrv_class} || $self->config->{$class};
378 L<TAP::Parser::IteratorFactory>,
379 L<TAP::Parser::SourceHandler>