File Coverage

blib/lib/Data/Toolkit/Connector/CSV.pm
Criterion Covered Total %
statement 97 101 96.0
branch 26 52 50.0
condition n/a
subroutine 17 17 100.0
pod 9 9 100.0
total 149 179 83.2


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2             #
3             # Data::Toolkit::Connector::CSV
4             #
5             # Andrew Findlay
6             # Nov 2006
7             # andrew.findlay@skills-1st.co.uk
8             #
9             # $Id: CSV.pm 388 2013-08-30 15:19:23Z remotesvn $
10              
11             package Data::Toolkit::Connector::CSV;
12              
13 1     1   700 use strict;
  1         2  
  1         39  
14 1     1   5 use Carp;
  1         2  
  1         63  
15 1     1   5 use Clone qw(clone);
  1         1  
  1         39  
16 1     1   13 use Data::Dumper;
  1         2  
  1         41  
17              
18 1     1   612 use Data::Toolkit::Entry;
  1         3  
  1         28  
19 1     1   7 use Data::Toolkit::Connector;
  1         2  
  1         46  
20             our @ISA = ("Data::Toolkit::Connector");
21              
22             =head1 NAME
23              
24             Data::Toolkit::Connector::CSV
25              
26             =head1 DESCRIPTION
27              
28             Connector for reading CSV files.
29              
30             =head1 SYNOPSIS
31              
32             $conn2 = Data::Toolkit::Connector::CSV->new();
33             open SOURCE, "
34             my $csvParser = Text::CSV_XS->new();
35             $conn2->parser( $csvParser );
36             $conn2->datasource( sub { return } );
37             $conn2->columns( ['one','two','three'] );
38             while ($entry = $conn2->next()) {
39             print $entry->dump(), "\n";
40             }
41              
42              
43              
44             =head1 DEPENDENCIES
45              
46             Carp
47             Clone
48             Text::CSV_XS for testing
49              
50             =cut
51              
52             ########################################################################
53             # Package globals
54             ########################################################################
55              
56 1     1   6 use vars qw($VERSION);
  1         1  
  1         1039  
