File Coverage

blib/lib/DataFlow/Proc/DBF.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package DataFlow::Proc::DBF;
2              
3 2     2   66773 use warnings;
  2         6  
  2         74  
4 2     2   11 use strict;
  2         4  
  2         107  
5              
6             our $VERSION = '0.03';
7              
8 2     2   2269 use Moose;
  0            
  0            
9             use MooseX::Aliases;
10             extends 'DataFlow::Proc::Converter';
11              
12             use XBase;
13             use File::Temp qw(:seekable);
14             use File::Spec ();
15             use autodie;
16             use namespace::autoclean;
17              
18             has '+converter' => (
19             lazy => 1,
20             default => sub { return XBase->new },
21             handles => {
22             dbf => sub { shift->converter(@_) },
23             dbf_opts => sub { shift->converter_opts(@_) },
24             has_dbf_opts => sub { shift->has_converter_opts },
25             },
26             init_arg => 'dbf',
27             );
28              
29             has '+converter_opts' => ( 'init_arg' => 'dbf_opts', );
30              
31             has 'header' => (
32             'is' => 'rw',
33             'isa' => 'ArrayRef[Maybe[Str]]',
34             'predicate' => 'has_header',
35             'alias' => 'headers',
36             'handles' => { 'has_headers' => sub { shift->has_header }, },
37             );
38              
39              
40            
41             has 'header_wanted' => (
42             'is' => 'rw',
43             'isa' => 'Bool',
44             'lazy' => 1,
45             'default' => sub {
46             my $self = shift;
47             return 0 if $self->direction eq 'CONVERT_FROM';
48             return $self->has_header;
49             },
50             );
51              
52              
53             sub _policy {
54             return shift->direction eq 'CONVERT_TO' ? 'ArrayRef' : 'Scalar';
55             }
56              
57             sub _build_subs {
58             my $self = shift;
59             return {
60             'CONVERT_TO' => sub {
61             my $data = $_;
62             my $options = $self->has_converter_opts
63             ? $self->converter_opts : {}
64             ;
65              
66             unless (exists $options->{'name'}) {
67             $options->{'dir'} = File::Temp->newdir( CLEANUP => 1 );
68             $options->{'name'} = File::Spec->catfile($options->{'dir'}, 'tmp.dbf');
69             }
70              
71             # header is mandatory, so we either
72             # use one provided by the user,
73             # or create our own "fake" version
74             my $field_names;
75             if ($self->header_wanted) {
76             $self->header_wanted(0);
77             $field_names = $self->header;
78             }
79             else {
80             push @$field_names, "item$_"
81             foreach ( 0 .. $#{ $data->[0] } );
82             }
83              
84             my $table = $self->converter->create(
85             name => $options->{'name'},
86             field_names => $field_names,
87             field_types => [],
88             field_lengths => [],
89             field_decimals => [],
90             ) or die 'error creating DBF: ' . $self->converter->errstr;
91              
92             foreach my $i ( 0 .. $#{$data} ) {
93             $table->set_record($i, @{ $data->[$i] } );
94             }
95              
96             $table->close;
97              
98             # temporary DBF file saved. Get the content back
99             open my $fh, '<', $options->{'name'};
100             binmode $fh;
101             my $content = do { local $/; <$fh> };
102             return $content;
103             },
104              
105             'CONVERT_FROM' => sub {
106             my $string = $_;
107             my $options = $self->has_converter_opts
108             ? $self->converter_opts : {}
109             ;
110              
111             my $dbf;
112              
113             # if the user passes a file name or handle
114             # to read from, we use it. Otherwise, we
115             # assume the DBF is in a binary string
116             # (the "flow") and make our interface with
117             # XBase using a temp file
118             my $fh;
119             unless (exists $options->{'name'} or exists $options->{'fh'}) {
120             $fh = File::Temp->new( UNLINK => 1 );
121             binmode $fh;
122             print $fh $string;
123             close $fh;
124              
125             $options->{name} = $fh->filename;
126             }
127              
128             $dbf = $self->converter;
129             $dbf->open( %$options )
130             or die XBase->errstr;
131              
132             my $records = $dbf->get_all_records;
133              
134             if ($self->header_wanted) {
135             $self->header_wanted(0);
136             $self->header( [$dbf->field_names] );
137             }
138              
139             $dbf->close;
140             return $records;
141             },
142             };
143             }
144              
145             __PACKAGE__->meta->make_immutable;
146              
147             42;
148             __END__
149              
150             =head1 NAME
151              
152             DataFlow::Proc::DBF - A dBase DBF converting processor
153              
154              
155             =head1 SYNOPSIS
156              
157             use DataFlow;
158              
159             # creating our flow
160             my $flow = DataFlow->new([
161             [ 'DBF' => { direction => 'CONVERT_FROM' } ],
162             ]);
163              
164              
165             # getting back a perl arrayref
166             my $perl_struct = $flow->process( $slurped_dbf_data );
167              
168              
169            
170             =head1 DESCRIPTION
171              
172             This module provides a processing step for dBase (DBF) files under
173             L<DataFlow>. It lets you C<CONVERT_FROM> a DBF file into a Perl
174             data structure (in this case, an array reference) and C<CONVERT_TO>
175             a DBF stream, from a Perl array reference (the stream can be saved
176             into a file for later inspection with dBase).
177              
178             =head1 OPTIONS
179              
180             =head2 direction
181              
182             =over 4
183              
184             =item * CONVERT_FROM
185              
186             Converts FROM a DBF stream or file into a Perl array reference.
187              
188             =item * CONVERT_TO
189              
190             Converts TO a a DBF stream or file, from a Perl array reference.
191              
192             =back
193              
194             =head2 header_wanted
195              
196             Saves the header of the structure into C<< $proc->header >>. You can
197             reach it via C<< $flow->procs->[ $i ]->header >>, where C<$i> is the
198             index of the DBF processor in your flow. For example:
199              
200             my $flow = DataFlow->new([
201             [ 'DBF' => { direction => 'CONVERT_FROM', header_wanted => 1 } ],
202             ]);
203              
204             my $perl_data = $flow->process( $dbf_data );
205             my $header = $flow->procs->[0]->header;
206              
207             =head2 dbf_opts
208              
209             my $flow = DataFlow->new([
210             [ 'DBF' => {
211             direction => 'CONVERT_FROM',
212             dbf_opts => { name => 'dbase.dbf' },
213             }
214             ],
215             ]);
216              
217             Sets any particular option you may want to pass to L<XBase>. The most
218             important one being B<name>, in which you can specify a file name
219             for either input (CONVERT_FROM) or output (CONVERT_TO).
220              
221              
222             =head1 DIAGNOSTICS
223              
224             =over 4
225              
226             =item C<< Error creating DBF: $MESSAGE >>
227              
228             The conversor was unable to create a DBF file with the provided structure.
229             Make sure you pass a matrix with lines containing the same number of elements.
230              
231             =back
232              
233              
234             =head1 CONFIGURATION AND ENVIRONMENT
235              
236             DataFlow::Proc::DBF requires no configuration files or environment variables.
237              
238              
239             =head1 BUGS AND LIMITATIONS
240              
241             Please report any bugs or feature requests to
242             C<bug-dataflow-proc-dbf@rt.cpan.org>, or through the web interface at
243             L<http://rt.cpan.org>.
244              
245              
246             =head1 SEE ALSO
247              
248             L<DataFlow>, L<XBase>
249              
250             =head1 AUTHOR
251              
252             Breno G. de Oliveira C<< <garu@cpan.org> >>
253              
254              
255             =head1 LICENCE AND COPYRIGHT
256              
257             Copyright (c) 2011, Breno G. de Oliveira C<< <garu@cpan.org> >>. All rights reserved.
258              
259             This module is free software; you can redistribute it and/or
260             modify it under the same terms as Perl itself. See L<perlartistic>.
261              
262              
263             =head1 DISCLAIMER OF WARRANTY
264              
265             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
266             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
267             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
268             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
269             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
270             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
271             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
272             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
273             NECESSARY SERVICING, REPAIR, OR CORRECTION.
274              
275             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
276             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
277             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
278             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
279             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
280             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
281             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
282             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
283             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
284             SUCH DAMAGES.