File Coverage

blib/lib/Beam/Make/DBI/CSV.pm
Criterion Covered Total %
statement 50 50 100.0
branch n/a
condition n/a
subroutine 12 12 100.0
pod 2 2 100.0
total 64 64 100.0


line stmt bran cond sub pod time code
1             package Beam::Make::DBI::CSV;
2             our $VERSION = '0.001';
3             # ABSTRACT: A Beam::Make recipe
4              
5             #pod =head1 SYNOPSIS
6             #pod
7             #pod ### container.yml
8             #pod # A Beam::Wire container to configure a database connection to use
9             #pod sqlite:
10             #pod $class: DBI
11             #pod $method: connect
12             #pod $args:
13             #pod - dbi:SQLite:conversion.db
14             #pod
15             #pod ### Beamfile
16             #pod load_data:
17             #pod $class: Beam::Wire::DBI
18             #pod dbh: { $ref: 'container.yml:sqlite' }
19             #pod table: cpan_recent
20             #pod file: cpan_recent.csv
21             #pod
22             #pod =head1 DESCRIPTION
23             #pod
24             #pod This L<Beam::Make> recipe class loads data into a database from a CSV file.
25             #pod
26             #pod =head1 SEE ALSO
27             #pod
28             #pod L<Beam::Make>, L<Beam::Wire>, L<DBI>
29             #pod
30             #pod =cut
31              
32 1     1   19 use v5.20;
  1         4  
33 1     1   7 use warnings;
  1         3  
  1         35  
34 1     1   498 use autodie;
  1         14099  
  1         4  
35 1     1   7345 use Moo;
  1         3  
  1         10  
36 1     1   685 use Time::Piece;
  1         2  
  1         12  
37 1     1   964 use Text::CSV;
  1         14573  
  1         51  
38 1     1   10 use Digest::SHA qw( sha1_base64 );
  1         2  
  1         55  
39 1     1   7 use experimental qw( signatures postderef );
  1         3  
  1         12  
40 1     1   197 use Log::Any qw( $LOG );
  1         3  
  1         12  
