Merge 'trunk' into 'current'
[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);
af31090c 421
181cc907 422 for my $table (@tables) {
423 my $moniker = $self->monikers->{$table};
424 my $class = $self->classes->{$table};
0ae6b65d 425
426 {
427 no warnings 'redefine';
428 local *Class::C3::reinitialize = sub {};
429 use warnings;
430
431 if ( Class::Unload->unload( $class ) ) {
432 my $resultset_class = ref $self->schema->resultset($moniker);
433 Class::Unload->unload( $resultset_class )
434 if $resultset_class ne 'DBIx::Class::ResultSet';
435 }
436 $class->require or die "Can't load $class: $@";
af31090c 437 }
af31090c 438
439 $self->schema_class->register_class($moniker, $class);
440 $self->schema->register_class($moniker, $class)
441 if $self->schema ne $self->schema_class;
442 }
443}
444
996be9ee 445sub _get_dump_filename {
446 my ($self, $class) = (@_);
447
448 $class =~ s{::}{/}g;
449 return $self->dump_directory . q{/} . $class . q{.pm};
450}
451
452sub _ensure_dump_subdirs {
453 my ($self, $class) = (@_);
454
455 my @name_parts = split(/::/, $class);
dd03ee1a 456 pop @name_parts; # we don't care about the very last element,
457 # which is a filename
458
996be9ee 459 my $dir = $self->dump_directory;
7cab3ab7 460 while (1) {
461 if(!-d $dir) {
25328cc4 462 mkdir($dir) or croak "mkdir('$dir') failed: $!";
996be9ee 463 }
7cab3ab7 464 last if !@name_parts;
465 $dir = File::Spec->catdir($dir, shift @name_parts);
996be9ee 466 }
467}
468
469sub _dump_to_dir {
af31090c 470 my ($self, @classes) = @_;
996be9ee 471
472 my $target_dir = $self->dump_directory;
d65cda9e 473
fc2b71fd 474 my $schema_class = $self->schema_class;
9c9c2f2b 475 my $schema_base_class = $self->schema_base_class || 'DBIx::Class::Schema';
996be9ee 476
af31090c 477 warn "Dumping manual schema for $schema_class to directory $target_dir ...\n"
478 unless $self->{dynamic} or $self->{quiet};
996be9ee 479
7cab3ab7 480 my $schema_text =
481 qq|package $schema_class;\n\n|
482 . qq|use strict;\nuse warnings;\n\n|
9c9c2f2b 483 . qq|use base '$schema_base_class';\n\n|;
f44ecc2f 484
485
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|;
501
502 }
996be9ee 503
7cab3ab7 504 $self->_write_classfile($schema_class, $schema_text);
996be9ee 505
9c9c2f2b 506 my $result_base_class = $self->result_base_class || 'DBIx::Class';
507
af31090c 508 foreach my $src_class (@classes) {
7cab3ab7 509 my $src_text =
510 qq|package $src_class;\n\n|
511 . qq|use strict;\nuse warnings;\n\n|
9c9c2f2b 512 . qq|use base '$result_base_class';\n\n|;
996be9ee 513
7cab3ab7 514 $self->_write_classfile($src_class, $src_text);
02356864 515 }
996be9ee 516
af31090c 517 warn "Schema dump completed.\n" unless $self->{dynamic} or $self->{quiet};
518
519 unshift @INC, $target_dir;
7cab3ab7 520}
521
522sub _write_classfile {
523 my ($self, $class, $text) = @_;
524
525 my $filename = $self->_get_dump_filename($class);
526 $self->_ensure_dump_subdirs($class);
527
28b4691d 528 if (-f $filename && $self->really_erase_my_files) {
7cab3ab7 529 warn "Deleting existing file '$filename' due to "
af31090c 530 . "'really_erase_my_files' setting\n" unless $self->{quiet};
7cab3ab7 531 unlink($filename);
532 }
533
419a2eeb 534 my $custom_content = $self->_get_custom_content($class, $filename);
7cab3ab7 535
a4476f41 536 $custom_content ||= qq|\n\n# You can replace this text with custom|
7cab3ab7 537 . qq| content, and it will be preserved on regeneration|
538 . qq|\n1;\n|;
539
540 $text .= qq|$_\n|
541 for @{$self->{_dump_storage}->{$class} || []};
542
543 $text .= qq|\n\n# Created by DBIx::Class::Schema::Loader|
544 . qq| v| . $DBIx::Class::Schema::Loader::VERSION
545 . q| @ | . POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
546 . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
547
548 open(my $fh, '>', $filename)
549 or croak "Cannot open '$filename' for writing: $!";
550
551 # Write the top half and its MD5 sum
a4476f41 552 print $fh $text . Digest::MD5::md5_base64($text) . "\n";
7cab3ab7 553
554 # Write out anything loaded via external partial class file in @INC
555 print $fh qq|$_\n|
556 for @{$self->{_ext_storage}->{$class} || []};
557
558 print $fh $custom_content;
559
560 close($fh)
561 or croak "Cannot close '$filename': $!";
562}
563
564sub _get_custom_content {
565 my ($self, $class, $filename) = @_;
566
567 return if ! -f $filename;
568 open(my $fh, '<', $filename)
569 or croak "Cannot open '$filename' for reading: $!";
570
571 my $mark_re =
419a2eeb 572 qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
7cab3ab7 573
574 my $found = 0;
575 my $buffer = '';
576 while(<$fh>) {
577 if(!$found && /$mark_re/) {
578 $found = 1;
579 $buffer .= $1;
7cab3ab7 580 croak "Checksum mismatch in '$filename'"
419a2eeb 581 if Digest::MD5::md5_base64($buffer) ne $2;
7cab3ab7 582
583 $buffer = '';
584 }
585 else {
586 $buffer .= $_;
587 }
996be9ee 588 }
589
28b4691d 590 croak "Cannot not overwrite '$filename' without 'really_erase_my_files',"
419a2eeb 591 . " it does not appear to have been generated by Loader"
5ef3c771 592 if !$found;
593
7cab3ab7 594 return $buffer;
996be9ee 595}
596
597sub _use {
598 my $self = shift;
599 my $target = shift;
600
601 foreach (@_) {
cb54990b 602 warn "$target: use $_;" if $self->debug;
996be9ee 603 $self->_raw_stmt($target, "use $_;");
996be9ee 604 }
605}
606
607sub _inject {
608 my $self = shift;
609 my $target = shift;
610 my $schema_class = $self->schema_class;
611
af31090c 612 my $blist = join(q{ }, @_);
613 warn "$target: use base qw/ $blist /;" if $self->debug && @_;
614 $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
996be9ee 615}
616
f96ef30f 617# Create class with applicable bases, setup monikers, etc
618sub _make_src_class {
619 my ($self, $table) = @_;
996be9ee 620
a13b2803 621 my $schema = $self->schema;
622 my $schema_class = $self->schema_class;
996be9ee 623
f96ef30f 624 my $table_moniker = $self->_table2moniker($table);
f44ecc2f 625 my @result_namespace = ($schema_class);
626 if ($self->use_namespaces) {
627 my $result_namespace = $self->result_namespace || 'Result';
628 if ($result_namespace =~ /^\+(.*)/) {
629 # Fully qualified namespace
630 @result_namespace = ($1)
631 }
632 else {
633 # Relative namespace
634 push @result_namespace, $result_namespace;
635 }
636 }
637 my $table_class = join(q{::}, @result_namespace, $table_moniker);
996be9ee 638
f96ef30f 639 my $table_normalized = lc $table;
640 $self->classes->{$table} = $table_class;
641 $self->classes->{$table_normalized} = $table_class;
642 $self->monikers->{$table} = $table_moniker;
643 $self->monikers->{$table_normalized} = $table_moniker;
996be9ee 644
f96ef30f 645 $self->_use ($table_class, @{$self->additional_classes});
af31090c 646 $self->_inject($table_class, @{$self->left_base_classes});
996be9ee 647
605fcea8 648 $self->_dbic_stmt($table_class, 'load_components', @{$self->components}, 'Core');
996be9ee 649
f96ef30f 650 $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
651 if @{$self->resultset_components};
af31090c 652 $self->_inject($table_class, @{$self->additional_base_classes});
f96ef30f 653}
996be9ee 654
af31090c 655# Set up metadata (cols, pks, etc)
f96ef30f 656sub _setup_src_meta {
657 my ($self, $table) = @_;
996be9ee 658
f96ef30f 659 my $schema = $self->schema;
660 my $schema_class = $self->schema_class;
a13b2803 661
f96ef30f 662 my $table_class = $self->classes->{$table};
663 my $table_moniker = $self->monikers->{$table};
996be9ee 664
f96ef30f 665 $self->_dbic_stmt($table_class,'table',$table);
996be9ee 666
f96ef30f 667 my $cols = $self->_table_columns($table);
668 my $col_info;
669 eval { $col_info = $self->_columns_info_for($table) };
670 if($@) {
671 $self->_dbic_stmt($table_class,'add_columns',@$cols);
672 }
673 else {
674 my %col_info_lc = map { lc($_), $col_info->{$_} } keys %$col_info;
e7213f4f 675 my $fks = $self->_table_fk_info($table);
676 for my $fkdef (@$fks) {
677 for my $col (@{ $fkdef->{local_columns} }) {
678 $col_info_lc{$col}->{is_foreign_key} = 1;
679 }
680 }
f96ef30f 681 $self->_dbic_stmt(
682 $table_class,
683 'add_columns',
684 map { $_, ($col_info_lc{$_}||{}) } @$cols
685 );
996be9ee 686 }
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");
996be9ee 691
f96ef30f 692 my $uniqs = $self->_table_uniq_info($table) || [];
693 $self->_dbic_stmt($table_class,'add_unique_constraint',@$_) for (@$uniqs);
996be9ee 694}
695
696=head2 tables
697
698Returns a sorted list of loaded tables, using the original database table
699names.
700
701=cut
702
703sub tables {
704 my $self = shift;
705
b97c2c1e 706 return keys %{$self->_tables};
996be9ee 707}
708
709# Make a moniker from a table
710sub _table2moniker {
711 my ( $self, $table ) = @_;
712
713 my $moniker;
714
715 if( ref $self->moniker_map eq 'HASH' ) {
716 $moniker = $self->moniker_map->{$table};
717 }
718 elsif( ref $self->moniker_map eq 'CODE' ) {
719 $moniker = $self->moniker_map->($table);
720 }
721
22270947 722 $moniker ||= join '', map ucfirst, split /[\W_]+/,
723 Lingua::EN::Inflect::Number::to_S(lc $table);
996be9ee 724
725 return $moniker;
726}
727
728sub _load_relationships {
e8ad6491 729 my ($self, $table) = @_;
996be9ee 730
e8ad6491 731 my $tbl_fk_info = $self->_table_fk_info($table);
732 foreach my $fkdef (@$tbl_fk_info) {
733 $fkdef->{remote_source} =
734 $self->monikers->{delete $fkdef->{remote_table}};
996be9ee 735 }
26f1c8c9 736 my $tbl_uniq_info = $self->_table_uniq_info($table);
996be9ee 737
e8ad6491 738 my $local_moniker = $self->monikers->{$table};
26f1c8c9 739 my $rel_stmts = $self->{relbuilder}->generate_code($local_moniker, $tbl_fk_info, $tbl_uniq_info);
996be9ee 740
996be9ee 741 foreach my $src_class (sort keys %$rel_stmts) {
742 my $src_stmts = $rel_stmts->{$src_class};
743 foreach my $stmt (@$src_stmts) {
744 $self->_dbic_stmt($src_class,$stmt->{method},@{$stmt->{args}});
745 }
746 }
747}
748
749# Overload these in driver class:
750
751# Returns an arrayref of column names
752sub _table_columns { croak "ABSTRACT METHOD" }
753
754# Returns arrayref of pk col names
755sub _table_pk_info { croak "ABSTRACT METHOD" }
756
757# Returns an arrayref of uniqs [ [ foo => [ col1, col2 ] ], [ bar => [ ... ] ] ]
758sub _table_uniq_info { croak "ABSTRACT METHOD" }
759
760# Returns an arrayref of foreign key constraints, each
761# being a hashref with 3 keys:
762# local_columns (arrayref), remote_columns (arrayref), remote_table
763sub _table_fk_info { croak "ABSTRACT METHOD" }
764
765# Returns an array of lower case table names
766sub _tables_list { croak "ABSTRACT METHOD" }
767
768# Execute a constructive DBIC class method, with debug/dump_to_dir hooks.
769sub _dbic_stmt {
770 my $self = shift;
771 my $class = shift;
772 my $method = shift;
773
996be9ee 774 my $args = dump(@_);
775 $args = '(' . $args . ')' if @_ < 2;
776 my $stmt = $method . $args . q{;};
777
778 warn qq|$class\->$stmt\n| if $self->debug;
996be9ee 779 $self->_raw_stmt($class, '__PACKAGE__->' . $stmt);
780}
781
782# Store a raw source line for a class (for dumping purposes)
783sub _raw_stmt {
784 my ($self, $class, $stmt) = @_;
af31090c 785 push(@{$self->{_dump_storage}->{$class}}, $stmt);
996be9ee 786}
787
7cab3ab7 788# Like above, but separately for the externally loaded stuff
789sub _ext_stmt {
790 my ($self, $class, $stmt) = @_;
af31090c 791 push(@{$self->{_ext_storage}->{$class}}, $stmt);
7cab3ab7 792}
793
996be9ee 794=head2 monikers
795
8f9d7ce5 796Returns a hashref of loaded table to moniker mappings. There will
996be9ee 797be two entries for each table, the original name and the "normalized"
798name, in the case that the two are different (such as databases
799that like uppercase table names, or preserve your original mixed-case
800definitions, or what-have-you).
801
802=head2 classes
803
8f9d7ce5 804Returns a hashref of table to class mappings. In some cases it will
996be9ee 805contain multiple entries per table for the original and normalized table
806names, as above in L</monikers>.
807
808=head1 SEE ALSO
809
810L<DBIx::Class::Schema::Loader>
811
812=cut
813
8141;