File Coverage

blib/lib/Tie/Array/CSV.pm
Criterion Covered Total %
statement 130 148 87.8
branch 29 38 76.3
condition 9 11 81.8
subroutine 28 32 87.5
pod 1 2 50.0
total 197 231 85.2


line stmt bran cond sub pod time code
1             package Tie::Array::CSV;
2              
3 7     7   77723 use strict;
  7         21  
  7         175  
4 7     7   31 use warnings;
  7         9  
  7         305  
5              
6             our $VERSION = '0.08';
7             $VERSION = eval $VERSION;
8              
9 7     7   39 use Carp;
  7         11  
  7         372  
10              
11 7     7   3031 use Tie::File;
  7         81185  
  7         219  
12 7     7   706 use Text::CSV;
  7         11640  
  7         262  
13              
14 7     7   53 use Scalar::Util qw/blessed/;
  7         13  
  7         328  
15              
16 7     7   3111 use Tie::Array;
  7         6681  
  7         5094  
17             our @ISA = ('Tie::Array');
18              
19             sub parse_opts {
20 29     29 0 56 my $class = shift;
21              
22 29 50       78 croak "Must specify a file" unless @_;
23              
24 29         38 my ($file, %opts);
25              
26             # handle one arg as either hashref (of opts) or file
27 29 100       58 if (@_ == 1) {
28 10 100       31 if (ref $_[0] eq 'HASH') {
29 2         3 %opts = %{ shift() };
  2         6  
30             } else {
31 8         14 $file = shift;
32             }
33             }
34              
35             # handle file and hashref of opts
36 29 100 100     89 if (@_ == 2 and ref $_[1] eq 'HASH') {
37 12         17 $file = shift;
38 12         11 %opts = %{ shift() };
  12         32  
39             }
40              
41             # handle file before hash of opts
42 29 100       65 if (@_ % 2) {
43 5         7 $file = shift;
44             }
45              
46             # handle hash of opts
47 29 100       67 if (@_) {
48 7         28 %opts = @_;
49             }
50              
51             # handle file passed has hash(ref) value to 'file' key
52 29 100 66     90 if (!$file and defined $opts{file}) {
53 4         5 $file = delete $opts{file};
54             }
55              
56             # file wasn't specified as lone arg or as a hash opt
57 29 50       70 croak "Must specify a file" unless $file;
58              
59             # parse specific options
60 29 100       56 if (exists $opts{sep_char}) {
61 4         11 $opts{text_csv}{sep_char} = delete $opts{sep_char};
62             }
63              
64 29         62 return ($file, \%opts);
65             }
66              
67             sub new {
68 7     7 1 1998 my $class = shift;
69 7         17 my ($file, $opts) = $class->parse_opts(@_);
70              
71 7         11 my @self;
72 7         15 tie @self, $class, $file, $opts;
73              
74 7         19 return \@self;
75              
76             }
77              
78             sub TIEARRAY {
79 22     22   9007 my $class = shift;
80 22         98 my ($file, $opts) = $class->parse_opts(@_);
81              
82 22         29 my @tiefile;
83 22 50       30 tie @tiefile, 'Tie::File', $file, recsep => "\n", %{ $opts->{tie_file} || {} }
  22 50       153  
84             or croak "Cannot tie file $file";
85              
86 22         2377 my $csv;
87 22 100 66     147 if (blessed $opts->{text_csv} and $opts->{text_csv}->isa('Text::CSV')) {
88 1         2 $csv = $opts->{text_csv};
89             } else {
90             $csv = Text::CSV->new($opts->{text_csv} || {})
91 21 50 100     158 or croak "CSV (new) error: " . Text::CSV->error_diag();
92             }
93              
94 22         2296 my $self = {
95             file => \@tiefile,
96             csv => $csv,
97             };
98              
99 22         38 bless $self, $class;
100              
101 22         72 return $self;
102             }
103              
104             sub FETCH {
105 92     92   6060 my $self = shift;
106 92         104 my $index = shift;
107              
108 92         218 my $line = $self->{file}[$index];
109              
110 92         7774 my $rowclass = ref($self) . '::Row';
111             tie my @line, $rowclass, {
112             file => $self->{file},
113             line_num => $index,
114             fields => $self->_parse($line),
115             csv => $self->{csv},
116 92         200 };
117              
118 92         302 return \@line;
119             }
120              
121             sub STORE {
122 4     4   33 my $self = shift;
123 4         7 my ($index, $value) = @_;
124              
125 4         7 $self->{file}[$index] = $self->_combine($value);
126             }
127              
128             sub FETCHSIZE {
129 81     81   10301 my $self = shift;
130              
131 81         103 return scalar @{ $self->{file} };
  81         248  
132             }
133              
134             sub STORESIZE {
135 3     3   367 my $self = shift;
136 3         4 my $new_size = shift;
137              
138 3         4 $#{ $self->{file} } = $new_size - 1;
  3         10  
139            
140             }
141              
142             sub EXISTS {
143 2     2   253 my $self = shift;
144 2         3 my ($index) = shift;
145 2         8 return exists $self->{file}[$index];
146             }
147              
148             sub DELETE {
149 1     1   13 my $self = shift;
150 1         2 my $index = shift;
151 1         3 return $self->SPLICE($index,1);
152             }
153              
154             sub _parse {
155 93     93   112 my $self = shift;
156 93         150 my ($line) = @_;
157 93 100       172 $line = '' unless defined $line;
158              
159 93 50       260 return [$self->{csv}->fields] if $self->{csv}->parse($line);
160              
161 0         0 croak "CSV parse error: " . $self->{csv}->error_diag;
162             }
163              
164             sub _combine {
165 5     5   7 my $self = shift;
166 5         6 my ($value) = @_;
167              
168             return $self->{csv}->string
169 5 50       23 if $self->{csv}->combine( ref $value ? @$value : ($value) );
    50          
170              
171 0         0 croak "CSV combine error: " . $self->{csv}->error_diag();
172             }
173              
174             package Tie::Array::CSV::Row;
175              
176 7     7   54 use Carp;
  7         22  
  7         366  
