File Coverage

lib/SweetPea/Cli/Data.pm
Criterion Covered Total %
statement 42 166 25.3
branch 0 40 0.0
condition 0 9 0.0
subroutine 14 21 66.6
pod 0 4 0.0
total 56 240 23.3


line stmt bran cond sub pod time code
1             package SweetPea::Cli::Data;
2              
3 1     1   9 use warnings;
  1         2  
  1         40  
4 1     1   6 use strict;
  1         2  
  1         35  
5              
6 1     1   6 use Cwd;
  1         3  
  1         91  
7 1     1   15346 use DBI;
  1         31485  
  1         79  
8 1     1   9352 use Exception::Handler;
  1         1021  
  1         53  
9 1     1   2276 use Hash::Merge::Simple qw(merge);
  1         1647  
  1         252  
10 1     1   3076 use SQL::Translator;
  1         450215  
  1         35  
11 1     1   9 use SQL::Translator::Schema::Field;
  1         2  
  1         28  
12 1     1   6 use SweetPea::Application;
  1         1  
  1         49  
13 1     1   3639 use SweetPea::Application::Config;
  1         52309  
  1         51  
14 1     1   12 use SweetPea::Cli::Util;
  1         3  
  1         30  
15 1     1   6 use SweetPea::Cli::Flash;
  1         4  
  1         28  
16 1     1   6 use SweetPea::Cli::Error;
  1         3  
  1         24  
17 1     1   8 use SweetPea::Cli::Help;
  1         2  
  1         4971  
