Documentation change: do not overwrite unchanged schema modules
[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
a6db40af 17our $VERSION = '0.04005';
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
65L<DBIx::Class::Schema::Loader/loader_opts>. Available constructor options are:
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
297b8847 165in memory. Existing files for schemas that have not changed will not
166be overwritten.
996be9ee 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)
288 )
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
302 # The rest is only relevant when dumping
303 return if !$self->dump_directory;
304
305 croak 'Failed to locate actual external module file for '
306 . "'$class'"
307 if !$real_inc_path;
308 open(my $fh, '<', $real_inc_path)
309 or croak "Failed to open '$real_inc_path' for reading: $!";
310 $self->_ext_stmt($class,
311 qq|# These lines were loaded from '$real_inc_path' found in \@INC.|
312 .q|# They are now part of the custom portion of this file|
313 .q|# for you to hand-edit. If you do not either delete|
314 .q|# this section or remove that file from @INC, this section|
315 .q|# will be repeated redundantly when you re-create this|
316 .q|# file again via Loader!|
317 );
318 while(<$fh>) {
319 chomp;
320 $self->_ext_stmt($class, $_);
996be9ee 321 }
f96ef30f 322 $self->_ext_stmt($class,
70b72fab 323 qq|# End of lines loaded from '$real_inc_path' |
f96ef30f 324 );
325 close($fh)
326 or croak "Failed to close $real_inc_path: $!";
996be9ee 327}
328
329=head2 load
330
331Does the actual schema-construction work.
332
333=cut
334
335sub load {
336 my $self = shift;
337
b97c2c1e 338 $self->_load_tables($self->_tables_list);
339}
340
341=head2 rescan
342
a60b5b8d 343Arguments: schema
344
b97c2c1e 345Rescan the database for newly added tables. Does
a60b5b8d 346not process drops or changes. Returns a list of
347the newly added table monikers.
348
349The schema argument should be the schema class
350or object to be affected. It should probably
351be derived from the original schema_class used
352during L</load>.
b97c2c1e 353
354=cut
355
356sub rescan {
a60b5b8d 357 my ($self, $schema) = @_;
358
359 $self->{schema} = $schema;
b97c2c1e 360
361 my @created;
362 my @current = $self->_tables_list;
363 foreach my $table ($self->_tables_list) {
364 if(!exists $self->{_tables}->{$table}) {
365 push(@created, $table);
366 }
367 }
368
c39e3507 369 my $loaded = $self->_load_tables(@created);
a60b5b8d 370
c39e3507 371 return map { $self->monikers->{$_} } @$loaded;
b97c2c1e 372}
373
374sub _load_tables {
375 my ($self, @tables) = @_;
376
f96ef30f 377 # First, use _tables_list with constraint and exclude
378 # to get a list of tables to operate on
379
380 my $constraint = $self->constraint;
381 my $exclude = $self->exclude;
f96ef30f 382
b97c2c1e 383 @tables = grep { /$constraint/ } @tables if $constraint;
384 @tables = grep { ! /$exclude/ } @tables if $exclude;
f96ef30f 385
b97c2c1e 386 # Save the new tables to the tables list
a60b5b8d 387 foreach (@tables) {
388 $self->{_tables}->{$_} = 1;
389 }
f96ef30f 390
391 # Set up classes/monikers
392 {
393 no warnings 'redefine';
394 local *Class::C3::reinitialize = sub { };
395 use warnings;
396
397 $self->_make_src_class($_) for @tables;
398 }
399
400 Class::C3::reinitialize;
401
402 $self->_setup_src_meta($_) for @tables;
403
e8ad6491 404 if(!$self->skip_relationships) {
405 $self->_load_relationships($_) for @tables;
406 }
407
f96ef30f 408 $self->_load_external($_)
75451704 409 for map { $self->classes->{$_} } @tables;
f96ef30f 410
996be9ee 411 $self->_dump_to_dir if $self->dump_directory;
412
5223f24a 413 # Drop temporary cache
414 delete $self->{_cache};
415
c39e3507 416 return \@tables;
996be9ee 417}
418
419sub _get_dump_filename {
420 my ($self, $class) = (@_);
421
422 $class =~ s{::}{/}g;
423 return $self->dump_directory . q{/} . $class . q{.pm};
424}
425
426sub _ensure_dump_subdirs {
427 my ($self, $class) = (@_);
428
429 my @name_parts = split(/::/, $class);
dd03ee1a 430 pop @name_parts; # we don't care about the very last element,
431 # which is a filename
432
996be9ee 433 my $dir = $self->dump_directory;
7cab3ab7 434 while (1) {
435 if(!-d $dir) {
25328cc4 436 mkdir($dir) or croak "mkdir('$dir') failed: $!";
996be9ee 437 }
7cab3ab7 438 last if !@name_parts;
439 $dir = File::Spec->catdir($dir, shift @name_parts);
996be9ee 440 }
441}
442
443sub _dump_to_dir {
444 my ($self) = @_;
445
446 my $target_dir = $self->dump_directory;
d65cda9e 447
fc2b71fd 448 my $schema_class = $self->schema_class;
996be9ee 449
25328cc4 450 croak "Must specify target directory for dumping!" if ! $target_dir;
996be9ee 451
fc2b71fd 452 warn "Dumping manual schema for $schema_class to directory $target_dir ...\n";
996be9ee 453
7cab3ab7 454 my $schema_text =
455 qq|package $schema_class;\n\n|
456 . qq|use strict;\nuse warnings;\n\n|
f44ecc2f 457 . qq|use base 'DBIx::Class::Schema';\n\n|;
458
459
460 if ($self->use_namespaces) {
461 $schema_text .= qq|__PACKAGE__->load_namespaces|;
462 my $namespace_options;
463 for my $attr (qw(result_namespace
464 resultset_namespace
465 default_resultset_class)) {
466 if ($self->$attr) {
467 $namespace_options .= qq| $attr => '| . $self->$attr . qq|',\n|
468 }
469 }
470 $schema_text .= qq|(\n$namespace_options)| if $namespace_options;
471 $schema_text .= qq|;\n|;
472 }
473 else {
474 $schema_text .= qq|__PACKAGE__->load_classes;\n|;
475
476 }
996be9ee 477
7cab3ab7 478 $self->_write_classfile($schema_class, $schema_text);
996be9ee 479
7cab3ab7 480 foreach my $src_class (sort keys %{$self->{_dump_storage}}) {
481 my $src_text =
482 qq|package $src_class;\n\n|
483 . qq|use strict;\nuse warnings;\n\n|
484 . qq|use base 'DBIx::Class';\n\n|;
996be9ee 485
7cab3ab7 486 $self->_write_classfile($src_class, $src_text);
02356864 487 }
996be9ee 488
7cab3ab7 489 warn "Schema dump completed.\n";
490}
491
492sub _write_classfile {
493 my ($self, $class, $text) = @_;
494
495 my $filename = $self->_get_dump_filename($class);
496 $self->_ensure_dump_subdirs($class);
497
28b4691d 498 if (-f $filename && $self->really_erase_my_files) {
7cab3ab7 499 warn "Deleting existing file '$filename' due to "
28b4691d 500 . "'really_erase_my_files' setting\n";
7cab3ab7 501 unlink($filename);
502 }
503
419a2eeb 504 my $custom_content = $self->_get_custom_content($class, $filename);
7cab3ab7 505
fb01c180 506 # only re-write the file if new content ($text) is different from old ($custom_content)
507 if ( $custom_content ) {
508 my $no_timestamp = $custom_content;
509 $no_timestamp =~ s/^# Created by DBIx::Class::Schema::Loader.*//;
510 return if ($no_timestamp eq $text);
511 }
512
a4476f41 513 $custom_content ||= qq|\n\n# You can replace this text with custom|
7cab3ab7 514 . qq| content, and it will be preserved on regeneration|
515 . qq|\n1;\n|;
516
517 $text .= qq|$_\n|
518 for @{$self->{_dump_storage}->{$class} || []};
519
520 $text .= qq|\n\n# Created by DBIx::Class::Schema::Loader|
521 . qq| v| . $DBIx::Class::Schema::Loader::VERSION
522 . q| @ | . POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
523 . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
524
525 open(my $fh, '>', $filename)
526 or croak "Cannot open '$filename' for writing: $!";
527
528 # Write the top half and its MD5 sum
a4476f41 529 print $fh $text . Digest::MD5::md5_base64($text) . "\n";
7cab3ab7 530
531 # Write out anything loaded via external partial class file in @INC
532 print $fh qq|$_\n|
533 for @{$self->{_ext_storage}->{$class} || []};
534
535 print $fh $custom_content;
536
537 close($fh)
538 or croak "Cannot close '$filename': $!";
539}
540
541sub _get_custom_content {
542 my ($self, $class, $filename) = @_;
543
544 return if ! -f $filename;
545 open(my $fh, '<', $filename)
546 or croak "Cannot open '$filename' for reading: $!";
547
548 my $mark_re =
419a2eeb 549 qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
7cab3ab7 550
551 my $found = 0;
552 my $buffer = '';
553 while(<$fh>) {
554 if(!$found && /$mark_re/) {
555 $found = 1;
556 $buffer .= $1;
7cab3ab7 557 croak "Checksum mismatch in '$filename'"
419a2eeb 558 if Digest::MD5::md5_base64($buffer) ne $2;
7cab3ab7 559
560 $buffer = '';
561 }
562 else {
563 $buffer .= $_;
564 }
996be9ee 565 }
566
28b4691d 567 croak "Cannot not overwrite '$filename' without 'really_erase_my_files',"
419a2eeb 568 . " it does not appear to have been generated by Loader"
5ef3c771 569 if !$found;
570
7cab3ab7 571 return $buffer;
996be9ee 572}
573
574sub _use {
575 my $self = shift;
576 my $target = shift;
cb54990b 577 my $evalstr;
996be9ee 578
579 foreach (@_) {
cb54990b 580 warn "$target: use $_;" if $self->debug;
996be9ee 581 $self->_raw_stmt($target, "use $_;");
cb54990b 582 $_->require or croak ($_ . "->require: $@");
583 $evalstr .= "package $target; use $_;";
996be9ee 584 }
cb54990b 585 eval $evalstr if $evalstr;
586 croak $@ if $@;
996be9ee 587}
588
589sub _inject {
590 my $self = shift;
591 my $target = shift;
592 my $schema_class = $self->schema_class;
593
594 my $blist = join(q{ }, @_);
cb54990b 595 warn "$target: use base qw/ $blist /;" if $self->debug && @_;
996be9ee 596 $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
996be9ee 597 foreach (@_) {
598 $_->require or croak ($_ . "->require: $@");
599 $schema_class->inject_base($target, $_);
600 }
601}
602
f96ef30f 603# Create class with applicable bases, setup monikers, etc
604sub _make_src_class {
605 my ($self, $table) = @_;
996be9ee 606
a13b2803 607 my $schema = $self->schema;
608 my $schema_class = $self->schema_class;
996be9ee 609
f96ef30f 610 my $table_moniker = $self->_table2moniker($table);
f44ecc2f 611 my @result_namespace = ($schema_class);
612 if ($self->use_namespaces) {
613 my $result_namespace = $self->result_namespace || 'Result';
614 if ($result_namespace =~ /^\+(.*)/) {
615 # Fully qualified namespace
616 @result_namespace = ($1)
617 }
618 else {
619 # Relative namespace
620 push @result_namespace, $result_namespace;
621 }
622 }
623 my $table_class = join(q{::}, @result_namespace, $table_moniker);
996be9ee 624
f96ef30f 625 my $table_normalized = lc $table;
626 $self->classes->{$table} = $table_class;
627 $self->classes->{$table_normalized} = $table_class;
628 $self->monikers->{$table} = $table_moniker;
629 $self->monikers->{$table_normalized} = $table_moniker;
996be9ee 630
f96ef30f 631 { no strict 'refs'; @{"${table_class}::ISA"} = qw/DBIx::Class/ }
996be9ee 632
f96ef30f 633 $self->_use ($table_class, @{$self->additional_classes});
634 $self->_inject($table_class, @{$self->additional_base_classes});
996be9ee 635
605fcea8 636 $self->_dbic_stmt($table_class, 'load_components', @{$self->components}, 'Core');
996be9ee 637
f96ef30f 638 $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
639 if @{$self->resultset_components};
640 $self->_inject($table_class, @{$self->left_base_classes});
641}
996be9ee 642
f96ef30f 643# Set up metadata (cols, pks, etc) and register the class with the schema
644sub _setup_src_meta {
645 my ($self, $table) = @_;
996be9ee 646
f96ef30f 647 my $schema = $self->schema;
648 my $schema_class = $self->schema_class;
a13b2803 649
f96ef30f 650 my $table_class = $self->classes->{$table};
651 my $table_moniker = $self->monikers->{$table};
996be9ee 652
f96ef30f 653 $self->_dbic_stmt($table_class,'table',$table);
996be9ee 654
f96ef30f 655 my $cols = $self->_table_columns($table);
656 my $col_info;
657 eval { $col_info = $self->_columns_info_for($table) };
658 if($@) {
659 $self->_dbic_stmt($table_class,'add_columns',@$cols);
660 }
661 else {
662 my %col_info_lc = map { lc($_), $col_info->{$_} } keys %$col_info;
663 $self->_dbic_stmt(
664 $table_class,
665 'add_columns',
666 map { $_, ($col_info_lc{$_}||{}) } @$cols
667 );
996be9ee 668 }
669
f96ef30f 670 my $pks = $self->_table_pk_info($table) || [];
671 @$pks ? $self->_dbic_stmt($table_class,'set_primary_key',@$pks)
672 : carp("$table has no primary key");
996be9ee 673
f96ef30f 674 my $uniqs = $self->_table_uniq_info($table) || [];
675 $self->_dbic_stmt($table_class,'add_unique_constraint',@$_) for (@$uniqs);
996be9ee 676
f96ef30f 677 $schema_class->register_class($table_moniker, $table_class);
678 $schema->register_class($table_moniker, $table_class) if $schema ne $schema_class;
996be9ee 679}
680
681=head2 tables
682
683Returns a sorted list of loaded tables, using the original database table
684names.
685
686=cut
687
688sub tables {
689 my $self = shift;
690
b97c2c1e 691 return keys %{$self->_tables};
996be9ee 692}
693
694# Make a moniker from a table
695sub _table2moniker {
696 my ( $self, $table ) = @_;
697
698 my $moniker;
699
700 if( ref $self->moniker_map eq 'HASH' ) {
701 $moniker = $self->moniker_map->{$table};
702 }
703 elsif( ref $self->moniker_map eq 'CODE' ) {
704 $moniker = $self->moniker_map->($table);
705 }
706
707 $moniker ||= join '', map ucfirst, split /[\W_]+/, lc $table;
708
709 return $moniker;
710}
711
712sub _load_relationships {
e8ad6491 713 my ($self, $table) = @_;
996be9ee 714
e8ad6491 715 my $tbl_fk_info = $self->_table_fk_info($table);
716 foreach my $fkdef (@$tbl_fk_info) {
717 $fkdef->{remote_source} =
718 $self->monikers->{delete $fkdef->{remote_table}};
996be9ee 719 }
720
e8ad6491 721 my $local_moniker = $self->monikers->{$table};
722 my $rel_stmts = $self->{relbuilder}->generate_code($local_moniker, $tbl_fk_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
757 if(!$self->debug && !$self->dump_directory) {
758 $class->$method(@_);
759 return;
760 }
761
762 my $args = dump(@_);
763 $args = '(' . $args . ')' if @_ < 2;
764 my $stmt = $method . $args . q{;};
765
766 warn qq|$class\->$stmt\n| if $self->debug;
767 $class->$method(@_);
768 $self->_raw_stmt($class, '__PACKAGE__->' . $stmt);
769}
770
771# Store a raw source line for a class (for dumping purposes)
772sub _raw_stmt {
773 my ($self, $class, $stmt) = @_;
774 push(@{$self->{_dump_storage}->{$class}}, $stmt) if $self->dump_directory;
775}
776
7cab3ab7 777# Like above, but separately for the externally loaded stuff
778sub _ext_stmt {
779 my ($self, $class, $stmt) = @_;
780 push(@{$self->{_ext_storage}->{$class}}, $stmt) if $self->dump_directory;
781}
782
996be9ee 783=head2 monikers
784
8f9d7ce5 785Returns a hashref of loaded table to moniker mappings. There will
996be9ee 786be two entries for each table, the original name and the "normalized"
787name, in the case that the two are different (such as databases
788that like uppercase table names, or preserve your original mixed-case
789definitions, or what-have-you).
790
791=head2 classes
792
8f9d7ce5 793Returns a hashref of table to class mappings. In some cases it will
996be9ee 794contain multiple entries per table for the original and normalized table
795names, as above in L</monikers>.
796
797=head1 SEE ALSO
798
799L<DBIx::Class::Schema::Loader>
800
801=cut
802
8031;