Perl's not the only TAP source
David E. Wheeler
david at kineticode.com
Thu Jun 11 22:36:59 GMT 2009
On Jun 11, 2009, at 2:43 PM, Steve Purkis wrote:
> I like the simplicity, and I think it'll work with Executable sources.
>
> But I don't think it'll work in all cases: while it's true, I
> currently expect most sources to be Execution based, we still need to
> make sure we don't limit people to it. As mentioned in my last email,
> there are valid sources that are not Execution based, including 2
> internal sources. Having a method called 'command' doesn't make sense
> there.
Agreed.
> I'm also not so convinced about removing the sub-classing, mainly
> because it would complicate the internal sources that aren't command-
> based, and it would take away the benefits of being able to sub-class
> (like re-using parent methods).
It's added complexity for the source developer, though. And the more
programming I do, the less I like inheritance. What we need here are
roles or traits. My proposal tries to emulate that.
> Coming back to Executable sources - the reason I like your idea is
> because plugin writers really won't care about cases where the
> $raw_source_ref is not a file, so the fact that it's a ref, and a raw
> source is irrelevant noise. Having extra params on the method calls
> is also annoying if you don't need them... So if the super-class was
> to set them all up as instance attrs then they're there for the
> taking, but won't get in the way otherwise. Tricky with can_handle,
> as the object's not instantiated yet, but would work for command.
Well, if it's not a class itself, but just provides an interface, you
can pass an object to it.
> I'd say: make it possible to create a plugin by writing 2 simple
> methods, as above. Gear up all the docs that way. But still allow
> people to do more if they need to.
Agreed. I'm actually thinking of three methods or functions. More below.
> So just by using the 'Foo' source, it'll get registered & able to vote
> (eg: for the .foo file).
Oh, right, it has to tell the SourceDetector that it's something that
can detect sources. Duh.
> If it didn't get registered, we'd have to
> have some way of passing in all the source info to TAP::Harness.
>
> Hmm. Same problem as last email - maybe we want that anyway?
Here's what I'm thinking: A source writer has to write a module that
implements two of three functions and has to register itself. In
addition to the can_handle() function, they must implement either
command() -- in which case it's an exec source -- or stream(). So the
PHP source would look something like this:
package TAP::Parser::Source::PHP;
use TAP::Parser::SourceDetector register => __PACKAGE__;
use strict;
our $VERSION = '3.18';
sub can_handle {
my $meta = shift;
return 0 unless $meta->{is_file};
return 1 if $meta->{file}{lc_ext} eq '.php';
return 0;
}
sub command {
my $params = shift;
return [ 'php', $params->{file}{name} @{ $params->{config} ||
[] }];
}
And the source for, say, a raw tap file, would look like this:
package TAP::Parser::Source::RawTAP;
use TAP::Parser::SourceDetector register => __PACKAGE__;
sub can_handle {
my ( $class, $raw_source_ref, $meta ) = @_;
return 0 if $meta->{file};
if ($meta->{scalar}) {
return 0 unless $meta->{has_newlines};
return 0.9 if $$raw_source_ref =~ /\d\.\.\d/;
return 0.7 if $$raw_source_ref =~ /ok/;
return 0.6;
} elsif ($meta->{array}) {
return 0.5;
}
return 0;
}
sub stream {
my $params = shift;
return $params{factory}->make_iterator( $params-
>{raw_source} );
}
So the upshot here is that most sources just have to say how to run
the command and TAP::Harness will use it to create the proper
iterator. But if they're not command type sources, then they can
create the iterators themselves.
Hell, as I think about this, maybe there should be no command()
function, but just a stream function (or iterator, if you like). For a
command, it'd be:
sub stream {
my ($params, $factory) = @_;
$factory->make_iterator(
command =>[ 'php', $params->{file}{name} @{ $params-
>{config} || [] }]
);
}
I like this because the amount of work is the same as before and the
required interface is the same for both command and non-command
sources. Hell, even better would be to have different constructors on
the $factory object, such as `make_with_command()` and
`make_with_rawsource()` or something like that.
Thoughts?
Best,
David
More information about the tapx-dev
mailing list