remove un-needed coerce from schema_class of type Str
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Admin.pm
CommitLineData
9f3849c3 1package DBIx::Class::Admin;
2
71ef99d5 3# check deps
4BEGIN {
5 my @_deps = qw(
6 Moose MooseX::Types MooseX::Types::JSON MooseX::Types::Path::Class
7 Try::Tiny parent JSON::Any Class::C3::Componentised
8 namespace::autoclean
9 );
10
11 my @_missing_deps;
12 foreach my $dep (@_deps) {
13 eval "require $dep";
14 if ($@) {
15 push @_missing_deps, $dep;
16 }
17 }
417e1784 18
71ef99d5 19 if (@_missing_deps > 0) {
20 die "The following dependecies are missing " . join ",", @_missing_deps;
21 }
22}
585072bb 23
71ef99d5 24use Moose;
585072bb 25use parent 'DBIx::Class::Schema';
71ef99d5 26use Carp::Clan qw/^DBIx::Class/;
9f3849c3 27
a62dec82 28use MooseX::Types::Moose qw/Int Str Any Bool/;
29use DBIx::Class::Admin::Types qw/DBICConnectInfo DBICHashRef/;
71ef99d5 30use MooseX::Types::JSON qw(JSON);
31use MooseX::Types::Path::Class qw(Dir File);
32use Try::Tiny;
bb464677 33use JSON::Any;
8aa16237 34use namespace::autoclean;
bb464677 35
e81f0fe2 36
595cb2c7 37=head1 NAME
38
39DBIx::Class::Admin - Administration object for schemas
40
41=head1 SYNOPSIS
42
9c34993a 43 use DBIx::Class::Admin;
595cb2c7 44
9c34993a 45 # ddl manipulation
46 my $admin = DBIx::Class::Admin->new(
47 schema_class=> 'MY::Schema',
48 sql_dir=> $sql_dir,
49 connect_info => { dsn => $dsn, user => $user, password => $pass },
50 );
595cb2c7 51
9c34993a 52 # create SQLite sql
53 $admin->create('SQLite');
595cb2c7 54
9c34993a 55 # create SQL diff for an upgrade
56 $admin->create('SQLite', {} , "1.0");
595cb2c7 57
9c34993a 58 # upgrade a database
59 $admin->upgrade();
595cb2c7 60
9c34993a 61 # install a version for an unversioned schema
62 $admin->install("3.0");
9f3849c3 63
64=head1 Attributes
65
595cb2c7 66=head2 schema_class
9f3849c3 67
595cb2c7 68the class of the schema to load
e81f0fe2 69
595cb2c7 70=cut
e81f0fe2 71
9f3849c3 72has 'schema_class' => (
a705b175 73 is => 'ro',
71ef99d5 74 isa => Str,
9f3849c3 75);
76
e81f0fe2 77
595cb2c7 78=head2 schema
9f3849c3 79
595cb2c7 80A pre-connected schema object can be provided for manipulation
e81f0fe2 81
595cb2c7 82=cut
e81f0fe2 83
9f3849c3 84has 'schema' => (
a705b175 85 is => 'ro',
86 isa => 'DBIx::Class::Schema',
87 lazy_build => 1,
9f3849c3 88);
89
9f3849c3 90sub _build_schema {
a705b175 91 my ($self) = @_;
92 $self->ensure_class_loaded($self->schema_class);
9f3849c3 93
a705b175 94 $self->connect_info->[3]->{ignore_version} =1;
95 return $self->schema_class->connect(@{$self->connect_info()} ); # , $self->connect_info->[3], { ignore_version => 1} );
9f3849c3 96}
97
e81f0fe2 98
bb464677 99=head2 resultset
100
101a resultset from the schema to operate on
e81f0fe2 102
bb464677 103=cut
e81f0fe2 104
bb464677 105has 'resultset' => (
a705b175 106 is => 'rw',
107 isa => Str,
bb464677 108);
109
e81f0fe2 110
bb464677 111=head2 where
112
113a hash ref or json string to be used for identifying data to manipulate
e81f0fe2 114
bb464677 115=cut
116
117has 'where' => (
a705b175 118 is => 'rw',
a62dec82 119 isa => DBICHashRef,
a705b175 120 coerce => 1,
bb464677 121);
122
e81f0fe2 123
bb464677 124=head2 set
e81f0fe2 125
bb464677 126a hash ref or json string to be used for inserting or updating data
e81f0fe2 127
bb464677 128=cut
129
130has 'set' => (
a705b175 131 is => 'rw',
a62dec82 132 isa => DBICHashRef,
a705b175 133 coerce => 1,
bb464677 134);
135
e81f0fe2 136
bb464677 137=head2 attrs
e81f0fe2 138
bb464677 139a hash ref or json string to be used for passing additonal info to the ->search call
e81f0fe2 140
bb464677 141=cut
e81f0fe2 142
bb464677 143has 'attrs' => (
a705b175 144 is => 'rw',
a62dec82 145 isa => DBICHashRef,
a705b175 146 coerce => 1,
bb464677 147);
e81f0fe2 148
149
595cb2c7 150=head2 connect_info
151
152connect_info the arguments to provide to the connect call of the schema_class
bb464677 153
e81f0fe2 154=cut
bb464677 155
9f3849c3 156has 'connect_info' => (
a705b175 157 is => 'ro',
158 isa => DBICConnectInfo,
159 lazy_build => 1,
160 coerce => 1,
9f3849c3 161);
162
163sub _build_connect_info {
a705b175 164 my ($self) = @_;
165 return $self->_find_stanza($self->config, $self->config_stanza);
9f3849c3 166}
167
e81f0fe2 168
595cb2c7 169=head2 config_file
170
171config_file provide a config_file to read connect_info from, if this is provided
172config_stanze should also be provided to locate where the connect_info is in the config
173The config file should be in a format readable by Config::General
e81f0fe2 174
595cb2c7 175=cut
e81f0fe2 176
595cb2c7 177has config_file => (
a705b175 178 is => 'ro',
179 isa => File,
180 coerce => 1,
595cb2c7 181);
182
e81f0fe2 183
595cb2c7 184=head2 config_stanza
185
186config_stanza for use with config_file should be a '::' deliminated 'path' to the connection information
187designed for use with catalyst config files
e81f0fe2 188
595cb2c7 189=cut
e81f0fe2 190
595cb2c7 191has 'config_stanza' => (
a705b175 192 is => 'ro',
71ef99d5 193 isa => Str,
595cb2c7 194);
195
e81f0fe2 196
595cb2c7 197=head2 config
198
199Instead of loading from a file the configuration can be provided directly as a hash ref. Please note
200config_stanza will still be required.
e81f0fe2 201
595cb2c7 202=cut
e81f0fe2 203
9f3849c3 204has config => (
a705b175 205 is => 'ro',
a62dec82 206 isa => DBICHashRef,
a705b175 207 lazy_build => 1,
9f3849c3 208);
209
210sub _build_config {
a705b175 211 my ($self) = @_;
585072bb 212 try { require Config::Any } catch { $self->throw_exception( "Config::Any is required to parse the config file"); };
9f3849c3 213
a705b175 214 my $cfg = Config::Any->load_files ( {files => [$self->config_file], use_ext =>1, flatten_to_hash=>1});
9f3849c3 215
a705b175 216 # just grab the config from the config file
217 $cfg = $cfg->{$self->config_file};
218 return $cfg;
9f3849c3 219}
220
e81f0fe2 221
595cb2c7 222=head2 sql_dir
9f3849c3 223
595cb2c7 224The location where sql ddl files should be created or found for an upgrade.
e81f0fe2 225
595cb2c7 226=cut
e81f0fe2 227
9f3849c3 228has 'sql_dir' => (
a705b175 229 is => 'ro',
230 isa => Dir,
231 coerce => 1,
9f3849c3 232);
233
e81f0fe2 234
595cb2c7 235=head2 version
9f3849c3 236
595cb2c7 237Used for install, the version which will be 'installed' in the schema
e81f0fe2 238
595cb2c7 239=cut
e81f0fe2 240
9f3849c3 241has version => (
a705b175 242 is => 'rw',
71ef99d5 243 isa => Str,
9f3849c3 244);
245
e81f0fe2 246
595cb2c7 247=head2 preversion
248
249Previouse version of the schema to create an upgrade diff for, the full sql for that version of the sql must be in the sql_dir
e81f0fe2 250
595cb2c7 251=cut
e81f0fe2 252
9f3849c3 253has preversion => (
a705b175 254 is => 'rw',
71ef99d5 255 isa => Str,
9f3849c3 256);
257
e81f0fe2 258
595cb2c7 259=head2 force
260
261Try and force certain operations.
e81f0fe2 262
595cb2c7 263=cut
e81f0fe2 264
912e2d5a 265has force => (
a705b175 266 is => 'rw',
71ef99d5 267 isa => Bool,
912e2d5a 268);
269
e81f0fe2 270
c57f1cf7 271=head2 quiet
595cb2c7 272
273Be less verbose about actions
e81f0fe2 274
595cb2c7 275=cut
e81f0fe2 276
64c012f4 277has quiet => (
a705b175 278 is => 'rw',
71ef99d5 279 isa => Bool,
64c012f4 280);
281
912e2d5a 282has '_confirm' => (
a705b175 283 is => 'bare',
71ef99d5 284 isa => Bool,
912e2d5a 285);
286
e81f0fe2 287
595cb2c7 288=head1 METHODS
289
290=head2 create
291
292=over 4
293
294=item Arguments: $sqlt_type, \%sqlt_args, $preversion
295
296=back
297
298L<create> will generate sql for the supplied schema_class in sql_dir. The flavour of sql to
299generate can be controlled by suppling a sqlt_type which should be a L<SQL::Translator> name.
300
301Arguments for L<SQL::Translator> can be supplied in the sqlt_args hashref.
302
303Optional preversion can be supplied to generate a diff to be used by upgrade.
e81f0fe2 304
595cb2c7 305=cut
306
9f3849c3 307sub create {
a705b175 308 my ($self, $sqlt_type, $sqlt_args, $preversion) = @_;
595cb2c7 309
a705b175 310 $preversion ||= $self->preversion();
595cb2c7 311
a705b175 312 my $schema = $self->schema();
313 # create the dir if does not exist
314 $self->sql_dir->mkpath() if ( ! -d $self->sql_dir);
9f3849c3 315
a705b175 316 $schema->create_ddl_dir( $sqlt_type, (defined $schema->schema_version ? $schema->schema_version : ""), $self->sql_dir->stringify, $preversion, $sqlt_args );
9f3849c3 317}
318
e81f0fe2 319
595cb2c7 320=head2 upgrade
321
322=over 4
323
324=item Arguments: <none>
325
326=back
327
328upgrade will attempt to upgrade the connected database to the same version as the schema_class.
329B<MAKE SURE YOU BACKUP YOUR DB FIRST>
e81f0fe2 330
595cb2c7 331=cut
332
9f3849c3 333sub upgrade {
a705b175 334 my ($self) = @_;
335 my $schema = $self->schema();
336 if (!$schema->get_db_version()) {
337 # schema is unversioned
585072bb 338 $self->throw_exception ("could not determin current schema version, please either install or deploy");
a705b175 339 } else {
340 my $ret = $schema->upgrade();
341 return $ret;
342 }
9f3849c3 343}
344
e81f0fe2 345
595cb2c7 346=head2 install
347
348=over 4
349
350=item Arguments: $version
351
352=back
353
354install is here to help when you want to move to L<DBIx::Class::Schema::Versioned> and have an existing
355database. install will take a version and add the version tracking tables and 'install' the version. No
356further ddl modification takes place. Setting the force attribute to a true value will allow overriding of
357already versioned databases.
e81f0fe2 358
595cb2c7 359=cut
e81f0fe2 360
9f3849c3 361sub install {
a705b175 362 my ($self, $version) = @_;
363
364 my $schema = $self->schema();
365 $version ||= $self->version();
366 if (!$schema->get_db_version() ) {
367 # schema is unversioned
368 print "Going to install schema version\n";
369 my $ret = $schema->install($version);
370 print "retun is $ret\n";
371 }
372 elsif ($schema->get_db_version() and $self->force ) {
585072bb 373 carp "Forcing install may not be a good idea";
a705b175 374 if($self->_confirm() ) {
a705b175 375 $self->schema->_set_db_version({ version => $version});
9c34993a 376 }
a705b175 377 }
378 else {
585072bb 379 $self->throw_exception ("schema already has a version not installing, try upgrade instead");
a705b175 380 }
9f3849c3 381
382}
383
e81f0fe2 384
595cb2c7 385=head2 deploy
386
387=over 4
388
389=item Arguments: $args
390
391=back
392
393deploy will create the schema at the connected database. C<$args> are passed straight to
e81f0fe2 394L<DBIx::Class::Schema/deploy>.
395
595cb2c7 396=cut
e81f0fe2 397
9f3849c3 398sub deploy {
a705b175 399 my ($self, $args) = @_;
400 my $schema = $self->schema();
401 if (!$schema->get_db_version() ) {
402 # schema is unversioned
403 $schema->deploy( $args, $self->sql_dir)
585072bb 404 or $self->throw_exception ("could not deploy schema");
a705b175 405 } else {
585072bb 406 $self->throw_exception("there already is a database with a version here, try upgrade instead");
a705b175 407 }
9f3849c3 408}
409
bb464677 410=head2 insert
595cb2c7 411
412=over 4
413
414=item Arguments: $rs, $set
415
416=back
417
bb464677 418insert takes the name of a resultset from the schema_class and a hashref of data to insert
595cb2c7 419into that resultset
420
421=cut
e81f0fe2 422
bb464677 423sub insert {
a705b175 424 my ($self, $rs, $set) = @_;
bb464677 425
a705b175 426 $rs ||= $self->resultset();
427 $set ||= $self->set();
428 my $resultset = $self->schema->resultset($rs);
429 my $obj = $resultset->create( $set );
430 print ''.ref($resultset).' ID: '.join(',',$obj->id())."\n" if (!$self->quiet);
9f3849c3 431}
432
595cb2c7 433
bb464677 434=head2 update
595cb2c7 435
e81f0fe2 436=over 4
595cb2c7 437
438=item Arguments: $rs, $set, $where
439
440=back
441
e81f0fe2 442update takes the name of a resultset from the schema_class, a hashref of data to update and
443a where hash used to form the search for the rows to update.
444
595cb2c7 445=cut
e81f0fe2 446
bb464677 447sub update {
a705b175 448 my ($self, $rs, $set, $where) = @_;
882931aa 449
a705b175 450 $rs ||= $self->resultset();
451 $where ||= $self->where();
452 $set ||= $self->set();
453 my $resultset = $self->schema->resultset($rs);
454 $resultset = $resultset->search( ($where||{}) );
882931aa 455
a705b175 456 my $count = $resultset->count();
457 print "This action will modify $count ".ref($resultset)." records.\n" if (!$self->quiet);
882931aa 458
a705b175 459 if ( $self->force || $self->_confirm() ) {
460 $resultset->update_all( $set );
461 }
9f3849c3 462}
463
e81f0fe2 464
bb464677 465=head2 delete
595cb2c7 466
467=over 4
468
469=item Arguments: $rs, $where, $attrs
470
471=back
472
e81f0fe2 473delete takes the name of a resultset from the schema_class, a where hashref and a attrs to pass to ->search.
595cb2c7 474The found data is deleted and cannot be recovered.
e81f0fe2 475
595cb2c7 476=cut
e81f0fe2 477
bb464677 478sub delete {
a705b175 479 my ($self, $rs, $where, $attrs) = @_;
9f3849c3 480
a705b175 481 $rs ||= $self->resultset();
482 $where ||= $self->where();
483 $attrs ||= $self->attrs();
484 my $resultset = $self->schema->resultset($rs);
485 $resultset = $resultset->search( ($where||{}), ($attrs||()) );
9f3849c3 486
a705b175 487 my $count = $resultset->count();
488 print "This action will delete $count ".ref($resultset)." records.\n" if (!$self->quiet);
9f3849c3 489
a705b175 490 if ( $self->force || $self->_confirm() ) {
491 $resultset->delete_all();
492 }
9f3849c3 493}
494
e81f0fe2 495
bb464677 496=head2 select
595cb2c7 497
498=over 4
499
500=item Arguments: $rs, $where, $attrs
501
502=back
503
bb464677 504select takes the name of a resultset from the schema_class, a where hashref and a attrs to pass to ->search.
595cb2c7 505The found data is returned in a array ref where the first row will be the columns list.
506
507=cut
e81f0fe2 508
bb464677 509sub select {
a705b175 510 my ($self, $rs, $where, $attrs) = @_;
511
512 $rs ||= $self->resultset();
513 $where ||= $self->where();
514 $attrs ||= $self->attrs();
515 my $resultset = $self->schema->resultset($rs);
516 $resultset = $resultset->search( ($where||{}), ($attrs||()) );
517
518 my @data;
519 my @columns = $resultset->result_source->columns();
520 push @data, [@columns];#
521
522 while (my $row = $resultset->next()) {
523 my @fields;
524 foreach my $column (@columns) {
525 push( @fields, $row->get_column($column) );
9c34993a 526 }
a705b175 527 push @data, [@fields];
528 }
9c34993a 529
a705b175 530 return \@data;
9f3849c3 531}
532
595cb2c7 533sub _confirm {
a705b175 534 my ($self) = @_;
535 print "Are you sure you want to do this? (type YES to confirm) \n";
536 # mainly here for testing
537 return 1 if ($self->meta->get_attribute('_confirm')->get_value($self));
538 my $response = <STDIN>;
539 return 1 if ($response=~/^YES/);
540 return;
9f3849c3 541}
542
595cb2c7 543sub _find_stanza {
a705b175 544 my ($self, $cfg, $stanza) = @_;
545 my @path = split /::/, $stanza;
546 while (my $path = shift @path) {
547 if (exists $cfg->{$path}) {
548 $cfg = $cfg->{$path};
549 }
550 else {
585072bb 551 $self->throw_exception("could not find $stanza in config, $path did not seem to exist");
9c34993a 552 }
a705b175 553 }
554 return $cfg;
595cb2c7 555}
bb464677 556
e81f0fe2 557=head1 AUTHORS
bb464677 558
e81f0fe2 559See L<DBIx::Class/CONTRIBUTORS>.
bb464677 560
561=head1 LICENSE
562
e81f0fe2 563You may distribute this code under the same terms as Perl itself
564
bb464677 565=cut
e81f0fe2 566
9f3849c3 5671;