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