File Coverage

blib/lib/Tie/Array/CSV.pm
Criterion Covered Total %
statement 127 145 87.5
branch 27 36 75.0
condition 9 11 81.8
subroutine 28 32 87.5
pod 1 2 50.0
total 192 226 84.9


line stmt bran cond sub pod time code
1             package Tie::Array::CSV;
2              
3 6     6   65389 use strict;
  6         15  
  6         229  
4 6     6   37 use warnings;
  6         9  
  6         387  
5              
6             our $VERSION = '0.07';
7             $VERSION = eval $VERSION;
8              
9 6     6   54 use Carp;
  6         11  
  6         410  
10              
11 6     6   5507 use Tie::File;
  6         130355  
  6         369  
12 6     6   1043 use Text::CSV;
  6         12018  
  6         78  
13              
14 6     6   192 use Scalar::Util qw/blessed/;
  6         15  
  6         407  
15              
16 6     6   6698 use Tie::Array;
  6         7992  
  6         23492  
17             our @ISA = ('Tie::Array');
18              
19             sub parse_opts {
20 28     28 0 52 my $class = shift;
21              
22 28 50       96 croak "Must specify a file" unless @_;
23              
24 28         39 my ($file, %opts);
25              
26             # handle one arg as either hashref (of opts) or file
27 28 100       87 if (@_ == 1) {
28 9 100       42 if (ref $_[0] eq 'HASH') {
29 2         4 %opts = %{ shift() };
  2         8  
30             } else {
31 7         18 $file = shift;
32             }
33             }
34              
35             # handle file and hashref of opts
36 28 100 100     150 if (@_ == 2 and ref $_[1] eq 'HASH') {
37 12         20 $file = shift;
38 12         15 %opts = %{ shift() };
  12         42  
39             }
40              
41             # handle file before hash of opts
42 28 100       101 if (@_ % 2) {
43 5         10 $file = shift;
44             }
45              
46             # handle hash of opts
47 28 100       62 if (@_) {
48 7         23 %opts = @_;
49             }
50              
51             # handle file passed has hash(ref) value to 'file' key
52 28 100 66     112 if (!$file and defined $opts{file}) {
53 4         8 $file = delete $opts{file};
54             }
55              
56             # file wasn't specified as lone arg or as a hash opt
57 28 50       92 croak "Must specify a file" unless $file;
58              
59             # parse specific options
60 28 100       91 if (exists $opts{sep_char}) {
61 4         14 $opts{text_csv}{sep_char} = delete $opts{sep_char};
62             }
63              
64 28         81 return ($file, \%opts);
65             }
66              
67             sub new {
68 7     7 1 3767 my $class = shift;
69 7         23 my ($file, $opts) = $class->parse_opts(@_);
70              
71 7         9 my @self;
72 7         57 tie @self, $class, $file, $opts;
73              
74 7         25 return \@self;
75              
76             }
77              
78             sub TIEARRAY {
79 21     21   16300 my $class = shift;
80 21         106 my ($file, $opts) = $class->parse_opts(@_);
81              
82 21         34 my @tiefile;
83 21 50       32 tie @tiefile, 'Tie::File', $file, recsep => "\n", %{ $opts->{tie_file} || {} }
  21 50       228  
84             or croak "Cannot tie file $file";
85              
86 21         2862 my $csv;
87 21 100 66     172 if (blessed $opts->{text_csv} and $opts->{text_csv}->isa('Text::CSV')) {
88 1         3 $csv = $opts->{text_csv};
89             } else {
90 20 50 100     1703 $csv = Text::CSV->new($opts->{text_csv} || {})
91             or croak "CSV (new) error: " . Text::CSV->error_diag();
92             }
93              
94 21         1915 my $self = {
95             file => \@tiefile,
96             csv => $csv,
97             };
98              
99 21         102 bless $self, $class;
100              
101 21         147 return $self;
102             }
103              
104             sub FETCH {
105 90     90   8080 my $self = shift;
106 90         111 my $index = shift;
107              
108 90         356 my $line = $self->{file}[$index];
109              
110 90         8523 my $rowclass = ref($self) . '::Row';
111 90         295 tie my @line, $rowclass, {
112             file => $self->{file},
113             line_num => $index,
114             fields => $self->_parse($line),
115             csv => $self->{csv},
116             };
117              
118 90         435 return \@line;
119             }
120              
121             sub STORE {
122 4     4   35 my $self = shift;
123 4         7 my ($index, $value) = @_;
124              
125 4         14 $self->{file}[$index] = $self->_combine($value);
126             }
127              
128             sub FETCHSIZE {
129 81     81   15324 my $self = shift;
130              
131 81         97 return scalar @{ $self->{file} };
  81         297  
132             }
133              
134             sub STORESIZE {
135 3     3   359 my $self = shift;
136 3         6 my $new_size = shift;
137              
138 3         6 $#{ $self->{file} } = $new_size - 1;
  3         21  
139            
140             }
141              
142             sub EXISTS {
143 2     2   419 my $self = shift;
144 2         5 my ($index) = shift;
145 2         10 return exists $self->{file}[$index];
146             }
147              
148             sub DELETE {
149 1     1   14 my $self = shift;
150 1         3 my $index = shift;
151 1         5 return $self->SPLICE($index,1);
152             }
153              
154             sub _parse {
155 91     91   116 my $self = shift;
156 91         115 my ($line) = @_;
157 91 100       238 $line = '' unless defined $line;
158              
159 91 50       361 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         9 my ($value) = @_;
167              
168 5 50       168 return $self->{csv}->string
    50          
169             if $self->{csv}->combine( ref $value ? @$value : ($value) );
170              
171 0         0 croak "CSV combine error: " . $self->{csv}->error_diag();
172             }
173              
174             package Tie::Array::CSV::Row;
175              
176 6     6   141 use Carp;
  6         11  
  6         676  
