Skip Menu |

This queue is for tickets about the CGI-Application-Plugin-Output-XSV CPAN distribution.

Report information
The Basics
Id: 62449
Status: resolved
Priority: 0/
Queue: CGI-Application-Plugin-Output-XSV

People
Owner: ZACKSE [...] cpan.org
Requestors: rhesa [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: 1.00
Fixed in: 1.01



Subject: Streaming support should stream rows, not collect output
The iterator support would be a tad more useful if the output were streamed row by row, rather than the current behavior where output is collected until the iterator is exhausted. Attached is a patch that makes this happen. Rhesa
Subject: cgi-application-plugin-output-xsv.diff
--- a/CGI/Application/Plugin/Output/XSV.pm 2009-09-26 00:24:54.000000000 +0200 +++ b/CGI/Application/Plugin/Output/XSV.pm 2010-10-25 23:16:54.000000000 +0200 @@ -159,6 +159,9 @@ # using iterator else { my $iterations = 0; + local $| = 1; # autoflush + print $output; # the csv headers, if any + $output = ''; # clear this buffer while ( my $list_ref = $opts{iterator}->($fields) ) { croak "return value from iterator is not an array reference, aborting" @@ -168,7 +171,7 @@ croak "iterator exceeded maximum iterations ($opts{maximum_iters})" if ++$iterations > $opts{maximum_iters}; - $output .= add_to_xsv( + print add_to_xsv( $csv, $row_filter->($list_ref, $fields), $opts{line_ending} ); } @@ -193,11 +196,18 @@ my %opts = ( %defaults, %$args ); - $self->header_props( + my %headers = ( -type => 'application/x-csv', '-content-disposition' => "attachment; filename=$opts{filename}", ); + if ( $opts{values} ) { + $self->header_props( %headers ); + } else { + $self->header_type('none'); # we're doing our own output + print $self->query->header(%headers); + } + # consider use of magic goto in case of croak() inside xsv_report return xsv_report( \%opts ); }
On Mon Oct 25 17:33:43 2010, RHESA wrote: Show quoted text
> The iterator support would be a tad more useful if the output were > streamed row by row, rather than the current behavior where output is > collected until the iterator is exhausted. > > Attached is a patch that makes this happen. > > Rhesa
Hello Rhesa, Thanks for the suggestion and the patch! I elected to go with a separate option to the xsv_report/xsv_report_web routines to indicate that streaming is desired. That way, backwards-compatibility is retained. When the option is supplied, it applies to all uses of the routines, not just when an iterator is used. A patch is attached, please let me know what you think. Thanks, -Evan
Subject: streaming.patch
diff --git a/XSV.pm b/XSV.pm index 3e66166..d98590b 100644 --- a/XSV.pm +++ b/XSV.pm @@ -1,5 +1,3 @@ -# $Id: XSV.pm 43 2006-10-13 15:45:07Z zackse $ - package CGI::Application::Plugin::Output::XSV; use strict; @@ -25,7 +23,7 @@ our %EXPORT_TAGS= ( all => [ @EXPORT, @EXPORT_OK ], ); -our $VERSION= '1.00'; +our $VERSION= '1.01'; ## @@ -46,10 +44,15 @@ sub xsv_report { line_ending => "\n", csv_opts => {}, maximum_iters => 1_000_000, # XXX reasonable default? + stream => 0, ); my %opts = ( %defaults, %$args ); + my $was_buffering = $|; + local $| = $was_buffering; + $| = 1 if $opts{stream}; + # deprecated option if ( $opts{get_row_cb} ) { if ( $opts{row_filter} ) { @@ -127,7 +130,7 @@ sub xsv_report { } my $csv = Text::CSV_XS->new( $opts{csv_opts} ); - my $output; + my $output = ''; if ( $opts{include_headers} ) { if ( ! $opts{headers} ) { @@ -146,14 +149,26 @@ sub xsv_report { croak "return value from headers_cb is not an array reference, aborting" if ref ( $readable_headers ) ne 'ARRAY'; - $output .= add_to_xsv( $csv, $readable_headers, $opts{line_ending} ); + if ( $opts{stream} ) { + print add_to_xsv( $csv, $readable_headers, $opts{line_ending} ); + } + else { + $output .= add_to_xsv( $csv, $readable_headers, $opts{line_ending} ); + } } if ( $opts{values} ) { foreach my $list_ref ( @{ $opts{values} } ) { - $output .= add_to_xsv( - $csv, $row_filter->($list_ref, $fields), $opts{line_ending} - ); + if ( $opts{stream} ) { + print add_to_xsv( + $csv, $row_filter->($list_ref, $fields), $opts{line_ending} + ); + } + else { + $output .= add_to_xsv( + $csv, $row_filter->($list_ref, $fields), $opts{line_ending} + ); + } } } # using iterator @@ -168,9 +183,16 @@ sub xsv_report { croak "iterator exceeded maximum iterations ($opts{maximum_iters})" if ++$iterations > $opts{maximum_iters}; - $output .= add_to_xsv( - $csv, $row_filter->($list_ref, $fields), $opts{line_ending} - ); + if ( $opts{stream} ) { + print add_to_xsv( + $csv, $row_filter->($list_ref, $fields), $opts{line_ending} + ); + } + else { + $output .= add_to_xsv( + $csv, $row_filter->($list_ref, $fields), $opts{line_ending} + ); + } } } @@ -193,11 +215,20 @@ sub xsv_report_web { my %opts = ( %defaults, %$args ); - $self->header_props( + my %headers = ( -type => 'application/x-csv', '-content-disposition' => "attachment; filename=$opts{filename}", ); + # we're doing our own output + if ( $opts{stream} ) { + $self->header_type('none'); + print $self->query->header( %headers ); + } + else { + $self->header_props( %headers ); + } + # consider use of magic goto in case of croak() inside xsv_report return xsv_report( \%opts ); } @@ -314,7 +345,7 @@ looked the same: -type => 'application/x-csv', '-content-disposition' => "attachment; filename=export.csv", ); - + return $output; The purpose of this module is to provide a simple method, C<xsv_report_web>, @@ -371,6 +402,7 @@ available options. iterator => \&get_members, csv_opts => { sep_char => "\t" }, filename => 'members.csv', + stream => 1, }); This method generates a csv file that is sent directly to the user's @@ -554,6 +586,22 @@ Note: This parameter used to be named C<get_row_cb>. That name is deprecated and a warning will be issued if it is used instead of C<row_filter>. +=item stream + + stream => 1, + +This flag controls whether or not output is printed immediately or +collected and returned to the caller. Set to a true value to remove +buffering on STDOUT and to emit output as it is generated. This can +save memory in the case of a large document, for example. + +The default is false to retain backwards-compatibility. In general, it +is probably more efficient to set this to a true value, but note that it +breaks with the standard L<CGI::Application|CGI::Application(3)> +convention of returning generated content from your runmodes rather than +printing it yourself. + + =back =back @@ -811,7 +859,7 @@ which is not applicable to this function. 1,2,3 4,5,6 -=item Generate each row on the fly (streaming) +=item Generate each row on the fly my @vals = qw(one two three four five six); @@ -876,13 +924,9 @@ L<Text::CSV_XS>, L<CGI::Application> =head1 COPYRIGHT AND LICENSE -Copyright (c) 2006 CommonMind, LLC. All rights reserved. +Copyright (c) 2006,2010 CommonMind, LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. -=head1 REVISION - -$Id: XSV.pm 43 2006-10-13 15:45:07Z zackse $ - =cut
Argh, the formatting is rough on this page. But if you click to download the patch it should be readable. On Mon Oct 25 22:29:37 2010, ZACKSE wrote: Show quoted text
> On Mon Oct 25 17:33:43 2010, RHESA wrote:
> > The iterator support would be a tad more useful if the output were > > streamed row by row, rather than the current behavior where output
> is
> > collected until the iterator is exhausted. > > > > Attached is a patch that makes this happen. > > > > Rhesa
> > Hello Rhesa, > > Thanks for the suggestion and the patch! I elected to go with a > separate option to the > xsv_report/xsv_report_web routines to indicate that streaming is > desired. That way, > backwards-compatibility is retained. When the option is supplied, it > applies to all uses of the > routines, not just when an iterator is used. > > A patch is attached, please let me know what you think. > > Thanks, > -Evan
Subject: Re: [rt.cpan.org #62449] Streaming support should stream rows, not collect output
Date: Tue, 26 Oct 2010 10:58:36 +0200
To: bug-CGI-Application-Plugin-Output-XSV [...] rt.cpan.org
From: Rhesa Rozendaal <rhesa [...] cpan.org>
On 10/26/2010 04:29 AM, Evan A. Zacks via RT wrote: Show quoted text
> <URL: https://rt.cpan.org/Ticket/Display.html?id=62449> > > Hello Rhesa, > > Thanks for the suggestion and the patch! I elected to go with a separate option to the > xsv_report/xsv_report_web routines to indicate that streaming is desired. That way, > backwards-compatibility is retained. When the option is supplied, it applies to all uses of the > routines, not just when an iterator is used.
I can see that helps backwards-compatibility with testing. Show quoted text
> A patch is attached, please let me know what you think.
Looks good to me, though I'd have phrased the if/else blocks a bit differently. See the attached patch. Either way the plugin now works with streaming, which is great news :) Thanks for the quick response. Glad to see you're still maintaining it after 4 years :) Rhesa

Message body is not shown because sender requested not to inline it.

On Tue Oct 26 04:58:48 2010, RHESA wrote: Show quoted text
> On 10/26/2010 04:29 AM, Evan A. Zacks via RT wrote:
> > <URL: https://rt.cpan.org/Ticket/Display.html?id=62449> > > > > Hello Rhesa, > > > > Thanks for the suggestion and the patch! I elected to go with a
> separate option to the
> > xsv_report/xsv_report_web routines to indicate that streaming is
> desired. That way,
> > backwards-compatibility is retained. When the option is supplied, it
> applies to all uses of the
> > routines, not just when an iterator is used.
> > I can see that helps backwards-compatibility with testing. >
> > A patch is attached, please let me know what you think.
> > Looks good to me, though I'd have phrased the if/else blocks a bit > differently. See the attached patch. Either way the plugin now works > with streaming, which is great news :) > > Thanks for the quick response. Glad to see you're still maintaining it > after 4 years :)
Thanks, the original patch was a bit sloppy. Applied and released as 1.01, should be on the CPAN mirrors soon. I have been away from the cgi-app community for a while, but it's good to hear that the library is working well for you. -Evan
Fixed in 1.01.