initial shot at "dev version" functionality
[dbsrgits/DBIx-Class-DeploymentHandler.git] / lib / DBIx / Class / DeploymentHandler.pm
1 package DBIx::Class::DeploymentHandler;
2
3 # ABSTRACT: Extensible DBIx::Class deployment
4
5 use Moose;
6
7 extends 'DBIx::Class::DeploymentHandler::Dad';
8 # a single with would be better, but we can't do that
9 # see: http://rt.cpan.org/Public/Bug/Display.html?id=46347
10 with 'DBIx::Class::DeploymentHandler::WithApplicatorDumple' => {
11     interface_role       => 'DBIx::Class::DeploymentHandler::HandlesDeploy',
12     class_name           => 'DBIx::Class::DeploymentHandler::DeployMethod::SQL::Translator',
13     delegate_name        => 'deploy_method',
14     attributes_to_assume => [qw(schema schema_version)],
15     attributes_to_copy   => [qw(
16       ignore_ddl databases script_directory sql_translator_args force_overwrite
17     )],
18   },
19   'DBIx::Class::DeploymentHandler::WithApplicatorDumple' => {
20     interface_role       => 'DBIx::Class::DeploymentHandler::HandlesVersioning',
21     class_name           => 'DBIx::Class::DeploymentHandler::VersionHandler::Monotonic',
22     delegate_name        => 'version_handler',
23     attributes_to_assume => [qw( database_version schema_version to_version )],
24   },
25   'DBIx::Class::DeploymentHandler::WithApplicatorDumple' => {
26     interface_role       => 'DBIx::Class::DeploymentHandler::HandlesVersionStorage',
27     class_name           => 'DBIx::Class::DeploymentHandler::VersionStorage::Standard',
28     delegate_name        => 'version_storage',
29     attributes_to_assume => ['schema'],
30   };
31 with 'DBIx::Class::DeploymentHandler::WithReasonableDefaults';
32
33 sub prepare_version_storage_install {
34   my $self = shift;
35
36   $self->prepare_resultsource_install({
37     result_source => $self->version_storage->version_rs->result_source
38   });
39 }
40
41 sub install_version_storage {
42   my $self = shift;
43
44   my $version = (shift||{})->{version} || $self->schema_version;
45
46   $self->install_resultsource({
47     result_source => $self->version_storage->version_rs->result_source,
48     version       => $version,
49   });
50 }
51
52 sub prepare_install {
53   $_[0]->prepare_deploy;
54   $_[0]->prepare_version_storage_install;
55 }
56
57 # the following is just a hack so that ->version_storage
58 # won't be lazy
59 sub BUILD { $_[0]->version_storage }
60 __PACKAGE__->meta->make_immutable;
61
62 sub dev_version_instaled { shift->database_version eq 'DEV' }
63
64 use SQL::Translator;
65 sub install_dev_version {
66    my ($self, $extra) = @_;
67
68    die 'roll back existing dev version before installing a new dev version'
69       if $self->dev_version_installed;
70
71    my $from_schema = do {
72       my $t = SQL::Translator->new({
73          debug => 0,
74          trace => 0,
75          parser => 'SQL::Translator::Parser::YAML',
76       });
77       $t->translate($self->deploy_method->_ddl_protoschema_produce_filename(
78          $self->database_version, $self->script_directory
79       ) or die $t->error;
80       $t->schema;
81    };
82
83    my $to_schema = do {
84       my $t = SQL::Translator->new({
85          parser => 'SQL::Translator::Parser::DBIx::Class',
86          parser_args => {
87             package => $self->schema,
88          },
89       });
90       $t->translate or die $t->error;
91       $t->schema;
92    };
93
94    my $db = $self->deploy_method->storage->sqlt_type;
95
96    my @up = [SQL::Translator::Diff::schema_diff(
97       $source_schema, $db,
98       $dest_schema,   $db,
99       $sqltargs
100    )];
101
102    my @down = [SQL::Translator::Diff::schema_diff(
103       $dest_schema,   $db,
104       $source_schema, $db,
105       $sqltargs
106    )];
107
108    $self->deploy_method->_run_sql_array(\@up);
109
110    $self->version_storage->version_rs->create({
111       version       => 'DEV',
112       upgrade_sql   => \@up,
113       downgrade_sql => \@down,
114       extra         => $extra,
115    });
116 }
117
118 sub remove_dev_version {
119    my ($self, $extra) = @_;
120
121    die 'no dev version installed to remove'
122       unless $self->dev_version_installed;
123
124    my ($dev_data, $to_version) = $self->version_storage
125       ->version_rs
126       ->search(undef, {
127          order_by => { -desc => 'id' },
128          rows => 2,
129       })->all;
130
131    $self->deploy_method->_run_sql_array(\@sql_to_run);
132
133    $self->version_storage->version_rs->create({
134       version => $to_version->version,
135       upgrade_sql => \@sql_to_run,
136       downgrade_sql => [], # not meant to be "reverted"
137       extra         => $extra
138    })
139 }
140
141 1;
142
143 #vim: ts=2 sw=2 expandtab
144
145 __END__
146
147 =head1 SYNOPSIS
148
149  use aliased 'DBIx::Class::DeploymentHandler' => 'DH';
150  my $s = My::Schema->connect(...);
151
152  my $dh = DH->new({
153    schema              => $s,
154    databases           => 'SQLite',
155    sql_translator_args => { add_drop_table => 0 },
156  });
157
158  $dh->prepare_install;
159
160  $dh->install;
161
162 or for upgrades:
163
164  use aliased 'DBIx::Class::DeploymentHandler' => 'DH';
165  my $s = My::Schema->connect(...);
166
167  my $dh = DH->new({
168    schema              => $s,
169    databases           => 'SQLite',
170    sql_translator_args => { add_drop_table => 0 },
171  });
172
173  $dh->prepare_upgrade({
174    from_version => 1,
175    to_version   => 2,
176  });
177
178  $dh->upgrade;
179
180 =head1 DESCRIPTION
181
182 C<DBIx::Class::DeploymentHandler> is, as its name suggests, a tool for
183 deploying and upgrading databases with L<DBIx::Class>.  It is designed to be
184 much more flexible than L<DBIx::Class::Schema::Versioned>, hence the use of
185 L<Moose> and lots of roles.
186
187 C<DBIx::Class::DeploymentHandler> itself is just a recommended set of roles
188 that we think will not only work well for everyone, but will also yield the
189 best overall mileage.  Each role it uses has its own nuances and
190 documentation, so I won't describe all of them here, but here are a few of the
191 major benefits over how L<DBIx::Class::Schema::Versioned> worked (and
192 L<DBIx::Class::DeploymentHandler::Deprecated> tries to maintain compatibility
193 with):
194
195 =over
196
197 =item *
198
199 Downgrades in addition to upgrades.
200
201 =item *
202
203 Multiple sql files files per upgrade/downgrade/install.
204
205 =item *
206
207 Perl scripts allowed for upgrade/downgrade/install.
208
209 =item *
210
211 Just one set of files needed for upgrade, unlike before where one might need
212 to generate C<factorial(scalar @versions)>, which is just silly.
213
214 =item *
215
216 And much, much more!
217
218 =back
219
220 That's really just a taste of some of the differences.  Check out each role for
221 all the details.
222
223 =head1 WHERE IS ALL THE DOC?!
224
225 C<DBIx::Class::DeploymentHandler> extends
226 L<DBIx::Class::DeploymentHandler::Dad>, so that's probably the first place to
227 look when you are trying to figure out how everything works.
228
229 Next would be to look at all the pieces that fill in the blanks that
230 L<DBIx::Class::DeploymentHandler::Dad> expects to be filled.  They would be
231 L<DBIx::Class::DeploymentHandler::DeployMethod::SQL::Translator>,
232 L<DBIx::Class::DeploymentHandler::VersionHandler::Monotonic>,
233 L<DBIx::Class::DeploymentHandler::VersionStorage::Standard>, and
234 L<DBIx::Class::DeploymentHandler::WithReasonableDefaults>.
235
236 =method prepare_version_storage_install
237
238  $dh->prepare_version_storage_install
239
240 Creates the needed C<.sql> file to install the version storage and not the rest
241 of the tables
242
243 =method prepare_install
244
245  $dh->prepare_install
246
247 First prepare all the tables to be installed and the prepare just the version
248 storage
249
250 =method install_version_storage
251
252  $dh->install_version_storage
253
254 Install the version storage and not the rest of the tables
255
256 =head1 THIS SUCKS
257
258 You started your project and weren't using C<DBIx::Class::DeploymentHandler>?
259 Lucky for you I had you in mind when I wrote this doc.
260
261 First,
262 L<define the version|DBIx::Class::DeploymentHandler::Intro/Sample_database>
263 in your main schema file (maybe using C<$VERSION>).
264
265 Then you'll want to just install the version_storage:
266
267  my $s = My::Schema->connect(...);
268  my $dh = DBIx::Class::DeploymentHandler->new({ schema => $s });
269
270  $dh->prepare_version_storage_install;
271  $dh->install_version_storage;
272
273 Then set your database version:
274
275  $dh->add_database_version({ version => $s->version });
276
277 Now you should be able to use C<DBIx::Class::DeploymentHandler> like normal!
278
279 =head1 LOGGING
280
281 This is a complex tool, and because of that sometimes you'll want to see
282 what exactly is happening.  The best way to do that is to use the built in
283 logging functionality.  It the standard six log levels; C<fatal>, C<error>,
284 C<warn>, C<info>, C<debug>, and C<trace>.  Most of those are pretty self
285 explanatory.  Generally a safe level to see what all is going on is debug,
286 which will give you everything except for the exact SQL being run.
287
288 To enable the various logging levels all you need to do is set an environment
289 variables: C<DBICDH_FATAL>, C<DBICDH_ERROR>, C<DBICDH_WARN>, C<DBICDH_INFO>,
290 C<DBICDH_DEBUG>, and C<DBICDH_TRACE>.  Each level can be set on its own,
291 but the default is the first three on and the last three off, and the levels
292 cascade, so if you turn on trace the rest will turn on automatically.
293
294 =head1 DONATIONS
295
296 If you'd like to thank me for the work I've done on this module, don't give me
297 a donation. I spend a lot of free time creating free software, but I do it
298 because I love it.
299
300 Instead, consider donating to someone who might actually need it.  Obviously
301 you should do research when donating to a charity, so don't just take my word
302 on this.  I like Children's Survival Fund:
303 L<http://www.childrenssurvivalfund.org>, but there are a host of other
304 charities that can do much more good than I will with your money.