Ovid wrote:
Hi all,
Just a quick question, mainly aimed at Schwern since he's most familiar with
TAP::Harness, though others with thoughts should definitely chime in.
To handle streams, I am thinking of this:
my $parser = TAPx::Parser->new;
while ( defined ( my $chunk = $tap_stream->next ) ) {
my @results = $parser->parse_chunk($chunk);
# do what you will with the result objects
}
$parser->finalize;
This seems rather clean because it doesn't matter how the stream is created.
Pass chunks (usually single lines, but it doesn't have to be) to the parser and
everything is golden. Also, this means that I am not dependent on the stream
interface.
I assume it's desired to be able to view the results as they're coming in
rather than wait for the end of the stream.
The only potential problem I see with the above scenario is that the chunks have to be "complete".
In other words, if I have a "got" in one chunk, having the "expected" in the next chunk
makes it a bit more difficult to handle (though not insurmountable). However, depending upon how buffering
is handled, I could see this condition arising.
Thoughts?
Seems like the stream and parser are integrally linked anyway. Maybe the
parser should have a stream.
my $parser = TAPx::Parser->new_from_stream($tap_stream);
while ( defined ( my $results = $parser->next ) ) {
# do what you will with the $results object
}
$parser->finalize;
In your above example, would you every use $chunk other than as an
argument to parse_chunk? Or, if it is needed, can't you call
$results->raw to get them?
The dependency on the stream interface is not necessarily bad; it would
just mean pushing the decoupling up a level: let the stream abstract
away the details of reading bits from some source. Your above example
would allow:
my $parser = TAPx::Parser->new;
while ( defined ( my $results = <STDOUT> ) ) {
my @results = $parser->parse_chunk($chunk);
}
$parser->finalize;
But if you make the stream into a complete, closed abstraction over your
input, it would have be more like:
my $tap_stream = TAP::Source->new(\*STDOUT);
my $parser = TAPx::Parser->new($tap_stream);
while ( defined ( my $results = $parser->next ) ) {
my @results = $parser->parse_chunk($chunk);
}
$parser->finalize;
Or something like that. It seems to me that requiring the source to be
wrapped an object would afford a little more protection through
separating concerns of each object: Streams handle streaming errors;
Parsers handle parsing errors. This also allows the parser to /know/ how
to retrieve a meaningful chunk of data from a stream, whether it's a
single line or multiple lines, because a stream has a guaranteed interface.
Is $tap_stream->next() a destructive iterator? Is use-once or
resettable? Is it unidirectional? Does it matter?
I think I do like the iterator approach. If an event model is needed, it
is easy enough to put together with a light layer on top.
Randy.