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.

Reply via email to