177              
178 7     7   37 use Tie::Array;
  7         13  
  7         422  
179             our @ISA = ('Tie::Array');
180              
181             use overload
182 7     7   40 '@{}' => sub{ $_[0]{fields} };
  7     0   14  
  7         75  
  0         0  
183              
184             sub TIEARRAY {
185 92     92   2523 my $class = shift;
186 92         109 my $self = shift;
187              
188 92         119 bless $self, $class;
189              
190 92         143 return $self;
191             }
192              
193             sub FETCH {
194 195     195   758 my $self = shift;
195 195         198 my $index = shift;
196              
197 195         416 return $self->{fields}[$index];
198             }
199              
200             sub STORE {
201 12     12   600 my $self = shift;
202 12         18 my ($index, $value) = @_;
203              
204 12         25 $self->{fields}[$index] = $value;
205              
206 12         32 $self->_update;
207              
208             }
209              
210             sub FETCHSIZE {
211 241     241   9019 my $self = shift;
212              
213 241         259 return scalar @{ $self->{fields} };
  241         536  
214             }
215              
216             sub STORESIZE {
217 3     3   10 my $self = shift;
218 3         8 my $new_size = shift;
219              
220             my $return = (
221 3         5 $#{ $self->{fields} } = $new_size - 1
  3         37  
222             );
223              
224 3         7 $self->_update;
225              
226 3         855 return $return;
227             }
228              
229             sub SHIFT {
230 1     1   2 my $self = shift;
231              
232 1         2 my $value = shift @{ $self->{fields} };
  1         3  
233              
234 1         3 $self->_update;
235              
236 1         263 return $value;
237             }
238              
239             sub UNSHIFT {
240 0     0   0 my $self = shift;
241 0         0 my $value = shift;
242              
243 0         0 unshift @{ $self->{fields} }, $value;
  0         0  
244              
245 0         0 $self->_update;
246              
247 0         0 return $self->FETCHSIZE();
248             }
249              
250             sub DELETE {
251 0     0   0 my $self = shift;
252 0         0 my $index = shift;
253              
254 0         0 my $return = splice @{ $self->{fields} }, $index, 1;
  0         0  
255 0         0 $self->_update;
256              
257 0         0 return $return;
258             }
259              
260             sub EXISTS {
261 0     0   0 my $self = shift;
262 0         0 my $index = shift;
263              
264 0         0 return exists $self->{fields}[$index];
265             }
266              
267             sub _update {
268 16     16   19 my $self = shift;
269              
270 16 100       18 if(@{ $self->{fields} }) {
  16         36  
271 14         58 $self->{csv}->combine(@{ $self->{fields} })
272 14 50       21 or croak "CSV combine error: " . $self->{csv}->error_diag();
273 14         260 $self->{file}[$self->{line_num}] = $self->{csv}->string;
274             } else {
275 2         10 $self->{file}[$self->{line_num}] = '';
276             }
277             }
278              
279             __END__