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