File Coverage

blib/lib/Tie/Handle/CSV.pm
Criterion Covered Total %
statement 128 128 100.0
branch 49 50 98.0
condition 13 14 92.8
subroutine 19 19 100.0
pod 2 2 100.0
total 211 213 99.0


line stmt bran cond sub pod time code
1             package Tie::Handle::CSV;
2              
3 10     10   58676 use 5.006;
  10         39  
  10         470  
4 10     10   85 use strict;
  10         24  
  10         1121  
5 10     10   65 use warnings;
  10         21  
  10         379  
6              
7 10     10   63 use Carp;
  10         25  
  10         3421  
8 10     10   777 use Symbol;
  10         833  
  10         785  
9 10     10   366 use Scalar::Util;
  10         28  
  10         460  
10 10     10   333258 use Text::CSV_XS;
  10         155985  
  10         815  
11              
12 10     10   7957 use Tie::Handle::CSV::Hash;
  10         38  
  10         328  
13 10     10   8287 use Tie::Handle::CSV::Array;
  10         32  
  10         16646  
14              
15             our $VERSION = '0.15';
16              
17             sub new
18             {
19 28     28 1 5283 my $class = shift;
20 28         133 my $self = bless gensym(), $class;
21 28         616 tie *$self, $self;
22 28         105 $self->_open(@_);
23 19         161 return $self;
24             }
25              
26             sub TIEHANDLE
27             {
28 37 100   37   6804 return $_[0] if ref $_[0];
29 9         21 my $class = shift;
30 9         43 return $class->new(@_);
31             }
32              
33             sub _open
34             {
35 28     28   102 my ($self, @opts) = @_;
36              
37 28         40 my ($file, %opts, $csv_fh);
38              
39             ## if an odd number of options are given,
40             ## assume the first arg is the file name
41 28 100       99 if (@opts % 2)
42             {
43 23         39 $file = shift @opts;
44 23         84 %opts = @opts;
45 23         59 $opts{'file'} = $file;
46             }
47             else
48             {
49 5         20 %opts = @opts;
50             }
51              
52             ## support old 'openmode' option key
53 28 100 100     217 if ( exists $opts{'openmode'} && ! exists $opts{'open_mode'} )
54             {
55 1         4 $opts{'open_mode'} = $opts{'openmode'};
56             }
57              
58             ## support old 'stringify' option key
59 28 100 100     115 if ( exists $opts{'stringify'} && ! exists $opts{'simple_reads'} )
60             {
61 1         4 $opts{'simple_reads'} = ! $opts{'stringify'};
62             }
63              
64 28   100     218 my $file_ref_type = Scalar::Util::reftype( $opts{'file'} ) || '';
65              
66 28 100       75 if ( $file_ref_type eq 'GLOB' )
67             {
68 1         3 $csv_fh = $opts{'file'};
69             }
70             else
71             {
72              
73             ## use 3-arg open if 'open_mode' is specified,
74             ## otherwise use 2-arg to work with STDIN via '-'
75 27 100       80 if ( defined $opts{'open_mode'} )
76             {
77 5 100       511 open( $csv_fh, $opts{'open_mode'}, $opts{'file'} )
78             || croak "$!: $opts{'file'}";
79             }
80             else
81             {
82 22 100       2941 open( $csv_fh, $opts{'file'} ) || croak "$!: $opts{'file'}";
83             }
84              
85             }
86              
87             ## establish the csv object
88             ## use given sep_char when possible
89 20 100       101 if ( $opts{'csv_parser'} )
    100          
90             {
91 2 100       8 if ( ref $opts{'csv_parser'} ne 'Text::CSV_XS' )
92             {
93 1         186 confess "'csv_parser' is not an instance of 'Text::CSV_XS'";
94             }
95             }
96             elsif ( defined $opts{'sep_char'} )
97             {
98 2         24 $opts{'csv_parser'} =
99             Text::CSV_XS->new( { sep_char => $opts{'sep_char'}, binary => 1 } );
100             }
101             else
102             {
103 16         159 $opts{'csv_parser'} = Text::CSV_XS->new( { binary => 1 } );
104             }
105              
106 19 100       2350 $opts{'header'} = 1 unless exists $opts{'header'};
107              
108 19 100       64 if ( $opts{'header'} )
109             {
110              
111 14 100       49 if ( ref $opts{'header'} ne 'ARRAY' )
112             {
113 9         175 my $header_line = <$csv_fh>;
114 9 50       47 $opts{'csv_parser'}->parse($header_line)
115             || croak $opts{'csv_parser'}->error_input();
116 9         391 $opts{'header'} = [ $opts{'csv_parser'}->fields() ];
117             }
118              
119 14         99 $opts{'orig_header'} = [ @{ $opts{'header'} } ];
  14         53  
120              
121             ## support old 'force_lower' option key
122 14 100 100     72 if ( $opts{'force_lower'} && ! $opts{'key_case'} )
123             {
124 4         11 $opts{'key_case'} = 'lower';
125             }
126              
127 14 100       40 if ( $opts{'key_case'} )
128             {
129              
130 9 100       37 if ( lc $opts{'key_case'} eq 'lower' )
    100          
131             {
132 6         9 for my $header ( @{ $opts{'header'} } )
  6         14  
133             {
134 18         36 $header = lc $header;
135             }
136             }
137             elsif ( lc $opts{'key_case'} eq 'upper' )
138             {
139 1         2 for my $header ( @{ $opts{'header'} } )
  1         3  
140             {
141 3         7 $header = uc $header;
142             }
143             }
144              
145             }
146              
147             }
148              
149 19         60 *$self->{handle} = $csv_fh;
150 19         71 *$self->{opts} = \%opts;
151             }
152              
153             sub READLINE
154             {
155 64     64   3652 my ($self) = @_;
156              
157 64         121 my $opts = *$self->{'opts'};
158              
159 64 100       142 if (wantarray)
160             {
161              
162 1         2 my @parsed_lines;
163              
164 1         6 while (my $parsed_line = $self->READLINE)
165             {
166 3         25 push @parsed_lines, $parsed_line;
167             }
168              
169 1         6 return @parsed_lines;
170              
171             }
172             else
173             {
174 63     9   2185 my $cols = $opts->{'csv_parser'}->getline(*$self->{'handle'});
  9         84  
  9         17  
  9         524  
175 63 100       2615 if (defined $cols)
176             {
177 44 100       167 if ( $opts->{'header'} )
178             {
179 33         43 my $parsed_line;
180              
181 33 100       79 if ( $opts->{'simple_reads'} )
182             {
183 11         16 @{ $parsed_line }{ @{ $opts->{'header'} } } = @{ $cols };
  11         49  
  11         23  
  11         21  
184             }
185             else
186             {
187 22         124 $parsed_line = Tie::Handle::CSV::Hash->_new($self);
188 22         70 $parsed_line->_init_store( $cols );
189             }
190              
191 33         148 return $parsed_line;
192             }
193             else
194             {
195 11         25 my $parsed_line;
196              
197 11 100       26 if ( $opts->{'simple_reads'} )
198             {
199 3         5 @{ $parsed_line } = @{ $cols };
  3         9  
  3         5  
200             }
201             else
202             {
203 8         51 $parsed_line = Tie::Handle::CSV::Array->_new($self);
204 8         27 $parsed_line->_init_store( $cols );
205             }
206              
207 11         53 return $parsed_line;
208             }
209             }
210              
211             }
212              
213 19         60 return;
214              
215             }
216              
217             sub CLOSE
218             {
219 19     19   54708 my ($self) = @_;
220 19         545 return close *$self->{'handle'};
221             }
222              
223             sub PRINT
224             {
225 1     1   4 my ($self, @list) = @_;
226 1         2 my $handle = *$self->{'handle'};
227 1         9 return print $handle @list;
228             }
229              
230             sub SEEK
231             {
232 3     3   6 my ($self, $position, $whence) = @_;
233 3         32 return seek *$self->{'handle'}, $position, $whence;
234             }
235              
236             sub TELL
237             {
238 2     2   6 my ($self) = @_;
239 2         11 return tell *$self->{'handle'};
240             }
241              
242             sub header
243             {
244 5     5 1 1254 my ($self) = @_;
245 5         13 my $opts = *$self->{opts};
246 5         11 my $header = $opts->{orig_header};
247 5         9 my $parser = $opts->{csv_parser};
248              
249 5 100 66     38 if ( ! $header || ref $header ne 'ARRAY' )
250             {
251 1         219 croak "handle does not contain a header";
252             }
253              
254 4         29 my $header_array = Tie::Handle::CSV::Array->_new($self);
255 4         6 @{ $header_array } = @{$header};
  4         94  
  4         9  
256 4         25 return $header_array;
257             }
258              
259             1;
260              
261             __END__