18              
19             # SweetPea::Cli::Data - Data profile builder for use with SweetPea-Cli
20              
21             sub new {
22 0     0 0   my $class = shift;
23 0           my $self = {};
24 0           bless $self, $class;
25            
26 0           my $c = $self->{c} = shift;
27 0           my $f = SweetPea::Cli::Flash->new;
28 0           my $e = SweetPea::Cli::Error->new;
29            
30             $self->{commands} = [
31             {
32             name => 'data',
33             code => sub {
34 0     0     $self->data(@_)
35             },
36 0           args => {
37             'create' => {
38             aliases => ['c']
39             },
40             'update' => {
41             aliases => ['u']
42             }
43             },
44             help => 'create and update database data profiles.'
45             }
46             ];
47            
48 0           return $self;
49             }
50              
51             sub data {
52 0     0 0   my $self = shift;
53 0           my $c = shift;
54 0           my $f = SweetPea::Cli::Flash->new;
55 0           my $e = SweetPea::Cli::Error->new;
56 0           my $u = SweetPea::Cli::Util->new;
57 0           my $h = SweetPea::Cli::Help->new;
58            
59             # check sweetpea-application availability
60 0           eval 'use SweetPea::Application;';
61 0 0         if ($@) {
62 0           my @error = (
63             'Error',
64             'SweetPea::Application does not seem to be available.',
65             '',
66             'Please install SweetPea::Application. Try cpan SweetPea::Application.'
67             );
68 0           return $e->error(@error)->report($c);
69             }
70             else {
71 0 0 0       if
72             (
73             -x -r -d './sweet/configuration/datastores/development' &&
74             -x -r -d './sweet/configuration/datastores/production'
75             )
76             {
77             # create or update database data profiles
78 0 0         if ($c->options->{create}) {
    0          
79 0           $self->create($c);
80             }
81             elsif ($c->options->{update}) {
82 0           $self->update($c);
83             }
84             else {
85 0           return $h->display('data', $c);
86             }
87             }
88             else {
89 0           my @error = (
90             'Please create database data files before attempting an update.',
91             'Use `help data;` for instructions.'
92             );
93 0           return $e->error(@error)->report($c);
94             }
95             }
96             }
97              
98             sub create {
99 0     0 0   my $self = shift;
100 0           my $c = shift;
101 0           my $f = SweetPea::Cli::Flash->new;
102 0           my $e = SweetPea::Cli::Error->new;
103 0           my $u = SweetPea::Cli::Util->new;
104 0           my $h = SweetPea::Cli::Help->new;
105 0           my $s = SweetPea::Application::Config->new(
106             SweetPea::Application->new,
107             Cwd::getcwd
108             );
109            
110             # check if profiles exist
111 0           my $datastore = $s->get('/application')->{datastore};
112 0 0 0       if
113             (
114             -f "./sweet/configuration/datastores/$datastore/table/users.yml" ||
115             -f "./sweet/configuration/datastores/$datastore/table/permissions.yml"
116             )
117             {
118 0           return $e->error('Database already created.')->report($c);
119             }
120             else
121             {
122             # check for valid dsn, user and pass
123 0 0         if ($self->_check_dsn($c->argv->[0], $c->argv->[1], $c->argv->[2])) {
124 0           my ($dsn, $user, $pass) =
125             ($c->argv->[0], $c->argv->[1], $c->argv->[2]);
126            
127 0           my @dsn = ($dsn, $user, $pass);
128 0           my ($scheme, $driver, @trash) = DBI->parse_dsn($dsn[0]);
129 0           my $translator = SQL::Translator->new(
130             debug => 0,
131             add_drop_table => 0,
132             quote_table_names => 1,
133             quote_field_names => 1,
134             validate => 1,
135             no_comments => 1,
136             producer => $self->_translate_database_type($driver)
137             );
138            
139 0           my $schema = $translator->schema(
140             name => $scheme,
141             );
142            
143 0           my $table = $schema->add_table( name => 'users' );
144 0           $table->add_field(
145             name => 'id',
146             data_type => 'integer',
147             size => 11,
148             table => $table,
149            
150             is_auto_increment => 1,
151             is_primary_key => 1
152             );
153 0           $table->add_field(
154             name => 'name',
155             data_type => 'varchar',
156             size => 255,
157             table => $table,
158            
159             is_nullable => 0
160             );
161 0           $table->add_field(
162             name => 'email',
163             data_type => 'varchar',
164             size => 255,
165             table => $table,
166            
167             is_nullable => 0
168             );
169 0           $table->add_field(
170             name => 'login',
171             data_type => 'varchar',
172             size => 255,
173             table => $table,
174             is_unique => 1,
175            
176             is_nullable => 0
177             );
178 0           $table->add_field(
179             name => 'password',
180             data_type => 'varchar',
181             size => 255,
182             table => $table,
183            
184             is_nullable => 0
185             );
186 0           $table->add_field(
187             name => 'status',
188             data_type => 'integer',
189             size => 1,
190             table => $table,
191            
192             is_nullable => 0
193             );
194 0           $table->primary_key('id');
195            
196 0           $table = $schema->add_table( name => 'permissions' );
197 0           $table->add_field(
198             name => 'id',
199             data_type => 'integer',
200             size => 11,
201             table => $table,
202            
203             is_auto_increment => 1,
204             is_primary_key => 1
205             );
206 0           $table->add_field(
207             name => 'user',
208             data_type => 'integer',
209             size => 11,
210             table => $table,
211            
212             is_nullable => 0
213             );
214 0           $table->add_field(
215             name => 'role',
216             data_type => 'varchar',
217             size => 255,
218             table => $table,
219            
220             is_nullable => 1
221             );
222 0           $table->add_field(
223             name => 'permission',
224             data_type => 'varchar',
225             size => 255,
226             table => $table,
227            
228             is_nullable => 1
229             );
230 0           $table->add_field(
231             name => 'operation',
232             data_type => 'varchar',
233             size => 255,
234             table => $table,
235            
236             is_nullable => 1
237             );
238 0           $table->primary_key('id');
239 0 0         my $db = DBI->connect(@dsn) or exit print "\n", $@;
240 0 0         if ($db) {
241             # hack
242 0           my ($scheme, $driver, @trash)
243             = DBI->parse_dsn($dsn[0]);
244            
245 0           for ($translator->translate(
246             to => $self->_translate_database_type($driver))) {
247 0 0         $db->do($_) or exit print "\n", $@;
248             }
249             }
250            
251             # auto-update
252 0           $self->update($c);
253            
254 0           return $f->flash('Database created successfully.')->report($c);
255             }
256             else {
257 0           my @return = (
258             'Invalid datasource. Use `help data;` for instructions.'
259             );
260 0           return $e->error(@return)->report($c);
261             }
262             }
263             }
264              
265             sub update {
266 0     0 0   my $self = shift;
267 0           my $c = shift;
268 0           my $f = SweetPea::Cli::Flash->new;
269 0           my $e = SweetPea::Cli::Error->new;
270 0           my $u = SweetPea::Cli::Util->new;
271 0           my $h = SweetPea::Cli::Help->new;
272 0           my $s = SweetPea::Application::Config->new(
273             SweetPea::Application->new,
274             Cwd::getcwd
275             );
276            
277             # check for valid dsn, user and pass
278 0 0         if ($self->_check_dsn($c->argv->[0], $c->argv->[1], $c->argv->[2])) {
279 0           my ($dsn, $user, $pass) =
280             ($c->argv->[0], $c->argv->[1], $c->argv->[2]);
281            
282 0           my @dsn = ($dsn, $user, $pass);
283 0           my ($scheme, $driver, @trash)
284             = DBI->parse_dsn($dsn[0]);
285            
286 0 0         my $db = DBI->connect(@dsn)
287             or exit print "\n", $@;
288            
289 0           my $translator = SQL::Translator->new(
290             parser => 'DBI',
291             parser_args => {
292             dsn => $dsn[0],
293             db_user => $dsn[1],
294             db_password => $dsn[2],
295             },
296             producer => $self->_translate_database_type($driver)
297             );
298 0 0         $translator->translate or die $translator->error;
299            
300 0           my $schema = $translator->schema;
301            
302 0 0         my @tables = $schema->get_tables
303             or exit print "\n", $translator->error;
304            
305             # update datastore config
306 0           my $application = $s->get('/application');
307 0           my $datastore = $s->get('/datastores');
308            
309 0           $datastore->{datastores}->{$application->{datastore}} = {
310             dsn => $dsn[0],
311             username => $dsn[1],
312             password => $dsn[2]
313             };
314            
315 0           $s->set('/datastores');
316            
317 0           my $store = $application->{datastore};
318            
319 0           foreach my $table (@tables) {
320            
321 0           my $profile = {};
322 0           my $name = $table->name;
323 0           my $yaml =
324             "/datastores/development/table/$name";
325 0           my $path =
326             "sweet/configuration/datastores/development/table/$name.yml";
327            
328 0           $profile = {
329             table => {
330             'name' => $name,
331             'columns' => {}
332             },
333             form => {
334             'name' => $name . "_form",
335             'fields' => {},
336             'validation' => {
337             optional => [],
338             required => [],
339             constraint_methods => {}
340             }
341             },
342             grid => {
343             'name' => $name . "_grid",
344             'columns' => {}
345             }
346             };
347            
348             # update table configuration data
349 0           my @fields = $table->get_fields;
350 0           foreach my $field (@fields) {
351 0           my $name = $field->name;
352 0 0         if ($name) {
353 0           my $field_label = ucfirst $name;
354 0           $field_label =
355 0           join(" ", map {ucfirst $_} split /_/, $field_label);
356            
357             # build validation hash
358            
359 0 0         if ($field->is_nullable) {
360 0           push @{$profile->{form}->{validation}->{optional}},
  0            
361             $name;
362             }
363             else {
364 0           push @{$profile->{form}->{validation}->{required}},
  0            
365             $name;
366             }
367            
368 0 0         $profile->{table}->{columns}->{$name} = {
369             'type' => $field->data_type,
370             'size' => $field->size,
371             'value' => ( lc($field->default_value) eq 'null' ?
372             '' : $field->default_value ),
373             'required' => $field->is_nullable,
374             'key' => $field->is_primary_key,
375             'auto' => $field->is_auto_increment,
376             'unique' => $field->is_unique
377             };
378 0           $profile->{form}->{fields}->{$name} = {
379             name => $name,
380             length => $field->size,
381             value => $field->default_value,
382             maps_to => $name,
383             label => $field_label,
384             type => 'text',
385             input_via => 'post',
386             attributes => {
387             class => 'form_field'
388             }
389             };
390 0           $profile->{grid}->{columns}->{$name} = {
391             attributes => {
392             class => 'grid_column'
393             },
394             maps_to => $name,
395             value => $field->default_value,
396             name => $name,
397             label => $field_label
398             };
399             }
400             }
401            
402             # get/set base table configuration data
403 0 0         if (-e $path) {
404 0           my $existing = $s->get($yaml);
405 0           $profile = merge $profile, $existing;
406             }
407            
408             # save changes
409 0           $s->set($yaml, $profile);
410             }
411            
412 0           return $f->flash('Database data files updated.')->report($c);
413             }
414             else {
415 0           my @return = (
416             'Invalid datasource. Use `help data;` for instructions.'
417             );
418 0           return $e->error(@return)->report($c);
419             }
420             }
421              
422             sub _check_dsn {
423 0     0     my $self = shift;
424 0           my ($dsn, $user, $pass) = @_;
425            
426 0 0         return 0 if !$dsn;
427 0 0 0       return ($dsn =~ /^dbi\:/ && $user) ? 1 : 0;
428             }
429              
430             sub _translate_database_type {
431 0     0     my $self = shift;
432 0           my $dsn = shift;
433 0 0         $dsn =~ s/dbi\:([a-zA-Z0-9\-\_]+)\:/dbi\:$1\:/ if $dsn =~ /\:/;
434 0           my $dbt = {
435             'db2' => 'DB2',
436             'mysql' => 'MySQL',
437             'oracle' => 'Oracle',
438             'pg' => 'PostgrSQL',
439             'odbc' => 'SQLServer',
440             'sqlite' => 'SQLite',
441             'sybase' => 'Sybase'
442             };
443 0           return $dbt->{lc($dsn)};
444             }
445             1; # End of SweetPea::Cli::Data