Now that we always dump to disk, don't bother testing twice.
[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
17ca645f 532 my $custom_content = $self->_get_custom_content($class, $filename);
533 $custom_content ||= qq|\n\n# You can replace this text with custom|
534 . qq| content, and it will be preserved on regeneration|
535 . qq|\n1;\n|;
536
7cab3ab7 537 $text .= qq|$_\n|
538 for @{$self->{_dump_storage}->{$class} || []};
539
540 $text .= qq|\n\n# Created by DBIx::Class::Schema::Loader|
541 . qq| v| . $DBIx::Class::Schema::Loader::VERSION
542 . q| @ | . POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
543 . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
544
545 open(my $fh, '>', $filename)
546 or croak "Cannot open '$filename' for writing: $!";
547
548 # Write the top half and its MD5 sum
a4476f41 549 print $fh $text . Digest::MD5::md5_base64($text) . "\n";
7cab3ab7 550
551 # Write out anything loaded via external partial class file in @INC
552 print $fh qq|$_\n|
553 for @{$self->{_ext_storage}->{$class} || []};
554
1eea4fb1 555 # Write out any custom content the user has added
7cab3ab7 556 print $fh $custom_content;
557
558 close($fh)
e9b8719e 559 or croak "Error closing '$filename': $!";
7cab3ab7 560}
561
562sub _get_custom_content {
563 my ($self, $class, $filename) = @_;
564
565 return if ! -f $filename;
566 open(my $fh, '<', $filename)
567 or croak "Cannot open '$filename' for reading: $!";
568
569 my $mark_re =
419a2eeb 570 qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
7cab3ab7 571
572 my $found = 0;
573 my $buffer = '';
574 while(<$fh>) {
575 if(!$found && /$mark_re/) {
576 $found = 1;
577 $buffer .= $1;
7cab3ab7 578 croak "Checksum mismatch in '$filename'"
419a2eeb 579 if Digest::MD5::md5_base64($buffer) ne $2;
7cab3ab7 580
581 $buffer = '';
582 }
583 else {
584 $buffer .= $_;
585 }
996be9ee 586 }
587
28b4691d 588 croak "Cannot not overwrite '$filename' without 'really_erase_my_files',"
419a2eeb 589 . " it does not appear to have been generated by Loader"
5ef3c771 590 if !$found;
591
7cab3ab7 592 return $buffer;
996be9ee 593}
594
595sub _use {
596 my $self = shift;
597 my $target = shift;
598
599 foreach (@_) {
cb54990b 600 warn "$target: use $_;" if $self->debug;
996be9ee 601 $self->_raw_stmt($target, "use $_;");
996be9ee 602 }
603}
604
605sub _inject {
606 my $self = shift;
607 my $target = shift;
608 my $schema_class = $self->schema_class;
609
af31090c 610 my $blist = join(q{ }, @_);
611 warn "$target: use base qw/ $blist /;" if $self->debug && @_;
612 $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
996be9ee 613}
614
f96ef30f 615# Create class with applicable bases, setup monikers, etc
616sub _make_src_class {
617 my ($self, $table) = @_;
996be9ee 618
a13b2803 619 my $schema = $self->schema;
620 my $schema_class = $self->schema_class;
996be9ee 621
f96ef30f 622 my $table_moniker = $self->_table2moniker($table);
f44ecc2f 623 my @result_namespace = ($schema_class);
624 if ($self->use_namespaces) {
625 my $result_namespace = $self->result_namespace || 'Result';
626 if ($result_namespace =~ /^\+(.*)/) {
627 # Fully qualified namespace
628 @result_namespace = ($1)
629 }
630 else {
631 # Relative namespace
632 push @result_namespace, $result_namespace;
633 }
634 }
635 my $table_class = join(q{::}, @result_namespace, $table_moniker);
996be9ee 636
f96ef30f 637 my $table_normalized = lc $table;
638 $self->classes->{$table} = $table_class;
639 $self->classes->{$table_normalized} = $table_class;
640 $self->monikers->{$table} = $table_moniker;
641 $self->monikers->{$table_normalized} = $table_moniker;
996be9ee 642
f96ef30f 643 $self->_use ($table_class, @{$self->additional_classes});
af31090c 644 $self->_inject($table_class, @{$self->left_base_classes});
996be9ee 645
605fcea8 646 $self->_dbic_stmt($table_class, 'load_components', @{$self->components}, 'Core');
996be9ee 647
f96ef30f 648 $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
649 if @{$self->resultset_components};
af31090c 650 $self->_inject($table_class, @{$self->additional_base_classes});
f96ef30f 651}
996be9ee 652
af31090c 653# Set up metadata (cols, pks, etc)
f96ef30f 654sub _setup_src_meta {
655 my ($self, $table) = @_;
996be9ee 656
f96ef30f 657 my $schema = $self->schema;
658 my $schema_class = $self->schema_class;
a13b2803 659
f96ef30f 660 my $table_class = $self->classes->{$table};
661 my $table_moniker = $self->monikers->{$table};
996be9ee 662
f96ef30f 663 $self->_dbic_stmt($table_class,'table',$table);
996be9ee 664
f96ef30f 665 my $cols = $self->_table_columns($table);
666 my $col_info;
667 eval { $col_info = $self->_columns_info_for($table) };
668 if($@) {
669 $self->_dbic_stmt($table_class,'add_columns',@$cols);
670 }
671 else {
672 my %col_info_lc = map { lc($_), $col_info->{$_} } keys %$col_info;
e7213f4f 673 my $fks = $self->_table_fk_info($table);
674 for my $fkdef (@$fks) {
675 for my $col (@{ $fkdef->{local_columns} }) {
676 $col_info_lc{$col}->{is_foreign_key} = 1;
677 }
678 }
f96ef30f 679 $self->_dbic_stmt(
680 $table_class,
681 'add_columns',
682 map { $_, ($col_info_lc{$_}||{}) } @$cols
683 );
996be9ee 684 }
685
d70c335f 686 my %uniq_tag; # used to eliminate duplicate uniqs
687
f96ef30f 688 my $pks = $self->_table_pk_info($table) || [];
689 @$pks ? $self->_dbic_stmt($table_class,'set_primary_key',@$pks)
690 : carp("$table has no primary key");
d70c335f 691 $uniq_tag{ join("\0", @$pks) }++ if @$pks; # pk is a uniq
996be9ee 692
f96ef30f 693 my $uniqs = $self->_table_uniq_info($table) || [];
d70c335f 694 for (@$uniqs) {
695 my ($name, $cols) = @$_;
696 next if $uniq_tag{ join("\0", @$cols) }++; # skip duplicates
697 $self->_dbic_stmt($table_class,'add_unique_constraint', $name, $cols);
698 }
699
996be9ee 700}
701
702=head2 tables
703
704Returns a sorted list of loaded tables, using the original database table
705names.
706
707=cut
708
709sub tables {
710 my $self = shift;
711
b97c2c1e 712 return keys %{$self->_tables};
996be9ee 713}
714
715# Make a moniker from a table
716sub _table2moniker {
717 my ( $self, $table ) = @_;
718
719 my $moniker;
720
721 if( ref $self->moniker_map eq 'HASH' ) {
722 $moniker = $self->moniker_map->{$table};
723 }
724 elsif( ref $self->moniker_map eq 'CODE' ) {
725 $moniker = $self->moniker_map->($table);
726 }
727
22270947 728 $moniker ||= join '', map ucfirst, split /[\W_]+/,
729 Lingua::EN::Inflect::Number::to_S(lc $table);
996be9ee 730
731 return $moniker;
732}
733
734sub _load_relationships {
e8ad6491 735 my ($self, $table) = @_;
996be9ee 736
e8ad6491 737 my $tbl_fk_info = $self->_table_fk_info($table);
738 foreach my $fkdef (@$tbl_fk_info) {
739 $fkdef->{remote_source} =
740 $self->monikers->{delete $fkdef->{remote_table}};
996be9ee 741 }
26f1c8c9 742 my $tbl_uniq_info = $self->_table_uniq_info($table);
996be9ee 743
e8ad6491 744 my $local_moniker = $self->monikers->{$table};
26f1c8c9 745 my $rel_stmts = $self->{relbuilder}->generate_code($local_moniker, $tbl_fk_info, $tbl_uniq_info);
996be9ee 746
996be9ee 747 foreach my $src_class (sort keys %$rel_stmts) {
748 my $src_stmts = $rel_stmts->{$src_class};
749 foreach my $stmt (@$src_stmts) {
750 $self->_dbic_stmt($src_class,$stmt->{method},@{$stmt->{args}});
751 }
752 }
753}
754
755# Overload these in driver class:
756
757# Returns an arrayref of column names
758sub _table_columns { croak "ABSTRACT METHOD" }
759
760# Returns arrayref of pk col names
761sub _table_pk_info { croak "ABSTRACT METHOD" }
762
763# Returns an arrayref of uniqs [ [ foo => [ col1, col2 ] ], [ bar => [ ... ] ] ]
764sub _table_uniq_info { croak "ABSTRACT METHOD" }
765
766# Returns an arrayref of foreign key constraints, each
767# being a hashref with 3 keys:
768# local_columns (arrayref), remote_columns (arrayref), remote_table
769sub _table_fk_info { croak "ABSTRACT METHOD" }
770
771# Returns an array of lower case table names
772sub _tables_list { croak "ABSTRACT METHOD" }
773
774# Execute a constructive DBIC class method, with debug/dump_to_dir hooks.
775sub _dbic_stmt {
776 my $self = shift;
777 my $class = shift;
778 my $method = shift;
779
996be9ee 780 my $args = dump(@_);
781 $args = '(' . $args . ')' if @_ < 2;
782 my $stmt = $method . $args . q{;};
783
784 warn qq|$class\->$stmt\n| if $self->debug;
996be9ee 785 $self->_raw_stmt($class, '__PACKAGE__->' . $stmt);
786}
787
788# Store a raw source line for a class (for dumping purposes)
789sub _raw_stmt {
790 my ($self, $class, $stmt) = @_;
af31090c 791 push(@{$self->{_dump_storage}->{$class}}, $stmt);
996be9ee 792}
793
7cab3ab7 794# Like above, but separately for the externally loaded stuff
795sub _ext_stmt {
796 my ($self, $class, $stmt) = @_;
af31090c 797 push(@{$self->{_ext_storage}->{$class}}, $stmt);
7cab3ab7 798}
799
996be9ee 800=head2 monikers
801
8f9d7ce5 802Returns a hashref of loaded table to moniker mappings. There will
996be9ee 803be two entries for each table, the original name and the "normalized"
804name, in the case that the two are different (such as databases
805that like uppercase table names, or preserve your original mixed-case
806definitions, or what-have-you).
807
808=head2 classes
809
8f9d7ce5 810Returns a hashref of table to class mappings. In some cases it will
996be9ee 811contain multiple entries per table for the original and normalized table
812names, as above in L</monikers>.
813
814=head1 SEE ALSO
815
816L<DBIx::Class::Schema::Loader>
817
818=cut
819
8201;