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