177              
178 6     6   47 use Tie::Array;
  6         10  
  6         465  
179             our @ISA = ('Tie::Array');
180              
181             use overload
182 6     6   49 '@{}' => sub{ $_[0]{fields} };
  6     0   11  
  6         88  
  0         0  
183              
184             sub TIEARRAY {
185 90     90   19625 my $class = shift;
186 90         109 my $self = shift;
187              
188 90         358 bless $self, $class;
189              
190 90         202 return $self;
191             }
192              
193             sub FETCH {
194 195     195   960 my $self = shift;
195 195         217 my $index = shift;
196              
197 195         704 return $self->{fields}[$index];
198             }
199              
200             sub STORE {
201 9     9   27 my $self = shift;
202 9         17 my ($index, $value) = @_;
203              
204 9         23 $self->{fields}[$index] = $value;
205              
206 9         29 $self->_update;
207              
208             }
209              
210             sub FETCHSIZE {
211 241     241   16494 my $self = shift;
212              
213 241         268 return scalar @{ $self->{fields} };
  241         935  
214             }
215              
216             sub STORESIZE {
217 1     1   5 my $self = shift;
218 1         6 my $new_size = shift;
219              
220 1         5 my $return = (
221 1         2 $#{ $self->{fields} } = $new_size - 1
222             );
223              
224 1         5 $self->_update;
225              
226 1         255 return $return;
227             }
228              
229             sub SHIFT {
230 1     1   2 my $self = shift;
231              
232 1         1 my $value = shift @{ $self->{fields} };
  1         4  
233              
234 1         4 $self->_update;
235              
236 1         314 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 11     11   18 my $self = shift;
269              
270 11 50       21 $self->{csv}->combine(@{ $self->{fields} })
  11         50  
271             or croak "CSV combine error: " . $self->{csv}->error_diag();
272 11         894 $self->{file}[$self->{line_num}] = $self->{csv}->string;
273             }
274              
275             __END__