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