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