File Coverage

blib/lib/DBIx/Schema/UpToDate.pm
Criterion Covered Total %
statement 71 72 98.6
branch 19 22 86.3
condition 2 2 100.0
subroutine 19 19 100.0
pod 12 12 100.0
total 123 127 96.8


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 DBIx-Schema-UpToDate
4             #
5             # This software is copyright (c) 2011 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   27229 use strict;
  2         5  
  2         90  
11 2     2   12 use warnings;
  2         4  
  2         116  
12              
13             package DBIx::Schema::UpToDate;
14             BEGIN {
15 2     2   44 $DBIx::Schema::UpToDate::VERSION = '1.001';
16             }
17             BEGIN {
18 2     2   53 $DBIx::Schema::UpToDate::AUTHORITY = 'cpan:RWSTAUNER';
19             }
20             # ABSTRACT: Helps keep a database schema up to date
21              
22 2     2   10 use Carp qw(croak carp); # core
  2         4  
  2         395  
23              
24              
25             sub new {
26 1     1 1 241 my $class = shift;
27 0         0 my $self = {
28             auto_update => 1,
29             sql_limit => 1,
30             transactions => 1,
31 1 50       6 @_ == 1 ? %{$_[0]} : @_
32             };
33 1         3 bless $self, $class;
34              
35             # make sure the database schema is current
36 1 50       9 $self->up_to_date()
37             if $self->{auto_update};
38              
39 1         3 return $self;
40             }
41              
42              
43             foreach my $action ( qw(begin_work commit) ){
44 2     2   11 no strict 'refs'; ## no critic (NoStrict)
  2         4  
  2         1846  
45             *$action = sub {
46 24     24   30 my ($self) = @_;
47 24 100       64 if( $self->{transactions} ){
48 22         39 my $dbh = $self->dbh;
49 22 100       109 $dbh->$action()
50             or croak $dbh->errstr;
51             }
52             }
53             }
54              
55              
56              
57             sub dbh {
58 81     81 1 87 my ($self) = @_;
59 81         163 return $self->{dbh};
60             }
61              
62              
63             sub current_version {
64 9     9 1 881 my ($self) = @_;
65 9         20 my $dbh = $self->dbh;
66 9         18 my $table = $self->version_table_name;
67 9         9 my $version;
68              
69 9         53 my $tables = $dbh->table_info('%', '%', $table, 'TABLE')
70             ->fetchall_arrayref;
71              
72             # if table exists query it for current database version
73 9 100       936 if( @$tables ){
74 5         11 my $qtable = $self->quoted_table_name;
75 5         277 my $field = $dbh->quote_identifier('version');
76              
77 5 100       252 my $v = $dbh->selectcol_arrayref(
78             "SELECT $field from $qtable ORDER BY $field DESC"
79             . ($self->{sql_limit} ? ' LIMIT 1' : '')
80             )->[0];
81 5 50       242 $version = $v
82             if defined $v;
83             }
84              
85 9         31 return $version;
86             }
87              
88              
89             sub initialize_version_table {
90 3     3 1 769 my ($self) = @_;
91 3         7 my $dbh = $self->dbh;
92              
93 3         8 my ($version, $updated) = $self->quote_identifiers(qw(version updated));
94              
95 3         168 $self->begin_work();
96              
97 3 100       178 $dbh->do('CREATE TABLE ' . $self->quoted_table_name .
98             " ($version integer, $updated timestamp)"
99             )
100             or croak $dbh->errstr;
101              
102 2         220 $self->set_version(0);
103              
104 2         279 $self->commit();
105             }
106              
107              
108             sub latest_version {
109 5     5 1 7 my ($self) = @_;
110 5         5 return scalar @{ $self->updates };
  5         9  
111             }
112              
113              
114             sub quoted_table_name {
115 20     20 1 29 my ($self) = @_;
116 20         34 return $self->dbh->quote_identifier($self->version_table_name);
117             }
118              
119              
120             sub quote_identifiers {
121 15     15 1 727 my ($self, @names) = @_;
122 15         31 my $dbh = $self->dbh;
123 15         25 return map { $dbh->quote_identifier($_) } @names;
  30         879  
124             }
125              
126              
127             sub set_version {
128 12     12 1 853 my ($self, $version) = @_;
129 12         26 my $dbh = $self->dbh;
130              
131 12 100       24 $dbh->do('INSERT INTO ' . $self->quoted_table_name .
132             ' (' .
133             join(', ', $self->quote_identifiers(qw(version updated)))
134             . ') VALUES(?, ?)',
135             {}, $version, time()
136             )
137             or croak $dbh->errstr;
138             }
139              
140              
141             sub updates {
142 15     15 1 19 my ($self) = @_;
143 15   100     71 return $self->{updates} ||= [
144             ];
145             }
146              
147              
148             sub update_to_version {
149 10     10 1 4413 my ($self, $version) = @_;
150              
151 10         22 $self->begin_work();
152              
153             # execute updates to bring database to $version
154 9         449 $self->updates->[$version - 1]->($self);
155              
156             # save the version now in case we get interrupted before the next commit
157 9         39 $self->set_version($version);
158              
159 9         1101 $self->commit();
160             }
161              
162              
163             sub up_to_date {
164 4     4 1 7 my ($self) = @_;
165              
166 4         9 my $current = $self->current_version;
167 4 100       13 if( !defined($current) ){
168 2         7 $self->initialize_version_table;
169 2         100 $current = $self->current_version;
170 2 100       13 die("Unable to initialize version table\n")
171             if !defined($current);
172             }
173              
174 3         5 my $latest = $self->latest_version;
175              
176             # execute each update required to go from current to latest version
177             # (starting with next version, obviously (don't redo current))
178             $self->update_to_version($_)
179 3         12 foreach ($current + 1) .. $latest;
180             }
181              
182              
183             sub version_table_name {
184 29     29 1 126 'schema_version'
185             }
186              
187             1;
188              
189              
190             __END__