File Coverage

blib/lib/ETL/Pipeline/Input/DelimitedText.pm
Criterion Covered Total %
statement 27 27 100.0
branch 1 2 50.0
condition n/a
subroutine 9 9 100.0
pod 2 3 66.6
total 39 41 95.1


line stmt bran cond sub pod time code
1             =pod
2              
3             =head1 NAME
4              
5             ETL::Pipeline::Input::DelimitedText - Input source for CSV, tab, or pipe
6             delimited files
7              
8             =head1 SYNOPSIS
9              
10             use ETL::Pipeline;
11             ETL::Pipeline->new( {
12             input => ['DelimitedText', matching => qr/\.csv$/i],
13             mapping => {First => 'Header1', Second => 'Header2'},
14             output => ['UnitTest']
15             } )->process;
16              
17             =head1 DESCRIPTION
18              
19             B<ETL::Pipeline::Input::DelimitedText> defines an input source for reading
20             CSV (comma seperated variable), tab delimited, or pipe delimited files. It
21             uses L<Text::CSV> for parsing.
22              
23             =cut
24              
25             package ETL::Pipeline::Input::DelimitedText;
26 1     1   4 use Moose;
  1         1  
  1         6  
27              
28 1     1   4651 use 5.014000;
  1         3  
29 1     1   3 use warnings;
  1         1  
  1         21  
30              
31 1     1   4 use Carp;
  1         0  
  1         56  
32 1     1   572 use Text::CSV;
  1         7566  
  1         5  
33              
34              
35             our $VERSION = '2.00';
36              
37              
38             =head1 METHODS & ATTRIBUTES
39              
40             =head2 Arguments for L<ETL::Pipeline/input>
41              
42             B<ETL::Pipeline::Input::DelimitedText> implements L<ETL::Pipeline::Input::File>
43             and L<ETL::Pipeline::Input::TabularFile>. It supports all of the attributes
44             from these roles.
45              
46             In addition, B<ETL::Pipeline::Input::DelimitedText> makes available all of the
47             options for L<Text::CSV>. See L<Text::CSV> for a list.
48              
49             # Pipe delimited, allowing embedded new lines.
50             $etl->input( 'DelimitedText',
51             matching => qr/\.dat$/i,
52             sep_char => '|',
53             binary => 1
54             );
55              
56             =cut
57              
58             sub BUILD {
59 1     1 0 1 my $self= shift;
60 1         1 my $arguments = shift;
61              
62 1         2 my %options;
63 1         4 while (my ($key, $value) = each %$arguments) {
64 2 50       35 $options{$key} = $value unless $self->meta->has_attribute( $key );
65             }
66              
67 1         22 $self->csv( Text::CSV->new( \%options ) );
68             }
69              
70              
71             =head2 Called from L<ETL::Pipeline/process>
72              
73             =head3 get
74              
75             B<get> retrieves one field from the current record. B<get> accepts one
76             parameter. That parameter can be an index number, a column name, or a regular
77             expression to match against column names.
78              
79             $etl->get( 0 );
80             $etl->get( 'First' );
81             $etl->get( qr/\bfirst\b/i );
82              
83             =cut
84              
85             sub get {
86             my ($self, $index) = @_;
87             return undef unless $index =~ m/^\d+$/;
88             return $self->_get_value( $index );
89             }
90              
91              
92             =head3 next_record
93              
94             Read one record from the file for processing. B<next_record> returns a boolean.
95             I<True> means success. I<False> means it reached the end of the file.
96              
97             while ($input->next_record) {
98             ...
99             }
100              
101             =cut
102              
103             sub next_record {
104             my ($self) = @_;
105              
106             my $fields = $self->csv->getline( $self->handle );
107             if (defined $fields) {
108             $self->record( $fields );
109             return 1;
110             } else {
111             return 0 if $self->csv->eof;
112             my ($code, $message, $position) = $self->csv->error_diag;
113             croak "Error $code: $message at character $position";
114             }
115             }
116              
117              
118             =head3 get_column_names
119              
120             B<get_column_names> reads the field names from the first row in the file.
121             L</get> can match field names using regular expressions.
122              
123             =cut
124              
125             sub get_column_names {
126 1     1 1 1 my ($self) = @_;
127            
128 1         4 $self->next_record;
129 1         34 $self->add_column( $_ ) foreach ($self->fields);
130             }
131              
132              
133             =head3 configure
134              
135             B<configure> opens the file for reading. It takes care of loading the column
136             names, if your file has them.
137              
138             =cut
139              
140             sub configure {
141             my ($self) = @_;
142              
143             $self->handle( $self->file->openr() );
144             die sprintf( 'Unable to open "%s" for reading', $self->file->stringify )
145             unless defined $self->handle;
146             }
147              
148              
149             =head3 finish
150              
151             B<finish> closes the file.
152              
153             =cut
154              
155 1     1 1 26 sub finish { close shift->handle; }
156              
157              
158             =head2 Other Methods & Attributes
159              
160             =head3 record
161              
162             B<ETL::Pipeline::Input::DelimitedText> stores each record as a list of fields.
163             The field name corresponds with the file order of the field, starting at zero.
164             This attribute holds the current record.
165              
166             =head3 fields
167              
168             Returns a list of fields from the current record. It dereferences L</record>.
169              
170             =head3 number_of_fields
171              
172             This method returns the number of fields in the current record.
173              
174             =cut
175              
176             has 'record' => (
177             handles => {
178             fields => 'elements',
179             _get_value => 'get',
180             number_of_fields => 'count',
181             },
182             is => 'rw',
183             isa => 'ArrayRef[Any]',
184             traits => [qw/Array/],
185             );
186              
187              
188             =head3 csv
189              
190             B<csv> holds a L<Text::CSV> object for reading the file. You can set options
191             for L<Text::CSV> in the L<ETL::Pipeline/input> command.
192              
193             =cut
194              
195             has 'csv' => (
196             is => 'rw',
197             isa => 'Text::CSV',
198             );
199              
200              
201             =head3 handle
202              
203             The Perl file handle for reading data. L<Text::CSV> operates on a handle.
204             L</next_record> needs the handle.
205              
206             =cut
207              
208             has 'handle' => (
209             is => 'rw',
210             isa => 'Maybe[FileHandle]',
211             );
212              
213              
214             =head1 SEE ALSO
215              
216             L<ETL::Pipeline>, L<ETL::Pipeline::Input>, L<ETL::Pipeline::Input::File>,
217             L<ETL::Pipeline::Input::Tabular>
218              
219             =cut
220              
221             with 'ETL::Pipeline::Input::File';
222             with 'ETL::Pipeline::Input::Tabular';
223             with 'ETL::Pipeline::Input';
224              
225              
226             =head1 AUTHOR
227              
228             Robert Wohlfarth <robert.j.wohlfarth@vanderbilt.edu>
229              
230             =head1 LICENSE
231              
232             Copyright 2016 (c) Vanderbilt University Medical Center
233              
234             This program is free software; you can redistribute it and/or modify it under
235             the same terms as Perl itself.
236              
237             =cut
238              
239 1     1   364 no Moose;
  1         2  
  1         6  
240             __PACKAGE__->meta->make_immutable;