File Coverage

blib/lib/ETL/Yertl/Format/csv.pm
Criterion Covered Total %
statement 61 61 100.0
branch 9 12 75.0
condition 6 9 66.6
subroutine 10 10 100.0
pod 3 4 75.0
total 89 96 92.7


line stmt bran cond sub pod time code
1             package ETL::Yertl::Format::csv;
2             our $VERSION = '0.036';
3             # ABSTRACT: CSV read/write support for Yertl
4              
5 1     1   83335 use ETL::Yertl;
  1         2  
  1         5  
6 1     1   29 use base 'ETL::Yertl::Format';
  1         2  
  1         262  
7 1     1   7 use Module::Runtime qw( use_module );
  1         1  
  1         6  
8 1     1   302 use ETL::Yertl::Util qw( pairs );
  1         3  
  1         601  
9              
10             sub new {
11 11     11 0 55934 my ( $class, %args ) = @_;
12 11   100     47 $args{delimiter} ||= ',';
13 11         39 return $class->SUPER::new( %args );
14             }
15              
16             #pod =attr format_module
17             #pod
18             #pod The module being used for this format. Possible modules, in order of importance:
19             #pod
20             #pod =over 4
21             #pod
22             #pod =item L (any version)
23             #pod
24             #pod =item L (any version)
25             #pod
26             #pod =back
27             #pod
28             #pod =cut
29              
30             # Pairs of module => supported version
31             our @FORMAT_MODULES = (
32             'Text::CSV_XS' => 0,
33             'Text::CSV' => 0,
34             );
35              
36             sub format_module {
37 11     11 1 15 my ( $self ) = @_;
38 11 50       22 return $self->{_format_module} if $self->{_format_module};
39 11         27 for my $format_module ( pairs @FORMAT_MODULES ) {
40 13         19 eval {
41             # Prototypes on use_module() make @$format_module not work correctly
42 13         36 use_module( $format_module->[0], $format_module->[1] );
43             };
44 13 100       1014 if ( !$@ ) {
45 10         58 return $format_module->[0];
46             }
47             }
48             die "Could not load a formatter for CSV. Please install one of the following modules:\n"
49             . join( "",
50 1 100       6 map { sprintf "\t%s (%s)", $_->[0], $_->[1] ? "version $_->[1]" : "Any version" }
  3         20  
51             pairs @FORMAT_MODULES
52             )
53             . "\n";
54             }
55              
56             sub _field_names {
57 20     20   33 my ( $self, $new_names ) = @_;
58 20 100       39 if ( $new_names ) {
59 10         15 $self->{_field_names} = $new_names;
60             }
61 20   100     59 return $self->{_field_names} || [];
62             }
63              
64             sub _csv {
65 10     10   15 my ( $self ) = @_;
66             return $self->{_csv} ||= $self->format_module->new({
67             binary => 1, eol => $\,
68             sep_char => $self->{delimiter},
69 10   33     29 });
70             }
71              
72             #pod =method write( DOCUMENTS )
73             #pod
74             #pod Convert the given C to CSV. Returns a CSV string.
75             #pod
76             #pod =cut
77              
78             sub write {
79 5     5 1 26 my ( $self, @docs ) = @_;
80 5         10 my $csv = $self->_csv;
81 5         538 my $str = '';
82 5         29 my @names = @{ $self->_field_names };
  5         15  
83              
84 5 50       16 if ( !@names ) {
85 5         7 @names = sort keys %{ $docs[0] };
  5         22  
86 5         17 $csv->combine( @names );
87 5         115 $str .= $csv->string . $/;
88 5         40 $self->_field_names( \@names );
89             }
90              
91 5         10 for my $doc ( @docs ) {
92 10         39 $csv->combine( map { $doc->{ $_ } } @names );
  30         55  
93 10         104 $str .= $csv->string . $/;
94             }
95              
96 5         44 return $str;
97             }
98              
99             #pod =method read()
100             #pod
101             #pod Read a CSV string from L and return all the documents.
102             #pod
103             #pod =cut
104              
105             sub read {
106 5     5 1 23 my ( $self ) = @_;
107 5   50     19 my $fh = $self->{input} || die "No input filehandle";
108 5         10 my $csv = $self->_csv;
109 5         599 my @names = @{ $self->_field_names };
  5         14  
110              
111 5 50       10 if ( !@names ) {
112 5         7 @names = @{ $csv->getline( $fh ) };
  5         169  
113 5         238 $self->_field_names( \@names );
114             }
115              
116 5         7 my @docs;
117 5         91 while ( my $row = $csv->getline( $fh ) ) {
118 10         207 push @docs, { map {; $names[ $_ ] => $row->[ $_ ] } 0..$#{ $row } };
  30         212  
  10         19  
119             }
120              
121 5         129 return @docs;
122             }
123              
124             1;
125              
126             __END__