Fix SQLite _extra_column_info for new DBD::SQLite which ->can('column_info')
[dbsrgits/DBIx-Class-Schema-Loader.git] / lib / DBIx / Class / Schema / Loader / Base.pm
CommitLineData
996be9ee 1package DBIx::Class::Schema::Loader::Base;
2
3use strict;
4use warnings;
5use base qw/Class::Accessor::Fast/;
6use Class::C3;
fa994d3c 7use Carp::Clan qw/^DBIx::Class/;
996be9ee 8use UNIVERSAL::require;
9use DBIx::Class::Schema::Loader::RelBuilder;
10use Data::Dump qw/ dump /;
11use POSIX qw//;
dd03ee1a 12use File::Spec qw//;
419a2eeb 13use Cwd qw//;
7cab3ab7 14use Digest::MD5 qw//;
22270947 15use Lingua::EN::Inflect::Number qw//;
af31090c 16use File::Temp qw//;
17use Class::Unload;
996be9ee 18require DBIx::Class;
19
41ef22da 20our $VERSION = '0.04999_06';
32f784fc 21
996be9ee 22__PACKAGE__->mk_ro_accessors(qw/
23 schema
24 schema_class
25
26 exclude
27 constraint
28 additional_classes
29 additional_base_classes
30 left_base_classes
31 components
32 resultset_components
59cfa251 33 skip_relationships
996be9ee 34 moniker_map
35 inflect_singular
36 inflect_plural
37 debug
38 dump_directory
d65cda9e 39 dump_overwrite
28b4691d 40 really_erase_my_files
f44ecc2f 41 use_namespaces
42 result_namespace
43 resultset_namespace
44 default_resultset_class
9c9c2f2b 45 schema_base_class
46 result_base_class
996be9ee 47
996be9ee 48 db_schema
49 _tables
50 classes
51 monikers
52 /);
53
54=head1 NAME
55
56DBIx::Class::Schema::Loader::Base - Base DBIx::Class::Schema::Loader Implementation.
57
58=head1 SYNOPSIS
59
60See L<DBIx::Class::Schema::Loader>
61
62=head1 DESCRIPTION
63
64This is the base class for the storage-specific C<DBIx::Class::Schema::*>
65classes, and implements the common functionality between them.
66
67=head1 CONSTRUCTOR OPTIONS
68
69These constructor options are the base options for
29ddb54c 70L<DBIx::Class::Schema::Loader/loader_options>. Available constructor options are:
3953cbee 71
59cfa251 72=head2 skip_relationships
996be9ee 73
59cfa251 74Skip setting up relationships. The default is to attempt the loading
75of relationships.
996be9ee 76
77=head2 debug
78
79If set to true, each constructive L<DBIx::Class> statement the loader
80decides to execute will be C<warn>-ed before execution.
81
d65cda9e 82=head2 db_schema
83
84Set the name of the schema to load (schema in the sense that your database
85vendor means it). Does not currently support loading more than one schema
86name.
87
996be9ee 88=head2 constraint
89
90Only load tables matching regex. Best specified as a qr// regex.
91
92=head2 exclude
93
94Exclude tables matching regex. Best specified as a qr// regex.
95
96=head2 moniker_map
97
8f9d7ce5 98Overrides the default table name to moniker translation. Can be either
99a hashref of table keys and moniker values, or a coderef for a translator
996be9ee 100function taking a single scalar table name argument and returning
101a scalar moniker. If the hash entry does not exist, or the function
102returns a false value, the code falls back to default behavior
103for that table name.
104
105The default behavior is: C<join '', map ucfirst, split /[\W_]+/, lc $table>,
106which is to say: lowercase everything, split up the table name into chunks
107anywhere a non-alpha-numeric character occurs, change the case of first letter
108of each chunk to upper case, and put the chunks back together. Examples:
109
110 Table Name | Moniker Name
111 ---------------------------
112 luser | Luser
113 luser_group | LuserGroup
114 luser-opts | LuserOpts
115
116=head2 inflect_plural
117
118Just like L</moniker_map> above (can be hash/code-ref, falls back to default
119if hash key does not exist or coderef returns false), but acts as a map
120for pluralizing relationship names. The default behavior is to utilize
121L<Lingua::EN::Inflect::Number/to_PL>.
122
123=head2 inflect_singular
124
125As L</inflect_plural> above, but for singularizing relationship names.
126Default behavior is to utilize L<Lingua::EN::Inflect::Number/to_S>.
127
9c9c2f2b 128=head2 schema_base_class
129
130Base class for your schema classes. Defaults to 'DBIx::Class::Schema'.
131
132=head2 result_base_class
133
134Base class for your table classes (aka result classes). Defaults to 'DBIx::Class'.
135
996be9ee 136=head2 additional_base_classes
137
138List of additional base classes all of your table classes will use.
139
140=head2 left_base_classes
141
142List of additional base classes all of your table classes will use
143that need to be leftmost.
144
145=head2 additional_classes
146
147List of additional classes which all of your table classes will use.
148
149=head2 components
150
151List of additional components to be loaded into all of your table
152classes. A good example would be C<ResultSetManager>.
153
154=head2 resultset_components
155
8f9d7ce5 156List of additional ResultSet components to be loaded into your table
996be9ee 157classes. A good example would be C<AlwaysRS>. Component
158C<ResultSetManager> will be automatically added to the above
159C<components> list if this option is set.
160
f44ecc2f 161=head2 use_namespaces
162
163Generate result class names suitable for
164L<DBIx::Class::Schema/load_namespaces> and call that instead of
165L<DBIx::Class::Schema/load_classes>. When using this option you can also
166specify any of the options for C<load_namespaces> (i.e. C<result_namespace>,
167C<resultset_namespace>, C<default_resultset_class>), and they will be added
168to the call (and the generated result class names adjusted appropriately).
169
996be9ee 170=head2 dump_directory
171
172This option is designed to be a tool to help you transition from this
173loader to a manually-defined schema when you decide it's time to do so.
174
175The value of this option is a perl libdir pathname. Within
176that directory this module will create a baseline manual
177L<DBIx::Class::Schema> module set, based on what it creates at runtime
178in memory.
179
180The created schema class will have the same classname as the one on
181which you are setting this option (and the ResultSource classes will be
7cab3ab7 182based on this name as well).
996be9ee 183
8f9d7ce5 184Normally you wouldn't hard-code this setting in your schema class, as it
996be9ee 185is meant for one-time manual usage.
186
187See L<DBIx::Class::Schema::Loader/dump_to_dir> for examples of the
188recommended way to access this functionality.
189
d65cda9e 190=head2 dump_overwrite
191
28b4691d 192Deprecated. See L</really_erase_my_files> below, which does *not* mean
193the same thing as the old C<dump_overwrite> setting from previous releases.
194
195=head2 really_erase_my_files
196
7cab3ab7 197Default false. If true, Loader will unconditionally delete any existing
198files before creating the new ones from scratch when dumping a schema to disk.
199
200The default behavior is instead to only replace the top portion of the
201file, up to and including the final stanza which contains
202C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!>
203leaving any customizations you placed after that as they were.
204
28b4691d 205When C<really_erase_my_files> is not set, if the output file already exists,
7cab3ab7 206but the aforementioned final stanza is not found, or the checksum
207contained there does not match the generated contents, Loader will
208croak and not touch the file.
d65cda9e 209
28b4691d 210You should really be using version control on your schema classes (and all
211of the rest of your code for that matter). Don't blame me if a bug in this
212code wipes something out when it shouldn't have, you've been warned.
213
996be9ee 214=head1 METHODS
215
216None of these methods are intended for direct invocation by regular
217users of L<DBIx::Class::Schema::Loader>. Anything you can find here
218can also be found via standard L<DBIx::Class::Schema> methods somehow.
219
220=cut
221
222# ensure that a peice of object data is a valid arrayref, creating
223# an empty one or encapsulating whatever's there.
224sub _ensure_arrayref {
225 my $self = shift;
226
227 foreach (@_) {
228 $self->{$_} ||= [];
229 $self->{$_} = [ $self->{$_} ]
230 unless ref $self->{$_} eq 'ARRAY';
231 }
232}
233
234=head2 new
235
236Constructor for L<DBIx::Class::Schema::Loader::Base>, used internally
237by L<DBIx::Class::Schema::Loader>.
238
239=cut
240
241sub new {
242 my ( $class, %args ) = @_;
243
244 my $self = { %args };
245
246 bless $self => $class;
247
996be9ee 248 $self->_ensure_arrayref(qw/additional_classes
249 additional_base_classes
250 left_base_classes
251 components
252 resultset_components
253 /);
254
255 push(@{$self->{components}}, 'ResultSetManager')
256 if @{$self->{resultset_components}};
257
258 $self->{monikers} = {};
259 $self->{classes} = {};
260
996be9ee 261 $self->{schema_class} ||= ( ref $self->{schema} || $self->{schema} );
262 $self->{schema} ||= $self->{schema_class};
263
28b4691d 264 croak "dump_overwrite is deprecated. Please read the"
265 . " DBIx::Class::Schema::Loader::Base documentation"
266 if $self->{dump_overwrite};
267
af31090c 268 $self->{dynamic} = ! $self->{dump_directory};
269 $self->{dump_directory} ||= File::Temp::tempdir( 'dbicXXXX',
270 TMPDIR => 1,
271 CLEANUP => 1,
272 );
273
e8ad6491 274 $self->{relbuilder} = DBIx::Class::Schema::Loader::RelBuilder->new(
275 $self->schema_class, $self->inflect_plural, $self->inflect_singular
276 ) if !$self->{skip_relationships};
277
996be9ee 278 $self;
279}
280
419a2eeb 281sub _find_file_in_inc {
282 my ($self, $file) = @_;
283
284 foreach my $prefix (@INC) {
af31090c 285 my $fullpath = File::Spec->catfile($prefix, $file);
286 return $fullpath if -f $fullpath
287 and Cwd::abs_path($fullpath) ne
288 Cwd::abs_path(File::Spec->catfile($self->dump_directory, $file)) || '';
419a2eeb 289 }
290
291 return;
292}
293
996be9ee 294sub _load_external {
f96ef30f 295 my ($self, $class) = @_;
296
297 my $class_path = $class;
298 $class_path =~ s{::}{/}g;
299 $class_path .= '.pm';
300
af31090c 301 my $real_inc_path = $self->_find_file_in_inc($class_path);
f96ef30f 302
af31090c 303 return if !$real_inc_path;
f96ef30f 304
305 # If we make it to here, we loaded an external definition
306 warn qq/# Loaded external class definition for '$class'\n/
307 if $self->debug;
308
f96ef30f 309 croak 'Failed to locate actual external module file for '
310 . "'$class'"
311 if !$real_inc_path;
312 open(my $fh, '<', $real_inc_path)
313 or croak "Failed to open '$real_inc_path' for reading: $!";
314 $self->_ext_stmt($class,
565ca24d 315 qq|# These lines were loaded from '$real_inc_path' found in \@INC.\n|
316 .qq|# They are now part of the custom portion of this file\n|
317 .qq|# for you to hand-edit. If you do not either delete\n|
318 .qq|# this section or remove that file from \@INC, this section\n|
319 .qq|# will be repeated redundantly when you re-create this\n|
320 .qq|# file again via Loader!\n|
f96ef30f 321 );
322 while(<$fh>) {
323 chomp;
324 $self->_ext_stmt($class, $_);
996be9ee 325 }
f96ef30f 326 $self->_ext_stmt($class,
70b72fab 327 qq|# End of lines loaded from '$real_inc_path' |
f96ef30f 328 );
329 close($fh)
330 or croak "Failed to close $real_inc_path: $!";
996be9ee 331}
332
333=head2 load
334
335Does the actual schema-construction work.
336
337=cut
338
339sub load {
340 my $self = shift;
341
b97c2c1e 342 $self->_load_tables($self->_tables_list);
343}
344
345=head2 rescan
346
a60b5b8d 347Arguments: schema
348
b97c2c1e 349Rescan the database for newly added tables. Does
a60b5b8d 350not process drops or changes. Returns a list of
351the newly added table monikers.
352
353The schema argument should be the schema class
354or object to be affected. It should probably
355be derived from the original schema_class used
356during L</load>.
b97c2c1e 357
358=cut
359
360sub rescan {
a60b5b8d 361 my ($self, $schema) = @_;
362
363 $self->{schema} = $schema;
b97c2c1e 364
365 my @created;
366 my @current = $self->_tables_list;
367 foreach my $table ($self->_tables_list) {
368 if(!exists $self->{_tables}->{$table}) {
369 push(@created, $table);
370 }
371 }
372
c39e3507 373 my $loaded = $self->_load_tables(@created);
a60b5b8d 374
c39e3507 375 return map { $self->monikers->{$_} } @$loaded;
b97c2c1e 376}
377
378sub _load_tables {
379 my ($self, @tables) = @_;
380
f96ef30f 381 # First, use _tables_list with constraint and exclude
382 # to get a list of tables to operate on
383
384 my $constraint = $self->constraint;
385 my $exclude = $self->exclude;
f96ef30f 386
b97c2c1e 387 @tables = grep { /$constraint/ } @tables if $constraint;
388 @tables = grep { ! /$exclude/ } @tables if $exclude;
f96ef30f 389
b97c2c1e 390 # Save the new tables to the tables list
a60b5b8d 391 foreach (@tables) {
392 $self->{_tables}->{$_} = 1;
393 }
f96ef30f 394
af31090c 395 $self->_make_src_class($_) for @tables;
f96ef30f 396 $self->_setup_src_meta($_) for @tables;
397
e8ad6491 398 if(!$self->skip_relationships) {
181cc907 399 # The relationship loader needs a working schema
af31090c 400 $self->{quiet} = 1;
181cc907 401 $self->_reload_classes(@tables);
e8ad6491 402 $self->_load_relationships($_) for @tables;
af31090c 403 $self->{quiet} = 0;
e8ad6491 404 }
405
f96ef30f 406 $self->_load_external($_)
75451704 407 for map { $self->classes->{$_} } @tables;
f96ef30f 408
181cc907 409 $self->_reload_classes(@tables);
996be9ee 410
5223f24a 411 # Drop temporary cache
412 delete $self->{_cache};
413
c39e3507 414 return \@tables;
996be9ee 415}
416
af31090c 417sub _reload_classes {
181cc907 418 my ($self, @tables) = @_;
419
420 $self->_dump_to_dir(map { $self->classes->{$_} } @tables);
e9b8719e 421
422 unshift @INC, $self->dump_directory;
af31090c 423
181cc907 424 for my $table (@tables) {
425 my $moniker = $self->monikers->{$table};
426 my $class = $self->classes->{$table};
0ae6b65d 427
428 {
429 no warnings 'redefine';
430 local *Class::C3::reinitialize = sub {};
431 use warnings;
432
433 if ( Class::Unload->unload( $class ) ) {
434 my $resultset_class = ref $self->schema->resultset($moniker);
435 Class::Unload->unload( $resultset_class )
436 if $resultset_class ne 'DBIx::Class::ResultSet';
437 }
438 $class->require or die "Can't load $class: $@";
af31090c 439 }
af31090c 440
441 $self->schema_class->register_class($moniker, $class);
442 $self->schema->register_class($moniker, $class)
443 if $self->schema ne $self->schema_class;
444 }
445}
446
996be9ee 447sub _get_dump_filename {
448 my ($self, $class) = (@_);
449
450 $class =~ s{::}{/}g;
451 return $self->dump_directory . q{/} . $class . q{.pm};
452}
453
454sub _ensure_dump_subdirs {
455 my ($self, $class) = (@_);
456
457 my @name_parts = split(/::/, $class);
dd03ee1a 458 pop @name_parts; # we don't care about the very last element,
459 # which is a filename
460
996be9ee 461 my $dir = $self->dump_directory;
7cab3ab7 462 while (1) {
463 if(!-d $dir) {
25328cc4 464 mkdir($dir) or croak "mkdir('$dir') failed: $!";
996be9ee 465 }
7cab3ab7 466 last if !@name_parts;
467 $dir = File::Spec->catdir($dir, shift @name_parts);
996be9ee 468 }
469}
470
471sub _dump_to_dir {
af31090c 472 my ($self, @classes) = @_;
996be9ee 473
fc2b71fd 474 my $schema_class = $self->schema_class;
9c9c2f2b 475 my $schema_base_class = $self->schema_base_class || 'DBIx::Class::Schema';
996be9ee 476
e9b8719e 477 my $target_dir = $self->dump_directory;
af31090c 478 warn "Dumping manual schema for $schema_class to directory $target_dir ...\n"
479 unless $self->{dynamic} or $self->{quiet};
996be9ee 480
7cab3ab7 481 my $schema_text =
482 qq|package $schema_class;\n\n|
483 . qq|use strict;\nuse warnings;\n\n|
9c9c2f2b 484 . qq|use base '$schema_base_class';\n\n|;
f44ecc2f 485
f44ecc2f 486 if ($self->use_namespaces) {
487 $schema_text .= qq|__PACKAGE__->load_namespaces|;
488 my $namespace_options;
489 for my $attr (qw(result_namespace
490 resultset_namespace
491 default_resultset_class)) {
492 if ($self->$attr) {
493 $namespace_options .= qq| $attr => '| . $self->$attr . qq|',\n|
494 }
495 }
496 $schema_text .= qq|(\n$namespace_options)| if $namespace_options;
497 $schema_text .= qq|;\n|;
498 }
499 else {
500 $schema_text .= qq|__PACKAGE__->load_classes;\n|;
f44ecc2f 501 }
996be9ee 502
7cab3ab7 503 $self->_write_classfile($schema_class, $schema_text);
996be9ee 504
9c9c2f2b 505 my $result_base_class = $self->result_base_class || 'DBIx::Class';
506
af31090c 507 foreach my $src_class (@classes) {
7cab3ab7 508 my $src_text =
509 qq|package $src_class;\n\n|
510 . qq|use strict;\nuse warnings;\n\n|
9c9c2f2b 511 . qq|use base '$result_base_class';\n\n|;
996be9ee 512
7cab3ab7 513 $self->_write_classfile($src_class, $src_text);
02356864 514 }
996be9ee 515
af31090c 516 warn "Schema dump completed.\n" unless $self->{dynamic} or $self->{quiet};
517
7cab3ab7 518}
519
520sub _write_classfile {
521 my ($self, $class, $text) = @_;
522
523 my $filename = $self->_get_dump_filename($class);
524 $self->_ensure_dump_subdirs($class);
525
28b4691d 526 if (-f $filename && $self->really_erase_my_files) {
7cab3ab7 527 warn "Deleting existing file '$filename' due to "
af31090c 528 . "'really_erase_my_files' setting\n" unless $self->{quiet};
7cab3ab7 529 unlink($filename);
530 }
531
7cab3ab7 532 $text .= qq|$_\n|
533 for @{$self->{_dump_storage}->{$class} || []};
534
535 $text .= qq|\n\n# Created by DBIx::Class::Schema::Loader|
536 . qq| v| . $DBIx::Class::Schema::Loader::VERSION
537 . q| @ | . POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
538 . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
539
540 open(my $fh, '>', $filename)
541 or croak "Cannot open '$filename' for writing: $!";
542
543 # Write the top half and its MD5 sum
a4476f41 544 print $fh $text . Digest::MD5::md5_base64($text) . "\n";
7cab3ab7 545
546 # Write out anything loaded via external partial class file in @INC
547 print $fh qq|$_\n|
548 for @{$self->{_ext_storage}->{$class} || []};
549
1eea4fb1 550 # Write out any custom content the user has added
551 my $custom_content = $self->_get_custom_content($class, $filename);
552
553 $custom_content ||= qq|\n\n# You can replace this text with custom|
554 . qq| content, and it will be preserved on regeneration|
555 . qq|\n1;\n|;
556
7cab3ab7 557 print $fh $custom_content;
558
559 close($fh)
e9b8719e 560 or croak "Error closing '$filename': $!";
7cab3ab7 561}
562
563sub _get_custom_content {
564 my ($self, $class, $filename) = @_;
565
566 return if ! -f $filename;
567 open(my $fh, '<', $filename)
568 or croak "Cannot open '$filename' for reading: $!";
569
570 my $mark_re =
419a2eeb 571 qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
7cab3ab7 572
573 my $found = 0;
574 my $buffer = '';
575 while(<$fh>) {
576 if(!$found && /$mark_re/) {
577 $found = 1;
578 $buffer .= $1;
7cab3ab7 579 croak "Checksum mismatch in '$filename'"
419a2eeb 580 if Digest::MD5::md5_base64($buffer) ne $2;
7cab3ab7 581
582 $buffer = '';
583 }
584 else {
585 $buffer .= $_;
586 }
996be9ee 587 }
588
28b4691d 589 croak "Cannot not overwrite '$filename' without 'really_erase_my_files',"
419a2eeb 590 . " it does not appear to have been generated by Loader"
5ef3c771 591 if !$found;
592
7cab3ab7 593 return $buffer;
996be9ee 594}
595
596sub _use {
597 my $self = shift;
598 my $target = shift;
599
600 foreach (@_) {
cb54990b 601 warn "$target: use $_;" if $self->debug;
996be9ee 602 $self->_raw_stmt($target, "use $_;");
996be9ee 603 }
604}
605
606sub _inject {
607 my $self = shift;
608 my $target = shift;
609 my $schema_class = $self->schema_class;
610
af31090c 611 my $blist = join(q{ }, @_);
612 warn "$target: use base qw/ $blist /;" if $self->debug && @_;
613 $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
996be9ee 614}
615
f96ef30f 616# Create class with applicable bases, setup monikers, etc
617sub _make_src_class {
618 my ($self, $table) = @_;
996be9ee 619
a13b2803 620 my $schema = $self->schema;
621 my $schema_class = $self->schema_class;
996be9ee 622
f96ef30f 623 my $table_moniker = $self->_table2moniker($table);
f44ecc2f 624 my @result_namespace = ($schema_class);
625 if ($self->use_namespaces) {
626 my $result_namespace = $self->result_namespace || 'Result';
627 if ($result_namespace =~ /^\+(.*)/) {
628 # Fully qualified namespace
629 @result_namespace = ($1)
630 }
631 else {
632 # Relative namespace
633 push @result_namespace, $result_namespace;
634 }
635 }
636 my $table_class = join(q{::}, @result_namespace, $table_moniker);
996be9ee 637
f96ef30f 638 my $table_normalized = lc $table;
639 $self->classes->{$table} = $table_class;
640 $self->classes->{$table_normalized} = $table_class;
641 $self->monikers->{$table} = $table_moniker;
642 $self->monikers->{$table_normalized} = $table_moniker;
996be9ee 643
f96ef30f 644 $self->_use ($table_class, @{$self->additional_classes});
af31090c 645 $self->_inject($table_class, @{$self->left_base_classes});
996be9ee 646
605fcea8 647 $self->_dbic_stmt($table_class, 'load_components', @{$self->components}, 'Core');
996be9ee 648
f96ef30f 649 $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
650 if @{$self->resultset_components};
af31090c 651 $self->_inject($table_class, @{$self->additional_base_classes});
f96ef30f 652}
996be9ee 653
af31090c 654# Set up metadata (cols, pks, etc)
f96ef30f 655sub _setup_src_meta {
656 my ($self, $table) = @_;
996be9ee 657
f96ef30f 658 my $schema = $self->schema;
659 my $schema_class = $self->schema_class;
a13b2803 660
f96ef30f 661 my $table_class = $self->classes->{$table};
662 my $table_moniker = $self->monikers->{$table};
996be9ee 663
f96ef30f 664 $self->_dbic_stmt($table_class,'table',$table);
996be9ee 665
f96ef30f 666 my $cols = $self->_table_columns($table);
667 my $col_info;
668 eval { $col_info = $self->_columns_info_for($table) };
669 if($@) {
670 $self->_dbic_stmt($table_class,'add_columns',@$cols);
671 }
672 else {
673 my %col_info_lc = map { lc($_), $col_info->{$_} } keys %$col_info;
e7213f4f 674 my $fks = $self->_table_fk_info($table);
675 for my $fkdef (@$fks) {
676 for my $col (@{ $fkdef->{local_columns} }) {
677 $col_info_lc{$col}->{is_foreign_key} = 1;
678 }
679 }
f96ef30f 680 $self->_dbic_stmt(
681 $table_class,
682 'add_columns',
683 map { $_, ($col_info_lc{$_}||{}) } @$cols
684 );
996be9ee 685 }
686
d70c335f 687 my %uniq_tag; # used to eliminate duplicate uniqs
688
f96ef30f 689 my $pks = $self->_table_pk_info($table) || [];
690 @$pks ? $self->_dbic_stmt($table_class,'set_primary_key',@$pks)
691 : carp("$table has no primary key");
d70c335f 692 $uniq_tag{ join("\0", @$pks) }++ if @$pks; # pk is a uniq
996be9ee 693
f96ef30f 694 my $uniqs = $self->_table_uniq_info($table) || [];
d70c335f 695 for (@$uniqs) {
696 my ($name, $cols) = @$_;
697 next if $uniq_tag{ join("\0", @$cols) }++; # skip duplicates
698 $self->_dbic_stmt($table_class,'add_unique_constraint', $name, $cols);
699 }
700
996be9ee 701}
702
703=head2 tables
704
705Returns a sorted list of loaded tables, using the original database table
706names.
707
708=cut
709
710sub tables {
711 my $self = shift;
712
b97c2c1e 713 return keys %{$self->_tables};
996be9ee 714}
715
716# Make a moniker from a table
717sub _table2moniker {
718 my ( $self, $table ) = @_;
719
720 my $moniker;
721
722 if( ref $self->moniker_map eq 'HASH' ) {
723 $moniker = $self->moniker_map->{$table};
724 }
725 elsif( ref $self->moniker_map eq 'CODE' ) {
726 $moniker = $self->moniker_map->($table);
727 }
728
22270947 729 $moniker ||= join '', map ucfirst, split /[\W_]+/,
730 Lingua::EN::Inflect::Number::to_S(lc $table);
996be9ee 731
732 return $moniker;
733}
734
735sub _load_relationships {
e8ad6491 736 my ($self, $table) = @_;
996be9ee 737
e8ad6491 738 my $tbl_fk_info = $self->_table_fk_info($table);
739 foreach my $fkdef (@$tbl_fk_info) {
740 $fkdef->{remote_source} =
741 $self->monikers->{delete $fkdef->{remote_table}};
996be9ee 742 }
26f1c8c9 743 my $tbl_uniq_info = $self->_table_uniq_info($table);
996be9ee 744
e8ad6491 745 my $local_moniker = $self->monikers->{$table};
26f1c8c9 746 my $rel_stmts = $self->{relbuilder}->generate_code($local_moniker, $tbl_fk_info, $tbl_uniq_info);
996be9ee 747
996be9ee 748 foreach my $src_class (sort keys %$rel_stmts) {
749 my $src_stmts = $rel_stmts->{$src_class};
750 foreach my $stmt (@$src_stmts) {
751 $self->_dbic_stmt($src_class,$stmt->{method},@{$stmt->{args}});
752 }
753 }
754}
755
756# Overload these in driver class:
757
758# Returns an arrayref of column names
759sub _table_columns { croak "ABSTRACT METHOD" }
760
761# Returns arrayref of pk col names
762sub _table_pk_info { croak "ABSTRACT METHOD" }
763
764# Returns an arrayref of uniqs [ [ foo => [ col1, col2 ] ], [ bar => [ ... ] ] ]
765sub _table_uniq_info { croak "ABSTRACT METHOD" }
766
767# Returns an arrayref of foreign key constraints, each
768# being a hashref with 3 keys:
769# local_columns (arrayref), remote_columns (arrayref), remote_table
770sub _table_fk_info { croak "ABSTRACT METHOD" }
771
772# Returns an array of lower case table names
773sub _tables_list { croak "ABSTRACT METHOD" }
774
775# Execute a constructive DBIC class method, with debug/dump_to_dir hooks.
776sub _dbic_stmt {
777 my $self = shift;
778 my $class = shift;
779 my $method = shift;
780
996be9ee 781 my $args = dump(@_);
782 $args = '(' . $args . ')' if @_ < 2;
783 my $stmt = $method . $args . q{;};
784
785 warn qq|$class\->$stmt\n| if $self->debug;
996be9ee 786 $self->_raw_stmt($class, '__PACKAGE__->' . $stmt);
787}
788
789# Store a raw source line for a class (for dumping purposes)
790sub _raw_stmt {
791 my ($self, $class, $stmt) = @_;
af31090c 792 push(@{$self->{_dump_storage}->{$class}}, $stmt);
996be9ee 793}
794
7cab3ab7 795# Like above, but separately for the externally loaded stuff
796sub _ext_stmt {
797 my ($self, $class, $stmt) = @_;
af31090c 798 push(@{$self->{_ext_storage}->{$class}}, $stmt);
7cab3ab7 799}
800
996be9ee 801=head2 monikers
802
8f9d7ce5 803Returns a hashref of loaded table to moniker mappings. There will
996be9ee 804be two entries for each table, the original name and the "normalized"
805name, in the case that the two are different (such as databases
806that like uppercase table names, or preserve your original mixed-case
807definitions, or what-have-you).
808
809=head2 classes
810
8f9d7ce5 811Returns a hashref of table to class mappings. In some cases it will
996be9ee 812contain multiple entries per table for the original and normalized table
813names, as above in L</monikers>.
814
815=head1 SEE ALSO
816
817L<DBIx::Class::Schema::Loader>
818
819=cut
820
8211;