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