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