41              
42             extends 'Beam::Make::Recipe';
43              
44             #pod =attr dbh
45             #pod
46             #pod Required. The L<DBI> database handle to use. Can be a reference to a service
47             #pod in a L<Beam::Wire> container using C<< { $ref: "<container>:<service>" } >>.
48             #pod
49             #pod =cut
50              
51             has dbh => ( is => 'ro', required => 1 );
52              
53             #pod =attr table
54             #pod
55             #pod Required. The table to load data to.
56             #pod
57             #pod =cut
58              
59             has table => ( is => 'ro', required => 1 );
60              
61             #pod =attr file
62             #pod
63             #pod Required. The path to the CSV file to load.
64             #pod
65             #pod =cut
66              
67             has file => ( is => 'ro', required => 1 );
68              
69             #pod =attr csv
70             #pod
71             #pod The configured L<Text::CSV> object to use. Can be a reference to a service
72             #pod in a L<Beam::Wire> container using C<< { $ref: "<container>:<service>" } >>.
73             #pod Defaults to a new, blank C<Text::CSV> object.
74             #pod
75             #pod ### container.yml
76             #pod # Configure a CSV parser for pipe-separated values
77             #pod psv:
78             #pod $class: Text::CSV
79             #pod $args:
80             #pod - binary: 1
81             #pod sep_char: '|'
82             #pod quote_char: ~
83             #pod escape_char: ~
84             #pod
85             #pod ### Beamfile
86             #pod # Load a PSV into the database
87             #pod load_psv:
88             #pod $class: Beam::Make::DBI::CSV
89             #pod dbh: { $ref: 'container.yml:sqlite' }
90             #pod csv: { $ref: 'container.yml:psv' }
91             #pod file: accounts.psv
92             #pod table: accounts
93             #pod
94             #pod =cut
95              
96             has csv => ( is => 'ro', default => sub { Text::CSV->new } );
97              
98 1     1 1 3 sub make( $self, %vars ) {
  1         2  
  1         2  
  1         3  
99 1         5 my $dbh = $self->dbh;
100 1         7 open my $fh, '<', $self->file;
101 1         2130 my $csv = $self->csv;
102 1         73 my @fields = $csv->getline( $fh )->@*;
103             my $sth = $dbh->prepare(
104             sprintf 'INSERT INTO %s ( %s ) VALUES ( %s )',
105             $dbh->quote_identifier( $self->table ),
106 1         86 join( ', ', map { $dbh->quote_identifier( $_ ) } @fields ),
  2         65  
107             join( ', ', ('?')x@fields ),
108             );
109 1         141 while ( my $row = $csv->getline( $fh ) ) {
110 2         24877 $sth->execute( @$row );
111             }
112 1         61 $self->cache->set( $self->name, $self->_cache_hash );
113 1         37 return 0;
114             }
115              
116 3     3   10 sub _cache_hash( $self ) {
  3         7  
  3         5  
117             my $content = join ';',
118 3         42 map { join ',', @$_ }
  4         372  
119             $self->dbh->selectall_arrayref( 'SELECT * FROM ' . $self->table )->@*;
120 3         237 return sha1_base64( $content );
121             }
122              
123 2     2 1 6 sub last_modified( $self ) {
  2         4  
  2         4  
124 2         17 return $self->cache->last_modified( $self->name, $self->_cache_hash );
125             }
126              
127             1;
128              
129             __END__
130              
131             =pod
132              
133             =head1 NAME
134              
135             Beam::Make::DBI::CSV - A Beam::Make recipe
136              
137             =head1 VERSION
138              
139             version 0.001
140              
141             =head1 SYNOPSIS
142              
143             ### container.yml
144             # A Beam::Wire container to configure a database connection to use
145             sqlite:
146             $class: DBI
147             $method: connect
148             $args:
149             - dbi:SQLite:conversion.db
150              
151             ### Beamfile
152             load_data:
153             $class: Beam::Wire::DBI
154             dbh: { $ref: 'container.yml:sqlite' }
155             table: cpan_recent
156             file: cpan_recent.csv
157              
158             =head1 DESCRIPTION
159              
160             This L<Beam::Make> recipe class loads data into a database from a CSV file.
161              
162             =head1 ATTRIBUTES
163              
164             =head2 dbh
165              
166             Required. The L<DBI> database handle to use. Can be a reference to a service
167             in a L<Beam::Wire> container using C<< { $ref: "<container>:<service>" } >>.
168              
169             =head2 table
170              
171             Required. The table to load data to.
172              
173             =head2 file
174              
175             Required. The path to the CSV file to load.
176              
177             =head2 csv
178              
179             The configured L<Text::CSV> object to use. Can be a reference to a service
180             in a L<Beam::Wire> container using C<< { $ref: "<container>:<service>" } >>.
181             Defaults to a new, blank C<Text::CSV> object.
182              
183             ### container.yml
184             # Configure a CSV parser for pipe-separated values
185             psv:
186             $class: Text::CSV
187             $args:
188             - binary: 1
189             sep_char: '|'
190             quote_char: ~
191             escape_char: ~
192              
193             ### Beamfile
194             # Load a PSV into the database
195             load_psv:
196             $class: Beam::Make::DBI::CSV
197             dbh: { $ref: 'container.yml:sqlite' }
198             csv: { $ref: 'container.yml:psv' }
199             file: accounts.psv
200             table: accounts
201              
202             =head1 SEE ALSO
203              
204             L<Beam::Make>, L<Beam::Wire>, L<DBI>
205              
206             =head1 AUTHOR
207              
208             Doug Bell <preaction@cpan.org>
209              
210             =head1 COPYRIGHT AND LICENSE
211              
212             This software is copyright (c) 2020 by Doug Bell.
213              
214             This is free software; you can redistribute it and/or modify it under
215             the same terms as the Perl 5 programming language system itself.
216              
217             =cut