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