[Catalyst-dev] RFC: Catalyst::View::CSV

J. Shirley jshirley at gmail.com
Mon Mar 3 17:27:42 GMT 2008


On Sun, Mar 2, 2008 at 10:05 PM, Travis Chase <travis at ti4tech.com> wrote:
> I have written a module for producing CSV formatted output as a view. It, of
> course, can create any sort of delimited format one desires. Here is the POD
> for details.
>
> =head1 NAME
>
> Catalyst::View::CSV - Comma separated values or Delimiter separated values
> for your data
>
> =head1 SYNOPSIS
>
>   # lib/MyApp/View/CSV.pm
>   package MyApp::View::CSV;
>   use base qw( Catalyst::View::CSV );
>   1;
>
>   # lib/MyApp/Controller/SomeController.pm
>   sub example_action_1 : Local {
>      my ($self, $c) = @_;
>
>     # Array reference of array references.
>     my $data = [
>       ['col 1','col 2','col ...','col N'], # row 1
>       ['col 1','col 2','col ...','col N'], # row 2
>        ['col 1','col 2','col ...','col N'], # row ...
>       ['col 1','col 2','col ...','col N']  # row N
>     ];
>
>     # To output your data in comma seperated values just pass your array by
> reference into the 'csv' key of the stash
>      $c->stash->{'csv'} = $data;
>
>     # Finally forward processing to the CSV View
>     $c->forward('MyApp::View::CSV');
>   }
>
>   # Other ways of storing data
>   sub example_action_2 : Local {
>      my ($self, $c) = @_;
>
>     # Array of array references
>     my @data;
>
>     push(@data,['col 1','col 2','col ...','col N']); # row 1
>     push(@data,['col 1','col 2','col ...','col N']); # row 2
>      push(@data,['col 1','col 2','col ...','col N']); # row ...
>     push(@data,['col 1','col 2','col ...','col N']); # row N
>
>     # OR to produce a single column of data you can simply do the following
>      my @data = (
>                 'col 1 row 1',
>                 'col 1 row 2',
>                 'col 1 row ...',
>                 'col 1 row N'
>                );
>
>     $c->stash->{'csv'} = \@data;
>
>     $c->forward('MyApp::View::CSV');
>   }
>
>   # Available Options to produce other types of delimiter seperated output
>   sub  example_action_3 : Local {
>     my ($self, $c) = @_;
>
>     my $data = [
>        ['col 1','col 2','col ...','col N'], # row 1
>       ['col 1','col 2','col ...','col N'] # row 2
>     ];
>
>     # You can change any of the aspects of a delimiter seperated values
> format by storing them in the appropriate stash key
>      # This is an example of tab seperated values for instance
>
>     $c->stash->{'quote_char'} = '"'; # default: '"'
>
>     $c->stash->{'escape_char'} = '"'; # default: '"'
>
>     $c->stash->{'sep_char'} = '\t'; # default: ','
>
>     $c->stash->{'eol'} = "\n"; # default: "\n"
>
>     $c->stash->{'csv'} = $data;
>    }
>
> =head1 DESCRIPTION
>
> Catalyst::View::CSV is a Catalyst View handler that returns data in
> delimiter seperated values (default is comma) format.
>
> =head1 MIME MEDIA TYPE
>
> If the Content-Type HTTP Header is not set, it will default to 'text/csv'.
>
>   # Example of setting your own Content-Type
>   $c->res->headers->header('Content-Type' => 'text/plain');
>
>   # Forward processing to CSV View with a text/plain Content-Type
>   $c->forward("MyApp::View::CSV");
>
> =head1 OPTIONS
>
> =over 4
>
> =item quote_char
>
> Determines what value will be enclosed within if it contains whitespace or
> the delimiter character. DEFAULT: '"'
>
>   $c->stash->{'quote_char'} = '/';
>
> =item escape_char
>
> Determines what value will be to escape any delimiter's found in a column.
> DEFAULT: '"'
>
>   $c->stash->{'escape_char'} = '/';
>
> =item sep_char
>
> Determines the separator between columns. DEFAULT: ','
>
>   $c->stash->{'sep_char'} = '|';
>
> =item eol
>
> Any characters defined in eol will be placed at the end of a row. DEFAULT:
> '\n'
>
>   $c->stash->{'eol'} = '\0';
>
> =item csv
>
> The data that will be processed into delimiter separated values format is
> stored here. The data should be an array ref of array refs of scalars or an
> array ref of scalars. Note: if nothing is found in csv, the stash is
> searched and any array references found will be used as the data instead.
>
>   # Array ref of array refs of scalars
>   my $data = [
>     ['apple','banana','pear'],
>     ['red','yellow','green']
>   ];
>
>   $c->stash->{csv} = $data;
>
>   # Array ref of scalars
>   my @data = ('Jan','Feb','Mar','Apr');
>
>   $c->stash->{csv} = \@data;
>
> =back
>
> =head1 SUBROUTINES
>
> =over 4
>
> =item process
>
> This method will be called by Catalyst if it is asked to forward to a
> component without a specified action.
>
> =item render
>
> Allows others to use this view for much more fine-grained content
> generation.
>
>  =item _csv
>
> Subroutine that actually produces the delimiter separated values. Intended
> to be private in scope to this module.
>
> =back
>
> =head1 AUTHOR
>
> Travis Chase - gaudeon_at_cpan_dot_org
>
> =head1 SEE ALSO
>
> L<Catalyst> L<Text::CSV>
>
> =head1 LICENSE
>
> This library is free software, you can redistribute it and/or modify
> it under the same terms as Perl itself.
>
> =cut
>
> _______________________________________________
>  Catalyst-dev mailing list
>  Catalyst-dev at lists.scsys.co.uk
>  http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst-dev
>
>

Hey Travis,

It's a good idea that you have there, but it may be better looped into
a larger set of modules.  Also, rather than setting a lot of the
config in stash, you should use the module's configuration, like so:

package MyApp::View::CSV;
use base qw(Catalyst::View::CSV);
__PACKAGE__->config(
    quote_char => '/'',
   'sep_char' => '|',
);

This way you can set sensible defaults in C::V::CSV that are easily
configured on a per-view basis and reduce what has to go into the
stash (and, you can have a configurable stash key as well)

Another idea that I have is an abstraction of serialization formats.
The way I see CSV exporting is a serialization format, much like XLS
would be or even XML and YAML.   Catalyst::Action::REST has a good
serialization setup, but it may not be worth factoring that entire
package into your application, and what may prove to be more useful
and reusable is to abstract out the serialization from C::A::REST into
a Catalyst::View::Serialize package and then creating
C::V::Serialize::(XML|CSV|XLS|Dumper|PHP).

This way C::A::REST can use that, and for non-RESTful applications the
serialization can be handled.  I tend to always use the REST base
classes, and then I create custom serializers for things like this.

Let me know if you like these thoughts, and we can work together on
getting it going.

-J

-- 
J. Shirley :: jshirley at gmail.com :: Killing two stones with one bird...
http://www.toeat.com



More information about the Catalyst-dev mailing list