Skip Menu |

This queue is for tickets about the IO-CSVHeaderFile CPAN distribution.

Report information
The Basics
Id: 27866
Status: resolved
Priority: 0/
Queue: IO-CSVHeaderFile

People
Owner: Nobody in particular
Requestors: ickphum [...] gmail.com
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in:
  • 0.03
  • 0.3
Fixed in: 0.04



Subject: Constructor with args broken for versions of Text::CSV_XS >= 0.28
Module version: IO::CSVHeaderFile-0.03 Perl version: This is perl, v5.8.5 built for i386-linux-thread-multi OS: 2.6.9-42.ELsmp #1 SMP Sat Aug 12 09:39:11 CDT 2006 i686 i686 i386 GNU/Linux Hi, If the version of Text::CSV_XS >= 0.28, constructing a new IO::CSVHeaderFile object will fail if any args are passed as a hash reference in the constructor call. This is because this hash reference is passed through to the Text::CSV_XS constructor and versions of Text::CSV_XS have tighter checking of the constructor args. When called like this: my @cols = qw(One Two Three); my $csv = IO::CSVHeaderFile->new('> test.csv', { col => \@cols }); The constructor fails with this error message: Can't call method "print" on an undefined value at /usr/lib/perl5/site_perl/5.8.5/IO/CSVHeaderFile.pm line 56 The patch below is one way to fix it, by using a specific key in the IO::CSVHeaderFile constructor to include arguments meant for Text::CSV_XS. This patch also changes the setting of the default eol key so that it uses a logical OR instead of a binary OR. --- CSVHeaderFile.pm.03 2004-11-21 09:21:02.000000000 +1100 +++ CSVHeaderFile.pm 2007-07-02 11:26:17.000000000 +1000 @@ -19,8 +19,9 @@ sub open { my $self = shift; my $args = {}; $args = pop @_ if ref($_[$#_]) eq 'HASH'; - $args->{eol}|= "\n"; - my $csv = Text::CSV_XS->new($args); + my $csv_args = $args->{csv_args} || {}; + $csv_args->{eol} ||= "\n"; + my $csv = Text::CSV_XS->new($csv_args); my $mode; if(@_ > 1){ croak 'usage: $fh->open(FILENAME [ ,< > >> ][,CSVOPT])' if $_[2] =~ /^\d+$/;
From: vasek [...] ti.cz
On Sun Jul 01 21:35:46 2007, ickphum wrote: Show quoted text
> Hi, > > If the version of Text::CSV_XS >= 0.28, constructing a new > IO::CSVHeaderFile object will fail if any args are passed as a hash > reference in the constructor call. This is because this hash reference > is passed through to the Text::CSV_XS constructor and versions of > Text::CSV_XS have tighter checking of the constructor args.
Please check out the 0.04 version to appear on CPAN soon (attached here). it should fix the problem while keeping backwards compatible style of calling. I will appreciate any feedback. -- Vasek
package IO::CSVHeaderFile; # $Id: CSVHeaderFile.pm,v 1.2 2007/07/06 08:44:46 vasek Exp $ use strict; use Text::CSV_XS; use IO::File; use vars qw(@ISA @EXPORT $VERSION); use Exporter; use Carp; @ISA = qw(IO::File Exporter); @EXPORT = qw( ); $VERSION = '0.04'; my $SUPPORTED_XS_ARGS; sub open { my $self = shift; my $args = {}; $args = pop @_ if ref($_[$#_]) eq 'HASH'; _init_supported_xs_args(); my %xs_args = ( 'eol' => "\n", map {exists $SUPPORTED_XS_ARGS->{$_} ? ($_ => $args->{$_}):()} keys %$args); my $csv = Text::CSV_XS->new(\%xs_args); my $mode; if(@_ > 1){ croak 'usage: $fh->open(FILENAME [ ,< > >> ][,CSVOPT])' if $_[2] =~ /^\d+$/; $mode = IO::Handle::_open_mode_string($_[1]); }else{ $mode = $_[0]; $mode =~ s/^(\+?<|>>?)(.*)$/$1/ or croak 'usage: $fh->open(FILENAME [,< > >> ][,CSVOPT])'; } my ($fh, $firstline); if($mode =~ /^<$/){ $fh = $self->SUPER::open( @_ ) or return; unless($args->{noheader}){ unless( $firstline = $self->getline ){ $self->close; return; } $csv->parse($firstline) and $args->{col} = [ $csv->fields ] unless $args->{col}; } unless(${*$self}{io_csvheaderfile_cols} = $args->{col}){ $self->close; croak "IO::CSVHeaderFile: Can't find the column names in '$_[0]'"; return; } }elsif( $mode =~ /^>>?$/){ unless(${*$self}{io_csvheaderfile_cols} = $args->{col}){ $self->close; croak "IO::CSVHeaderFile: Can't find the column names in '$_[0]'"; return; } $fh = $self->SUPER::open( @_ ) or return; $csv->print($self, $args->{col}) unless $mode =~ /^>>$/ or $args->{noheader}; }else{ croak "IO::CSVHeaderFile: Invalid mode '$mode'"; return; } ${*$self}{io_csvheaderfile_csv} = $csv; $fh } sub csv_read{ my $self = shift; my $line = $self->getline() or return; my @result = (); if( ${*$self}{io_csvheaderfile_csv}->parse($line) ){ my @cols = ${*$self}{io_csvheaderfile_csv}->fields; my $colnames = ${*$self}{io_csvheaderfile_cols}; my $avail_cols = (@cols > @$colnames)? @$colnames : @cols; for(my $i = 0; $i < $avail_cols; $i++){ push @result, $colnames->[$i] => $cols[$i]; } } wantarray? @result : { @result } } sub csv_print{ my $self = shift; return undef unless @_; my $rec = $_[0]; my @columns = (); my $colnames = ${*$self}{io_csvheaderfile_cols}; unless( ref $rec ){ my %map = (); for(my $i = 0; $i < @$colnames; $i++){ $map{$colnames->[$i]} = [] unless exists $map{$colnames->[$i]}; push @{$map{$colnames->[$i]}}, $i; } while ( my ($key, $value) = splice(@_, 0, 2) ) { my $idx = $map{$key} or next; $columns[$idx->[0]] = $value; shift @$idx if @$idx > 1; } }elsif( ref ($rec) eq 'HASH' ){ push( @columns, $rec->{$_}) foreach (@$colnames); }elsif( ref ($rec) eq 'ARRAY' ){ for( my $i = 0; $i < @$colnames; $i++){ push @columns, $rec->[$i]; } } ${*$self}{io_csvheaderfile_csv}->print($self,\@columns); } sub _init_supported_xs_args { return if defined $SUPPORTED_XS_ARGS; my $tmpcsvxs = Text::CSV_XS->new(); $SUPPORTED_XS_ARGS = UNIVERSAL::isa($tmpcsvxs, "HASH")? {%$tmpcsvxs}: {map {$_ => undef} qw(eol sep_char allow_whitespace quote_char allow_loose_quotes escape_char allow_loose_escapes binary types always_quote keep_meta_info)}; } 1; __END__ # Below is stub documentation for your module. You better edit it! =head1 NAME IO::CSVHeaderFile - Perl extension for CSV Files =head1 SYNOPSIS # to read ... use IO::CSVHeaderFile; my $csv = IO::CSVHeaderFile->new( "< $filename" ); while(my $hash = $csv->csv_read ){ print "$hash->{ColHeaderTitle}\n"; } $csv->close; # or for same named columns my $csv = IO::CSVHeaderFile->new( "< $filename" ); my $data; while(@array = $csv->csv_read ){ for(my $i=0; $i< @array; $i++) { print "Column '$array[$i]': $array[$i]\n"; } print "-- end of record\n"; } $csv->close; # to write ... use IO::CSVHeaderFile; my $csv = IO::CSVHeaderFile->new( "> $filename" , {col => ['ColHeaderTitle1','ColHeaderTitle2','ColHeaderTitle1'], noheaders => 1} ); $csv->csv_print({ColHeaderTitle1 => 'First', ColHeaderTitle2 => 'Second'}) or return; $csv->csv_print(['Uno', 'Duo', 'Tre']) or return; $csv->csv_print( ColHeaderTitle1 => 'One', ColHeaderTitle2 => 'Two', ColHeaderTitle1 => 'Three with the same name as One' ) or return; $csv->close; =head1 DESCRIPTION Read from and write to csv file. =head2 EXPORT None by default. =head2 FUNCTIONS =over 4 =item csv_print RECORD | LIST Store the C<RECORD> into file, C<RECORD> can be hash reference as returned from C<csv_read> or an array ref with values ordered same as respctive headers in file. If LIST variant is used it can be a hash definition like a list in form of headers and values, but the header names doesn't have to be unique. This is usefull when creating a CSV file with several same named columns. =item csv_read Return the next record (hash reference in scalar context, array of header names and values in list context) from the file. Returns C<undef> if C<eof>. =cut =head1 AUTHOR Vasek Balcar, E<lt>vasek@ti.czE<gt> =head1 SEE ALSO L<IO::File>, L<IO::Handle>, L<perl>. =cut
Subject: Re: [rt.cpan.org #27866] Constructor with args broken for versions of Text::CSV_XS >= 0.28
Date: Fri, 6 Jul 2007 22:01:27 +1000
To: bug-IO-CSVHeaderFile [...] rt.cpan.org
From: "Ian Macdonald" <ickphum [...] gmail.com>
Thanks Vasek, will test it next week and get back to you. Ian On 7/6/07, VaĊĦek Balcar via RT <bug-IO-CSVHeaderFile@rt.cpan.org> wrote: Show quoted text
> > > <URL: http://rt.cpan.org/Ticket/Display.html?id=27866 > > > On Sun Jul 01 21:35:46 2007, ickphum wrote: >
> > Hi, > > > > If the version of Text::CSV_XS >= 0.28, constructing a new > > IO::CSVHeaderFile object will fail if any args are passed as a hash > > reference in the constructor call. This is because this hash reference > > is passed through to the Text::CSV_XS constructor and versions of > > Text::CSV_XS have tighter checking of the constructor args.
> > Please check out the 0.04 version to appear on CPAN soon (attached here). > it should fix the > problem while keeping backwards compatible style of calling. > > I will appreciate any feedback. > > -- > Vasek > >
From: myfanwy.white [...] datacomit.com.au
On Fri Jul 06 08:01:49 2007, ickphum wrote: Show quoted text
> Thanks Vasek, will test it next week and get back to you. > > Ian >
This has been tested and the ticket can be resolved.