File Coverage

blib/lib/App/csv2sqlite.pm
Criterion Covered Total %
statement 48 53 90.5
branch 4 8 50.0
condition 2 7 28.5
subroutine 13 15 86.6
pod 0 6 0.0
total 67 89 75.2


line stmt bran cond sub pod time code
1             # vim: set ts=2 sts=2 sw=2 expandtab smarttab:
2             #
3             # This file is part of App-csv2sqlite
4             #
5             # This software is copyright (c) 2012 by Randy Stauner.
6             #
7             # This is free software; you can redistribute it and/or modify it under
8             # the same terms as the Perl 5 programming language system itself.
9             #
10 2     2   155170 use strict;
  2         4  
  2         70  
11 2     2   12 use warnings;
  2         3  
  2         118  
12              
13             package App::csv2sqlite;
14             {
15             $App::csv2sqlite::VERSION = '0.004';
16             }
17             # git description: v0.003-3-g85d53f9
18              
19             BEGIN {
20 2     2   36 $App::csv2sqlite::AUTHORITY = 'cpan:RWSTAUNER';
21             }
22             # ABSTRACT: Import CSV files into a SQLite database
23              
24 2     2   4682 use Moo 1;
  2         45968  
  2         14  
25              
26 2     2   9209 use DBI 1.6 ();
  2         50727  
  2         101  
27 2     2   2658 use DBD::SQLite 1 ();
  2         20015  
  2         61  
28 2     2   1408 use DBIx::TableLoader::CSV 1.102 (); # catch csv errors and close transactions; file_encoding
  2         57687  
  2         77  
29 2     2   2684 use Getopt::Long 2.34 ();
  2         37926  
  2         1537  
30              
31             sub new_from_argv {
32 7     7 0 25293 my ($class, $args) = @_;
33 7         40 $class->new( $class->getopt($args) );
34             }
35              
36             around BUILDARGS => sub {
37             my ($orig, $self, @args) = @_;
38             my $args = $self->$orig(@args);
39              
40             if( my $enc = delete $args->{encoding} ){
41             ($args->{loader_options} ||= {})->{file_encoding} ||= $enc;
42             }
43              
44             return $args;
45             };
46              
47             has csv_files => (
48             is => 'ro',
49             coerce => sub { ref $_[0] eq 'ARRAY' ? $_[0] : [ $_[0] ] },
50             );
51              
52             has csv_options => (
53             is => 'ro',
54             default => sub { +{} },
55             );
56              
57             has loader_options => (
58             is => 'ro',
59             default => sub { +{} },
60             );
61              
62             has dbname => (
63             is => 'ro',
64             );
65              
66             has dbh => (
67             is => 'lazy',
68             );
69              
70             sub _build_dbh {
71 14     14   4187 my ($self) = @_;
72             # TODO: does the dbname need to be escaped in some way?
73 14 100       129 my $dbh = DBI->connect('dbi:SQLite:dbname=' . $self->dbname, undef, undef, {
74             RaiseError => 1,
75             PrintError => 0,
76             sqlite_unicode => $self->encoding ? 1 : 0,
77             });
78 14         12419 return $dbh;
79             }
80              
81             sub encoding {
82 16     16 0 3567 return $_[0]->loader_options->{file_encoding};
83             }
84              
85 0     0 0 0 sub help { Getopt::Long::HelpMessage(2); }
86              
87              
88             sub getopt {
89 7     7 0 20 my ($class, $args) = @_;
90 7         16 my $opts = {};
91              
92             {
93 7         14 local @ARGV = @$args;
  7         30  
94 7         97 my $p = Getopt::Long::Parser->new(
95             config => [qw(pass_through auto_help auto_version)],
96             );
97 7 50       801 $p->getoptions($opts,
98             'csv_files|csv-file|csvfile|csv=s@',
99             # TODO: 'named_csv_files=s%'
100             # or maybe --csv and --named should be subs that append to an array ref to keep order?
101             'csv_options|csv-opt|csvopt|o=s%',
102             # TODO: tableloader options like 'drop' or maybe --no-create
103             'loader_options|loader-opt|loaderopt|l=s%',
104             'dbname|database=s',
105             'encoding|enc|e=s',
106             ) or $class->help;
107 7         7955 $args = [@ARGV];
108             }
109              
110             # last arguments
111 7   33     57 $opts->{dbname} ||= pop @$args;
112              
113             # first argument
114 7 50       26 if( @$args ){
115 7   50     12 push @{ $opts->{csv_files} ||= [] }, @$args;
  7         210  
116             }
117              
118 7         1041 return $opts;
119             }
120              
121             sub load_tables {
122 7     7 0 18899 my ($self) = @_;
123              
124             # TODO: option for wrapping the whole loop in a transaction rather than each table
125              
126 7         15 foreach my $file ( @{ $self->csv_files } ){
  7         37  
127 8         97 my %opts = (
128 8         80 %{ $self->loader_options },
129 8         27559 csv_opts => { %{ $self->csv_options } },
130             file => $file,
131             );
132              
133             # TODO: This could work but i hate the escaping thing.
134             # Allow table=file (use "=file" for files with an equal sign).
135             #if( $file =~ /^([^=:]*)[=:](.+)$/ ){ $opts{name} = $1 if $1; $opts{file} = $2; }
136              
137 8         229 DBIx::TableLoader::CSV->new(
138             %opts,
139             dbh => $self->dbh,
140             )->load;
141             }
142              
143 6         553988 return;
144             }
145              
146             sub run {
147 0   0 0 0   my $class = shift || __PACKAGE__;
148 0 0         my $args = @_ ? shift : [@ARGV];
149              
150 0           my $self = $class->new_from_argv($args);
151 0           $self->load_tables;
152             }
153              
154             1;
155              
156             __END__