File Coverage

blib/lib/App/mimi.pm
Criterion Covered Total %
statement 111 115 96.5
branch 45 54 83.3
condition 10 17 58.8
subroutine 19 19 100.0
pod 6 6 100.0
total 191 211 90.5


line stmt bran cond sub pod time code
1             package App::mimi;
2              
3 1     1   49655 use strict;
  1         2  
  1         22  
4 1     1   3 use warnings;
  1         1  
  1         32  
5              
6             our $VERSION = '0.03';
7              
8 1     1   3 use Carp qw(croak);
  1         2  
  1         38  
9 1     1   3 use File::Spec;
  1         1  
  1         13  
10 1     1   3 use File::Basename ();
  1         1  
  1         15  
11 1     1   3 use DBI;
  1         1  
  1         27  
12 1     1   300 use App::mimi::db;
  1         1  
  1         912  
13              
14             sub new {
15 18     18 1 23878 my $class = shift;
16 18         34 my (%params) = @_;
17              
18 18         20 my $self = {};
19 18         21 bless $self, $class;
20              
21 18         28 $self->{dsn} = $params{dsn};
22 18         22 $self->{schema} = $params{schema};
23 18         20 $self->{dry_run} = $params{dry_run};
24 18         17 $self->{verbose} = $params{verbose};
25 18         20 $self->{migration} = $params{migration};
26 18         22 $self->{dbh} = $params{dbh};
27              
28 18         52 return $self;
29             }
30              
31             sub setup {
32 5     5 1 111 my $self = shift;
33              
34 5         11 my $db = $self->_build_db;
35              
36 5 100       9 die "Error: migrations table already exists\n" if $db->is_prepared;
37              
38 4         12 $self->_print("Creating migrations table");
39              
40 4 50       6 $db->prepare unless $self->_is_dry_run;
41              
42 4         1027 return $self;
43             }
44              
45             sub migrate {
46 12     12 1 332 my $self = shift;
47              
48             die "Error: Schema directory is required\n"
49 12 100 66     253 unless $self->{schema} && -d $self->{schema};
50              
51 11         916 my @schema_files = glob("$self->{schema}/*.sql");
52 11 100       36 die "Error: No schema *.sql files found in '$self->{schema}'\n"
53             unless @schema_files;
54              
55 10         22 my $db = $self->_build_db_prepared;
56              
57 9         21 my $last_migration = $db->fetch_last_migration;
58              
59 9 100 100     34 if ($last_migration && $last_migration->{status} ne 'success') {
60 1   50     7 $last_migration->{error} ||= 'Unknown error';
61 1         11 die "Error: Migrations are dirty. "
62             . "Last error was in migration $last_migration->{no}:\n\n"
63             . " $last_migration->{error}\n"
64             . "After fixing the problem run command\n";
65             }
66              
67 8 100       23 $self->_print("Found last migration $last_migration->{no}")
68             if $last_migration;
69              
70 8         8 my @migrations;
71 8         10 for my $file (@schema_files) {
72 8         362 my ($no, $name) = File::Basename::basename($file) =~ /^(\d+)(.*)$/;
73 8 50 33     38 next unless $no && $name;
74              
75 8         13 $no = int($no);
76              
77 8 50 66     23 next if $last_migration && $no <= $last_migration->{no};
78              
79 8         15 my @sql = split /;/, $self->_slurp($file);
80              
81 8         41 push @migrations,
82             {
83             file => $file,
84             no => $no,
85             name => $name,
86             sql => \@sql
87             };
88             }
89              
90 8 50       15 if (@migrations) {
91 8         14 my $dbh = $self->{dbh};
92 8         10 foreach my $migration (@migrations) {
93 8         28 $self->_print("Migrating '$migration->{file}'");
94              
95 8         7 my $e;
96 8 100       11 if (!$self->_is_dry_run) {
97 7 50       8 eval { $dbh->do($_) for @{$migration->{sql} || []} } or do {
  7 50       5  
  7         38  
98 7         174 $e = $@;
99              
100 7         22 $e =~ s{ at .*? line \d+.$}{};
101             };
102             }
103              
104 8         25 $self->_print("Creating migration: $migration->{no}");
105              
106             $db->create_migration(
107             no => $migration->{no},
108 8 100       12 created => time,
    100          
109             status => $e ? 'error' : 'success',
110             error => $e
111             ) unless $self->_is_dry_run;
112              
113 8 100       34 die "Error: $e\n" if $e;
114             }
115             }
116             else {
117 0         0 $self->_print("Nothing to migrate");
118             }
119              
120 6         25 return $self;
121             }
122              
123             sub check {
124 4     4 1 717 my $self = shift;
125              
126 4         8 $self->{verbose} = 1;
127              
128 4         8 my $db = $self->_build_db;
129              
130 4 100       22 if (!$db->is_prepared) {
131 1         4 $self->_print('Migrations are not installed');
132             } else {
133 3         7 my $last_migration = $db->fetch_last_migration;
134              
135 3 100       7 if (!defined $last_migration) {
136 1         2 $self->_print('No migrations found');
137             } else {
138             $self->_print(sprintf 'Last migration: %d (%s)',
139 2         13 $last_migration->{no}, $last_migration->{status});
140              
141 2 100       8 if (my $error = $last_migration->{error}) {
142 1         3 $self->_print("\n" . $error);
143             }
144             }
145             }
146             }
147              
148             sub fix {
149 2     2 1 10 my $self = shift;
150              
151 2         5 my $db = $self->_build_db_prepared;
152              
153 2         6 my $last_migration = $db->fetch_last_migration;
154              
155 2 50 33     16 if (!$last_migration || $last_migration->{status} eq 'success') {
156 0         0 $self->_print('Nothing to fix');
157             }
158             else {
159 2         9 $self->_print("Fixing migration $last_migration->{no}");
160              
161 2 100       5 $db->fix_last_migration unless $self->_is_dry_run;
162             }
163             }
164              
165             sub set {
166 2     2 1 6 my $self = shift;
167              
168 2         5 my $db = $self->_build_db_prepared;
169              
170 2         24 $self->_print("Creating migration $self->{migration}");
171              
172             $db->create_migration(
173             no => $self->{migration},
174 2 100       6 created => time,
175             status => 'success'
176             ) unless $self->_is_dry_run;
177             }
178              
179             sub _build_db_prepared {
180 14     14   12 my $self = shift;
181              
182 14         20 my $db = $self->_build_db;
183              
184 14 100       24 die "Error: Migrations table not found. Run command first\n"
185             unless $db->is_prepared;
186              
187 13         17 return $db;
188             }
189              
190             sub _build_db {
191 23     23   20 my $self = shift;
192              
193 23         34 my $dbh = $self->{dbh};
194              
195 23 50       42 if (!$dbh) {
196 0         0 $dbh = DBI->connect($self->{dsn}, '', '',
197             {RaiseError => 1, PrintError => 0, PrintWarn => 0});
198 0         0 $self->{dbh} = $dbh;
199             }
200              
201 23         77 return App::mimi::db->new(dbh => $dbh);
202             }
203              
204             sub _print {
205 33     33   30 my $self = shift;
206              
207 33 100       42 return unless $self->_is_verbose;
208              
209 9 100       14 print 'DRY RUN: ' if $self->_is_dry_run;
210              
211 9         310 print @_, "\n";
212             }
213              
214 61     61   304 sub _is_dry_run { $_[0]->{dry_run} }
215 33 100   33   86 sub _is_verbose { $_[0]->{verbose} || $_[0]->_is_dry_run }
216              
217             sub _slurp {
218 8     8   8 my $self = shift;
219 8         9 my ($file) = @_;
220              
221 8 50       254 open my $fh, '<', $file or croak $!;
222 8         28 local $/;
223 8         181 <$fh>;
224             }
225              
226             1;
227             __END__