57             $VERSION = '1.0';
58              
59             # Set this non-zero for debug logging
60             #
61             my $debug = 0;
62              
63             ########################################################################
64             # Constructors and destructors
65             ########################################################################
66              
67             =head1 Constructor
68              
69             =head2 new
70              
71             my $csvConn = Data::Toolkit::Connector::CSV->new();
72              
73             Creates an object of type Data::Toolkit::Connector::CSV
74              
75             =cut
76              
77             sub new {
78 1     1 1 643 my $class = shift;
79              
80 1         10 my $self = $class->SUPER::new(@_);
81 1         3 bless ($self, $class);
82              
83 1         8 $self->{linecount} = 0;
84              
85 1 50       4 carp "Data::Toolkit::Connector::CSV->new $self" if $debug;
86 1         3 return $self;
87             }
88              
89             sub DESTROY {
90 1     1   3 my $self = shift;
91 1 50       34 carp "Data::Toolkit::Connector::CSV Destroying $self" if $debug;
92             }
93              
94             ########################################################################
95             # Methods
96             ########################################################################
97              
98             =head1 Methods
99              
100             =cut
101              
102             ########################################
103              
104             =head2 parser
105              
106             Define the CSV parser for the connector to use.
107             This should be an object of type Text::CSV_XS or similar.
108              
109             my $res = $csvConn->parser( Text::CSV_XS->new() );
110              
111             Returns the object that it is passed.
112              
113             =cut
114              
115             sub parser {
116 1     1 1 858 my $self = shift;
117 1         3 my $parser = shift;
118              
119 1 50       4 croak "Data::Toolkit::Connector::CSV->parser expects a parameter" if !$parser;
120 1 50       3 carp "Data::Toolkit::Connector::CSV->parser $self" if $debug;
121              
122 1         6 return $self->{parser} = $parser;
123             }
124              
125             ########################################
126              
127             =head2 datasource
128              
129             Specify a data source.
130             This should be a reference to a procedure that returns one line of text per call.
131              
132             If the parameter is undef, data will be read using the magic "<>" token
133              
134             my $res = $csvConn->datasource( sub { return ; } );
135              
136             Returns the object that it is passed.
137              
138             =cut
139              
140             sub datasource {
141 2     2 1 6 my $self = shift;
142 2         3 my $source = shift;
143              
144 2 50       46 carp "Data::Toolkit::Connector::CSV->datasource $self" if $debug;
145              
146             # Reset the linecount as we have a new datasource
147 2         4 $self->{linecount} = 0;
148 2         5 $self->{currentline} = undef;
149              
150 2         9 return $self->{datasource} = $source;
151             }
152              
153             ########################################
154              
155             =head2 columns
156              
157             Specify the names of the columns in the CSV file
158              
159             my $arrayRef = $csvConn->columns( ['firstname','surname','mail','phone'] );
160             my $arrayRef = $csvConn->columns();
161              
162             Returns the list of columns as an array or array reference
163              
164             =cut
165              
166             sub columns {
167 1     1 1 3 my $self = shift;
168 1         2 my $cols = shift;
169              
170 1 50       5 if ($cols) {
171 1 50       5 croak "Data::Toolkit::Connector::CSV->columns expects an array reference" if ((ref $cols) ne 'ARRAY');
172 1 50       4 carp "Data::Toolkit::Connector::CSV->columns $self $cols" if $debug;
173 1         14 $self->{config}->{cols} = clone( $cols );
174             }
175             else {
176 0 0       0 carp "Data::Toolkit::Connector::CSV->columns $self" if $debug;
177             }
178              
179 1         4 my @colReturn = $self->{config}->{cols};
180              
181 1 50       6 return wantarray ? @colReturn : \@colReturn;
182             }
183              
184             ########################################
185              
186             =head2 colsFromFile
187              
188             Read the column names from the first line of the CSV file
189              
190             my $entry = $csvConn->colsFromFile();
191              
192             Returns the list of columns on success, or undef on failure.
193              
194             It is an error to call this method without first calling the
195             datasource and parser methods. Doing so will cause an exception
196             to be thrown.
197              
198             =cut
199              
200             sub colsFromFile {
201 1     1 1 2 my $self = shift;
202              
203 1 50       4 carp "Data::Toolkit::Connector::CSV->colsFromFile $self" if $debug;
204              
205 1 50       5 croak "colsFromFile called but no parser yet defined" if !$self->{parser};
206              
207 1         2 my $line;
208             # Use the datasource procedure if we have one
209             # Otherwise, read from the 'magic open' file
210             #
211 1 50       5 if (defined($self->{datasource})) {
212 1         4 $line = $self->{datasource}();
213             }
214             else {
215 0         0 $line = <>;
216             }
217 1 50       30 return undef if !defined($line);
218 1         3 chomp $line;
219              
220             # Count lines and stash the current one for reference
221 1         2 $self->{linecount}++;
222 1         3 $self->{currentline} = $line;
223              
224             # Parse the line to find the field names
225 1         5 my $status = $self->{parser}->parse( $line );
226 1 50       21 return undef if not $status;
227              
228 1         4 my @fields = $self->{parser}->fields();
229 1         10 $self->{config}->{cols} = \@fields;
230              
231 1 50       11 carp "Data::Toolkit::Connector::CSV->colsFromFile returning $line" if $debug;
232              
233 1 50       9 return wantarray ? @fields : \@fields;
234             }
235              
236              
237             ########################################
238              
239             =head2 next
240              
241             Read the next entry from the CSV file
242              
243             my $entry = $csvConn->next();
244              
245             The result is a Data::Toolkit::Entry object if there is data left in the file,
246             otherwise it is undef.
247              
248             =cut
249              
250             sub next {
251 5     5 1 278 my $self = shift;
252              
253 5 50       14 carp "Data::Toolkit::Connector::CSV->next $self" if $debug;
254              
255             # print "####" . Dumper($self) . "\n";
256              
257 5         6 my $line;
258             # Use the datasource procedure if we have one
259             # Otherwise, read from the 'magic open' file
260             #
261 5 50       13 if (defined($self->{datasource})) {
262 5         13 $line = $self->{datasource}();
263             }
264             else {
265 0         0 $line = <>;
266             }
267 5 100       56 return undef if !defined($line);
268 4         10 chomp $line;
269              
270             # Count lines and stash the current one for reference
271 4         6 $self->{linecount}++;
272 4         9 $self->{currentline} = $line;
273              
274 4         18 my $status = $self->{parser}->parse( $line );
275 4 50       183 return undef if not $status;
276              
277 4         17 my @fields = $self->{parser}->fields();
278              
279 4         52 my $entry = Data::Toolkit::Entry->new();
280              
281             # Now step through the list of columns and assign data to attributes in the entry
282 4         5 my $colname;
283 4         5 my $col = 0;
284 4         8 my $names = $self->{config}->{cols};
285              
286 4         9 foreach $colname (@$names) {
287 13         57 $entry->set( $colname, [ $fields[$col] ] );
288 13         195 $col++;
289             }
290              
291 4 50       20 carp "Data::Toolkit::Connector::CSV->next returning data $line" if $debug;
292              
293 4         16 return $entry;
294             }
295              
296              
297             ########################################
298              
299             =head2 linecount
300              
301             Return the number of the line that we are currently processing
302              
303             $count = $csvConn->linecount();
304              
305             =cut
306              
307             sub linecount {
308 1     1 1 3 my $self = shift;
309              
310 1         2 my $count = $self->{linecount};
311 1 50       4 carp "Data::Toolkit::Connector::CSV->linecount $self returns $count" if $debug;
312              
313 1         7 return $count;
314             }
315              
316             ########################################
317              
318             =head2 currentline
319              
320             Return the line that we are currently processing
321              
322             $count = $csvConn->currentline();
323              
324             =cut
325              
326             sub currentline {
327 1     1 1 2 my $self = shift;
328              
329 1         3 my $line = $self->{currentline};
330 1 50       4 carp "Data::Toolkit::Connector::CSV->currentline $self returns '$line'" if $debug;
331              
332 1         7 return $line;
333             }
334              
335             ########################################################################
336             # Debugging methods
337             ########################################################################
338              
339             =head1 Debugging methods
340              
341             =head2 debug
342              
343             Set and/or get the debug level for Data::Toolkit::Connector
344              
345             my $currentDebugLevel = Data::Toolkit::Connector::CSV->debug();
346             my $newDebugLevel = Data::Toolkit::Connector::CSV->debug(1);
347              
348             Any non-zero debug level causes the module to print copious debugging information.
349              
350             Note that this is a package method, not an object method. It should always be
351             called exactly as shown above.
352              
353             All debug information is reported using "carp" from the Carp module, so if
354             you want a full stack backtrace included you can run your program like this:
355              
356             perl -MCarp=verbose myProg
357              
358             =cut
359              
360             # Class method to set and/or get debug level
361             #
362             sub debug {
363 1     1 1 31 my $class = shift;
364 1 50       5 if (ref $class) { croak "Class method 'debug' called as object method" }
  0         0  
365             # print "DEBUG: ", (join '/', @_), "\n";
366 1 50       6 $debug = shift if (@_ == 1);
367 1         11 return $debug
368             }
369              
370              
371             ########################################################################
372             ########################################################################
373              
374             =head1 Author
375              
376             Andrew Findlay
377              
378             Skills 1st Ltd
379              
380             andrew.findlay@skills-1st.co.uk
381              
382             http://www.skills-1st.co.uk/
383              
384             =cut
385              
386             ########################################################################
387             ########################################################################
388             1;