File Coverage

blib/lib/DB/SQL/Migrations.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package DB::SQL::Migrations;
2              
3 1     1   17622 use 5.010000;
  1         5  
  1         51  
4 1     1   437 use Mojo::Base -base;
  1         7086  
  1         7  
5 1     1   146 use File::Basename;
  1         4  
  1         74  
6 1     1   208 use DBIx::MultiStatementDo;
  0            
  0            
7             use File::Slurp;
8              
9             our $VERSION = '0.07';
10              
11             has [qw( dbh migrations_directory )];
12             has schema_migrations_table => sub { 'schema_migrations' };
13             has schema_migrations_name_field => sub { 'name' };
14             has schema_migrations_date_field => sub { 'date_applied' };
15              
16             has _applied_migrations => sub {
17             my $self = shift;
18             my %applied_migrations;
19              
20             my $sth = $self->dbh->prepare("SELECT " .$self->schema_migrations_name_field . ", ". $self->schema_migrations_date_field ." FROM ". $self->schema_migrations_table );
21             $sth->execute();
22             $sth->bind_columns( \my ( $name, $date_applied ) );
23             while ( $sth->fetch() ) {
24             $applied_migrations{$name} = $date_applied;
25             }
26             $sth->finish;
27              
28             return \%applied_migrations;
29             };
30              
31             sub _pending_migrations {
32             my $self = shift;
33             my @pending_migrations;
34              
35             foreach my $migration_file( $self->_migration_files_in_order ) {
36             my $migration_key = $self->_migration_key($migration_file);
37             push @pending_migrations, $migration_file unless exists $self->_applied_migrations->{$migration_key};
38             }
39              
40             foreach my $pending_migration(@pending_migrations) {
41             print "$pending_migration is pending\n";
42             }
43              
44             return @pending_migrations;
45             }
46              
47             sub apply {
48             my $self = shift;
49              
50             my @pending_migrations = $self->_pending_migrations;
51              
52             if(scalar(@pending_migrations)) {
53              
54             foreach my $migration(@pending_migrations) {
55             $self->_apply_migration($migration);
56             }
57             }
58             else {
59             print "Up to date\n";
60             }
61             }
62              
63             sub _apply_migration {
64             my $self = shift;
65             my $file_name = shift;
66              
67             my $sql = read_file($file_name);
68             my $batch = DBIx::MultiStatementDo->new(
69             dbh => $self->dbh,
70             rollback => 0
71             );
72             $batch->dbh->{AutoCommit} = 0;
73             $batch->dbh->{RaiseError} = 1;
74              
75             eval {
76             $batch->do( $sql );
77             $batch->dbh->commit;
78             1
79             } or do {
80             print "$@ \n";
81             eval { $batch->dbh->rollback };
82             print "Failed to apply migration: $file_name\n";
83              
84             die "Exiting due to failed migrations \n";
85             };
86              
87             $self->_insert_into_schema_migrations($file_name);
88              
89             print "Applied migration $file_name \n";
90             }
91              
92             sub _insert_into_schema_migrations {
93             my $self = shift;
94             my $migration = shift;
95             my $migration_key = $self->_migration_key($migration);
96              
97             $self->dbh->do("INSERT INTO ". $self->schema_migrations_table ." (". $self->schema_migrations_name_field .", ". $self->schema_migrations_date_field .") VALUES (?,NOW())", undef, $migration_key );
98             $self->dbh->commit;
99             }
100              
101             sub _migration_files_in_order {
102             my $self = shift;
103             my $dir = $self->migrations_directory;
104              
105             return sort <$dir/*.sql>;
106             }
107              
108             sub create_migrations_table {
109             my $self = shift;
110             my $table_name = $self->schema_migrations_table;
111             my $name_field = $self->schema_migrations_name_field;
112             my $date_field = $self->schema_migrations_date_field;
113              
114             my $sql = "CREATE TABLE IF NOT EXISTS $table_name (
115             $name_field varchar(255) NOT NULL PRIMARY KEY,
116             $date_field datetime NOT NULL
117             );
118             ";
119              
120             $self->dbh->do($sql);
121             }
122              
123             sub _migration_key {
124             my $self = shift;
125             my $migration_file = shift;
126              
127             #Use filename for the key
128             my($filename, $directories, $suffix) = fileparse($migration_file);
129             return $filename;
130             }
131              
132             1;
133             __END__
134              
135             =head1 NAME
136              
137             DB::SQL::Migrations - apply database migrations via scripts in a directory
138              
139             =head1 SYNOPSIS
140              
141             use DB::SQL::Migrations;
142             my $migrator = DB::SQL::Migrations->new( dbh => $some_db_handle,
143             migrations => $some_path,
144             )
145              
146             $migrator->create_migrations_table(); #creates schema table if it doesn't exist
147             $migrator->apply();
148              
149             =head1 DESCRIPTION
150              
151             Run a number of small SQL scripts
152              
153             =head1 REPOSITORY
154              
155             L<https://github.com/jontaylor/DB-SQL-Migrations>
156              
157             =head1 AUTHOR
158              
159             Adam Omielan, E<lt>adam@assure24.comE<gt>
160             Jonathan Taylor, E<lt>jon@stackhaus.comE<gt>
161              
162             =head1 COPYRIGHT AND LICENSE
163              
164             Copyright (C) 2012 by Jonathan Taylor
165              
166             This library is free software; you can redistribute it and/or modify
167             it under the same terms as Perl itself, either Perl version 5.14.2 or,
168             at your option, any later version of Perl 5 you may have available.
169              
170              
171             =cut