new dev 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;
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
0a701ff3 19our $VERSION = '0.04999_12';
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
2229729e 133Base class for your table classes (aka result classes). Defaults to
134'DBIx::Class::Core'.
9c9c2f2b 135
996be9ee 136=head2 additional_base_classes
137
138List of additional base classes all of your table classes will use.
139
140=head2 left_base_classes
141
142List of additional base classes all of your table classes will use
143that need to be leftmost.
144
145=head2 additional_classes
146
147List of additional classes which all of your table classes will use.
148
149=head2 components
150
151List of additional components to be loaded into all of your table
152classes. A good example would be C<ResultSetManager>.
153
154=head2 resultset_components
155
8f9d7ce5 156List of additional ResultSet components to be loaded into your table
996be9ee 157classes. A good example would be C<AlwaysRS>. Component
158C<ResultSetManager> will be automatically added to the above
159C<components> list if this option is set.
160
f44ecc2f 161=head2 use_namespaces
162
163Generate result class names suitable for
164L<DBIx::Class::Schema/load_namespaces> and call that instead of
165L<DBIx::Class::Schema/load_classes>. When using this option you can also
166specify any of the options for C<load_namespaces> (i.e. C<result_namespace>,
167C<resultset_namespace>, C<default_resultset_class>), and they will be added
168to the call (and the generated result class names adjusted appropriately).
169
996be9ee 170=head2 dump_directory
171
172This option is designed to be a tool to help you transition from this
173loader to a manually-defined schema when you decide it's time to do so.
174
175The value of this option is a perl libdir pathname. Within
176that directory this module will create a baseline manual
177L<DBIx::Class::Schema> module set, based on what it creates at runtime
178in memory.
179
180The created schema class will have the same classname as the one on
181which you are setting this option (and the ResultSource classes will be
7cab3ab7 182based on this name as well).
996be9ee 183
8f9d7ce5 184Normally you wouldn't hard-code this setting in your schema class, as it
996be9ee 185is meant for one-time manual usage.
186
187See L<DBIx::Class::Schema::Loader/dump_to_dir> for examples of the
188recommended way to access this functionality.
189
d65cda9e 190=head2 dump_overwrite
191
28b4691d 192Deprecated. See L</really_erase_my_files> below, which does *not* mean
193the same thing as the old C<dump_overwrite> setting from previous releases.
194
195=head2 really_erase_my_files
196
7cab3ab7 197Default false. If true, Loader will unconditionally delete any existing
198files before creating the new ones from scratch when dumping a schema to disk.
199
200The default behavior is instead to only replace the top portion of the
201file, up to and including the final stanza which contains
202C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!>
203leaving any customizations you placed after that as they were.
204
28b4691d 205When C<really_erase_my_files> is not set, if the output file already exists,
7cab3ab7 206but the aforementioned final stanza is not found, or the checksum
207contained there does not match the generated contents, Loader will
208croak and not touch the file.
d65cda9e 209
28b4691d 210You should really be using version control on your schema classes (and all
211of the rest of your code for that matter). Don't blame me if a bug in this
212code wipes something out when it shouldn't have, you've been warned.
213
996be9ee 214=head1 METHODS
215
216None of these methods are intended for direct invocation by regular
217users of L<DBIx::Class::Schema::Loader>. Anything you can find here
218can also be found via standard L<DBIx::Class::Schema> methods somehow.
219
220=cut
221
222# ensure that a peice of object data is a valid arrayref, creating
223# an empty one or encapsulating whatever's there.
224sub _ensure_arrayref {
225 my $self = shift;
226
227 foreach (@_) {
228 $self->{$_} ||= [];
229 $self->{$_} = [ $self->{$_} ]
230 unless ref $self->{$_} eq 'ARRAY';
231 }
232}
233
234=head2 new
235
236Constructor for L<DBIx::Class::Schema::Loader::Base>, used internally
237by L<DBIx::Class::Schema::Loader>.
238
239=cut
240
241sub new {
242 my ( $class, %args ) = @_;
243
244 my $self = { %args };
245
246 bless $self => $class;
247
996be9ee 248 $self->_ensure_arrayref(qw/additional_classes
249 additional_base_classes
250 left_base_classes
251 components
252 resultset_components
253 /);
254
255 push(@{$self->{components}}, 'ResultSetManager')
256 if @{$self->{resultset_components}};
257
258 $self->{monikers} = {};
259 $self->{classes} = {};
260
996be9ee 261 $self->{schema_class} ||= ( ref $self->{schema} || $self->{schema} );
262 $self->{schema} ||= $self->{schema_class};
263
28b4691d 264 croak "dump_overwrite is deprecated. Please read the"
265 . " DBIx::Class::Schema::Loader::Base documentation"
266 if $self->{dump_overwrite};
267
af31090c 268 $self->{dynamic} = ! $self->{dump_directory};
79193756 269 $self->{temp_directory} ||= File::Temp::tempdir( 'dbicXXXX',
af31090c 270 TMPDIR => 1,
271 CLEANUP => 1,
272 );
273
79193756 274 $self->{dump_directory} ||= $self->{temp_directory};
275
e8ad6491 276 $self->{relbuilder} = DBIx::Class::Schema::Loader::RelBuilder->new(
706ef173 277 $self->schema, $self->inflect_plural, $self->inflect_singular
e8ad6491 278 ) if !$self->{skip_relationships};
279
996be9ee 280 $self;
281}
282
419a2eeb 283sub _find_file_in_inc {
284 my ($self, $file) = @_;
285
286 foreach my $prefix (@INC) {
af31090c 287 my $fullpath = File::Spec->catfile($prefix, $file);
288 return $fullpath if -f $fullpath
289 and Cwd::abs_path($fullpath) ne
290 Cwd::abs_path(File::Spec->catfile($self->dump_directory, $file)) || '';
419a2eeb 291 }
292
293 return;
294}
295
996be9ee 296sub _load_external {
f96ef30f 297 my ($self, $class) = @_;
298
299 my $class_path = $class;
300 $class_path =~ s{::}{/}g;
301 $class_path .= '.pm';
302
af31090c 303 my $real_inc_path = $self->_find_file_in_inc($class_path);
f96ef30f 304
af31090c 305 return if !$real_inc_path;
f96ef30f 306
307 # If we make it to here, we loaded an external definition
308 warn qq/# Loaded external class definition for '$class'\n/
309 if $self->debug;
310
f96ef30f 311 croak 'Failed to locate actual external module file for '
312 . "'$class'"
313 if !$real_inc_path;
314 open(my $fh, '<', $real_inc_path)
315 or croak "Failed to open '$real_inc_path' for reading: $!";
316 $self->_ext_stmt($class,
565ca24d 317 qq|# These lines were loaded from '$real_inc_path' found in \@INC.\n|
318 .qq|# They are now part of the custom portion of this file\n|
319 .qq|# for you to hand-edit. If you do not either delete\n|
320 .qq|# this section or remove that file from \@INC, this section\n|
321 .qq|# will be repeated redundantly when you re-create this\n|
322 .qq|# file again via Loader!\n|
f96ef30f 323 );
324 while(<$fh>) {
325 chomp;
326 $self->_ext_stmt($class, $_);
996be9ee 327 }
f96ef30f 328 $self->_ext_stmt($class,
70b72fab 329 qq|# End of lines loaded from '$real_inc_path' |
f96ef30f 330 );
331 close($fh)
332 or croak "Failed to close $real_inc_path: $!";
996be9ee 333}
334
335=head2 load
336
337Does the actual schema-construction work.
338
339=cut
340
341sub load {
342 my $self = shift;
343
b97c2c1e 344 $self->_load_tables($self->_tables_list);
345}
346
347=head2 rescan
348
a60b5b8d 349Arguments: schema
350
b97c2c1e 351Rescan the database for newly added tables. Does
a60b5b8d 352not process drops or changes. Returns a list of
353the newly added table monikers.
354
355The schema argument should be the schema class
356or object to be affected. It should probably
357be derived from the original schema_class used
358during L</load>.
b97c2c1e 359
360=cut
361
362sub rescan {
a60b5b8d 363 my ($self, $schema) = @_;
364
365 $self->{schema} = $schema;
706ef173 366 $self->{relbuilder}{schema} = $schema;
b97c2c1e 367
368 my @created;
369 my @current = $self->_tables_list;
370 foreach my $table ($self->_tables_list) {
371 if(!exists $self->{_tables}->{$table}) {
372 push(@created, $table);
373 }
374 }
375
c39e3507 376 my $loaded = $self->_load_tables(@created);
a60b5b8d 377
c39e3507 378 return map { $self->monikers->{$_} } @$loaded;
b97c2c1e 379}
380
381sub _load_tables {
382 my ($self, @tables) = @_;
383
f96ef30f 384 # First, use _tables_list with constraint and exclude
385 # to get a list of tables to operate on
386
387 my $constraint = $self->constraint;
388 my $exclude = $self->exclude;
f96ef30f 389
b97c2c1e 390 @tables = grep { /$constraint/ } @tables if $constraint;
391 @tables = grep { ! /$exclude/ } @tables if $exclude;
f96ef30f 392
b97c2c1e 393 # Save the new tables to the tables list
a60b5b8d 394 foreach (@tables) {
395 $self->{_tables}->{$_} = 1;
396 }
f96ef30f 397
af31090c 398 $self->_make_src_class($_) for @tables;
f96ef30f 399 $self->_setup_src_meta($_) for @tables;
400
e8ad6491 401 if(!$self->skip_relationships) {
181cc907 402 # The relationship loader needs a working schema
af31090c 403 $self->{quiet} = 1;
79193756 404 local $self->{dump_directory} = $self->{temp_directory};
181cc907 405 $self->_reload_classes(@tables);
e8ad6491 406 $self->_load_relationships($_) for @tables;
af31090c 407 $self->{quiet} = 0;
79193756 408
409 # Remove that temp dir from INC so it doesn't get reloaded
410 @INC = grep { $_ ne $self->{dump_directory} } @INC;
e8ad6491 411 }
412
f96ef30f 413 $self->_load_external($_)
75451704 414 for map { $self->classes->{$_} } @tables;
f96ef30f 415
181cc907 416 $self->_reload_classes(@tables);
996be9ee 417
5223f24a 418 # Drop temporary cache
419 delete $self->{_cache};
420
c39e3507 421 return \@tables;
996be9ee 422}
423
af31090c 424sub _reload_classes {
181cc907 425 my ($self, @tables) = @_;
426
4daef04f 427 # so that we don't repeat custom sections
428 @INC = grep $_ ne $self->dump_directory, @INC;
429
181cc907 430 $self->_dump_to_dir(map { $self->classes->{$_} } @tables);
e9b8719e 431
432 unshift @INC, $self->dump_directory;
af31090c 433
706ef173 434 my @to_register;
435 my %have_source = map { $_ => $self->schema->source($_) }
436 $self->schema->sources;
437
181cc907 438 for my $table (@tables) {
439 my $moniker = $self->monikers->{$table};
440 my $class = $self->classes->{$table};
0ae6b65d 441
442 {
443 no warnings 'redefine';
444 local *Class::C3::reinitialize = sub {};
445 use warnings;
446
706ef173 447 Class::Unload->unload($class);
448 my ($source, $resultset_class);
449 if (
450 ($source = $have_source{$moniker})
451 && ($resultset_class = $source->resultset_class)
452 && ($resultset_class ne 'DBIx::Class::ResultSet')
453 ) {
454 my $has_file = Class::Inspector->loaded_filename($resultset_class);
455 Class::Unload->unload($resultset_class);
6ae3f335 456 $self->ensure_class_loaded($resultset_class) if $has_file;
0ae6b65d 457 }
6ae3f335 458 $self->ensure_class_loaded($class);
af31090c 459 }
706ef173 460 push @to_register, [$moniker, $class];
461 }
af31090c 462
706ef173 463 Class::C3->reinitialize;
464 for (@to_register) {
465 $self->schema->register_class(@$_);
af31090c 466 }
467}
468
996be9ee 469sub _get_dump_filename {
470 my ($self, $class) = (@_);
471
472 $class =~ s{::}{/}g;
473 return $self->dump_directory . q{/} . $class . q{.pm};
474}
475
476sub _ensure_dump_subdirs {
477 my ($self, $class) = (@_);
478
479 my @name_parts = split(/::/, $class);
dd03ee1a 480 pop @name_parts; # we don't care about the very last element,
481 # which is a filename
482
996be9ee 483 my $dir = $self->dump_directory;
7cab3ab7 484 while (1) {
485 if(!-d $dir) {
25328cc4 486 mkdir($dir) or croak "mkdir('$dir') failed: $!";
996be9ee 487 }
7cab3ab7 488 last if !@name_parts;
489 $dir = File::Spec->catdir($dir, shift @name_parts);
996be9ee 490 }
491}
492
493sub _dump_to_dir {
af31090c 494 my ($self, @classes) = @_;
996be9ee 495
fc2b71fd 496 my $schema_class = $self->schema_class;
9c9c2f2b 497 my $schema_base_class = $self->schema_base_class || 'DBIx::Class::Schema';
996be9ee 498
e9b8719e 499 my $target_dir = $self->dump_directory;
af31090c 500 warn "Dumping manual schema for $schema_class to directory $target_dir ...\n"
501 unless $self->{dynamic} or $self->{quiet};
996be9ee 502
7cab3ab7 503 my $schema_text =
504 qq|package $schema_class;\n\n|
b4dcbcc5 505 . qq|# Created by DBIx::Class::Schema::Loader\n|
506 . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
7cab3ab7 507 . qq|use strict;\nuse warnings;\n\n|
9c9c2f2b 508 . qq|use base '$schema_base_class';\n\n|;
f44ecc2f 509
f44ecc2f 510 if ($self->use_namespaces) {
511 $schema_text .= qq|__PACKAGE__->load_namespaces|;
512 my $namespace_options;
513 for my $attr (qw(result_namespace
514 resultset_namespace
515 default_resultset_class)) {
516 if ($self->$attr) {
517 $namespace_options .= qq| $attr => '| . $self->$attr . qq|',\n|
518 }
519 }
520 $schema_text .= qq|(\n$namespace_options)| if $namespace_options;
521 $schema_text .= qq|;\n|;
522 }
523 else {
524 $schema_text .= qq|__PACKAGE__->load_classes;\n|;
f44ecc2f 525 }
996be9ee 526
7cab3ab7 527 $self->_write_classfile($schema_class, $schema_text);
996be9ee 528
2229729e 529 my $result_base_class = $self->result_base_class || 'DBIx::Class::Core';
9c9c2f2b 530
af31090c 531 foreach my $src_class (@classes) {
7cab3ab7 532 my $src_text =
533 qq|package $src_class;\n\n|
b4dcbcc5 534 . qq|# Created by DBIx::Class::Schema::Loader\n|
535 . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
7cab3ab7 536 . qq|use strict;\nuse warnings;\n\n|
9c9c2f2b 537 . qq|use base '$result_base_class';\n\n|;
996be9ee 538
7cab3ab7 539 $self->_write_classfile($src_class, $src_text);
02356864 540 }
996be9ee 541
af31090c 542 warn "Schema dump completed.\n" unless $self->{dynamic} or $self->{quiet};
543
7cab3ab7 544}
545
79193756 546sub _sig_comment {
547 my ($self, $version, $ts) = @_;
548 return qq|\n\n# Created by DBIx::Class::Schema::Loader|
549 . qq| v| . $version
550 . q| @ | . $ts
551 . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
552}
553
7cab3ab7 554sub _write_classfile {
555 my ($self, $class, $text) = @_;
556
557 my $filename = $self->_get_dump_filename($class);
558 $self->_ensure_dump_subdirs($class);
559
28b4691d 560 if (-f $filename && $self->really_erase_my_files) {
7cab3ab7 561 warn "Deleting existing file '$filename' due to "
af31090c 562 . "'really_erase_my_files' setting\n" unless $self->{quiet};
7cab3ab7 563 unlink($filename);
564 }
565
79193756 566 my ($custom_content, $old_md5, $old_ver, $old_ts) = $self->_get_custom_content($class, $filename);
17ca645f 567
7cab3ab7 568 $text .= qq|$_\n|
569 for @{$self->{_dump_storage}->{$class} || []};
570
79193756 571 # Check and see if the dump is infact differnt
572
573 my $compare_to;
574 if ($old_md5) {
575 $compare_to = $text . $self->_sig_comment($old_ver, $old_ts);
576
577
578 if (Digest::MD5::md5_base64($compare_to) eq $old_md5) {
579 return;
580 }
581 }
582
583 $text .= $self->_sig_comment(
584 $DBIx::Class::Schema::Loader::VERSION,
585 POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
586 );
7cab3ab7 587
588 open(my $fh, '>', $filename)
589 or croak "Cannot open '$filename' for writing: $!";
590
591 # Write the top half and its MD5 sum
a4476f41 592 print $fh $text . Digest::MD5::md5_base64($text) . "\n";
7cab3ab7 593
594 # Write out anything loaded via external partial class file in @INC
595 print $fh qq|$_\n|
596 for @{$self->{_ext_storage}->{$class} || []};
597
1eea4fb1 598 # Write out any custom content the user has added
7cab3ab7 599 print $fh $custom_content;
600
601 close($fh)
e9b8719e 602 or croak "Error closing '$filename': $!";
7cab3ab7 603}
604
79193756 605sub _default_custom_content {
606 return qq|\n\n# You can replace this text with custom|
607 . qq| content, and it will be preserved on regeneration|
608 . qq|\n1;\n|;
609}
610
7cab3ab7 611sub _get_custom_content {
612 my ($self, $class, $filename) = @_;
613
79193756 614 return ($self->_default_custom_content) if ! -f $filename;
615
7cab3ab7 616 open(my $fh, '<', $filename)
617 or croak "Cannot open '$filename' for reading: $!";
618
619 my $mark_re =
419a2eeb 620 qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
7cab3ab7 621
7cab3ab7 622 my $buffer = '';
79193756 623 my ($md5, $ts, $ver);
7cab3ab7 624 while(<$fh>) {
79193756 625 if(!$md5 && /$mark_re/) {
626 $md5 = $2;
627 my $line = $1;
628
629 # Pull out the previous version and timestamp
630 ($ver, $ts) = $buffer =~ m/# Created by DBIx::Class::Schema::Loader v(.*?) @ (.*?)$/s;
631
632 $buffer .= $line;
7cab3ab7 633 croak "Checksum mismatch in '$filename'"
79193756 634 if Digest::MD5::md5_base64($buffer) ne $md5;
7cab3ab7 635
636 $buffer = '';
637 }
638 else {
639 $buffer .= $_;
640 }
996be9ee 641 }
642
28b4691d 643 croak "Cannot not overwrite '$filename' without 'really_erase_my_files',"
419a2eeb 644 . " it does not appear to have been generated by Loader"
79193756 645 if !$md5;
646
647 # Default custom content:
648 $buffer ||= $self->_default_custom_content;
5ef3c771 649
79193756 650 return ($buffer, $md5, $ver, $ts);
996be9ee 651}
652
653sub _use {
654 my $self = shift;
655 my $target = shift;
656
657 foreach (@_) {
cb54990b 658 warn "$target: use $_;" if $self->debug;
996be9ee 659 $self->_raw_stmt($target, "use $_;");
996be9ee 660 }
661}
662
663sub _inject {
664 my $self = shift;
665 my $target = shift;
666 my $schema_class = $self->schema_class;
667
af31090c 668 my $blist = join(q{ }, @_);
669 warn "$target: use base qw/ $blist /;" if $self->debug && @_;
670 $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
996be9ee 671}
672
f96ef30f 673# Create class with applicable bases, setup monikers, etc
674sub _make_src_class {
675 my ($self, $table) = @_;
996be9ee 676
a13b2803 677 my $schema = $self->schema;
678 my $schema_class = $self->schema_class;
996be9ee 679
f96ef30f 680 my $table_moniker = $self->_table2moniker($table);
f44ecc2f 681 my @result_namespace = ($schema_class);
682 if ($self->use_namespaces) {
683 my $result_namespace = $self->result_namespace || 'Result';
684 if ($result_namespace =~ /^\+(.*)/) {
685 # Fully qualified namespace
686 @result_namespace = ($1)
687 }
688 else {
689 # Relative namespace
690 push @result_namespace, $result_namespace;
691 }
692 }
693 my $table_class = join(q{::}, @result_namespace, $table_moniker);
996be9ee 694
f96ef30f 695 my $table_normalized = lc $table;
696 $self->classes->{$table} = $table_class;
697 $self->classes->{$table_normalized} = $table_class;
698 $self->monikers->{$table} = $table_moniker;
699 $self->monikers->{$table_normalized} = $table_moniker;
996be9ee 700
f96ef30f 701 $self->_use ($table_class, @{$self->additional_classes});
af31090c 702 $self->_inject($table_class, @{$self->left_base_classes});
996be9ee 703
2229729e 704 if (my @components = @{ $self->components }) {
705 $self->_dbic_stmt($table_class, 'load_components', @components);
706 }
996be9ee 707
f96ef30f 708 $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
709 if @{$self->resultset_components};
af31090c 710 $self->_inject($table_class, @{$self->additional_base_classes});
f96ef30f 711}
996be9ee 712
af31090c 713# Set up metadata (cols, pks, etc)
f96ef30f 714sub _setup_src_meta {
715 my ($self, $table) = @_;
996be9ee 716
f96ef30f 717 my $schema = $self->schema;
718 my $schema_class = $self->schema_class;
a13b2803 719
f96ef30f 720 my $table_class = $self->classes->{$table};
721 my $table_moniker = $self->monikers->{$table};
996be9ee 722
ff30991a 723 my $table_name = $table;
724 my $name_sep = $self->schema->storage->sql_maker->name_sep;
725
c177d483 726 if ($name_sep && $table_name =~ /\Q$name_sep\E/) {
ff30991a 727 $table_name = \ $self->_quote_table_name($table_name);
728 }
729
730 $self->_dbic_stmt($table_class,'table',$table_name);
996be9ee 731
f96ef30f 732 my $cols = $self->_table_columns($table);
733 my $col_info;
734 eval { $col_info = $self->_columns_info_for($table) };
735 if($@) {
736 $self->_dbic_stmt($table_class,'add_columns',@$cols);
737 }
738 else {
0906d55b 739 if ($self->_is_case_sensitive) {
740 for my $col (keys %$col_info) {
741 $col_info->{$col}{accessor} = lc $col
742 if $col ne lc($col);
743 }
744 } else {
745 $col_info = { map { lc($_), $col_info->{$_} } keys %$col_info };
c9373b79 746 }
747
e7213f4f 748 my $fks = $self->_table_fk_info($table);
565335e6 749
e7213f4f 750 for my $fkdef (@$fks) {
751 for my $col (@{ $fkdef->{local_columns} }) {
565335e6 752 $col_info->{$col}{is_foreign_key} = 1;
e7213f4f 753 }
754 }
f96ef30f 755 $self->_dbic_stmt(
756 $table_class,
757 'add_columns',
565335e6 758 map { $_, ($col_info->{$_}||{}) } @$cols
f96ef30f 759 );
996be9ee 760 }
761
d70c335f 762 my %uniq_tag; # used to eliminate duplicate uniqs
763
f96ef30f 764 my $pks = $self->_table_pk_info($table) || [];
765 @$pks ? $self->_dbic_stmt($table_class,'set_primary_key',@$pks)
766 : carp("$table has no primary key");
d70c335f 767 $uniq_tag{ join("\0", @$pks) }++ if @$pks; # pk is a uniq
996be9ee 768
f96ef30f 769 my $uniqs = $self->_table_uniq_info($table) || [];
d70c335f 770 for (@$uniqs) {
771 my ($name, $cols) = @$_;
772 next if $uniq_tag{ join("\0", @$cols) }++; # skip duplicates
773 $self->_dbic_stmt($table_class,'add_unique_constraint', $name, $cols);
774 }
775
996be9ee 776}
777
778=head2 tables
779
780Returns a sorted list of loaded tables, using the original database table
781names.
782
783=cut
784
785sub tables {
786 my $self = shift;
787
b97c2c1e 788 return keys %{$self->_tables};
996be9ee 789}
790
791# Make a moniker from a table
c39e403e 792sub _default_table2moniker {
793 my ($self, $table) = @_;
794
795 return join '', map ucfirst, split /[\W_]+/,
796 Lingua::EN::Inflect::Number::to_S(lc $table);
797}
798
996be9ee 799sub _table2moniker {
800 my ( $self, $table ) = @_;
801
802 my $moniker;
803
804 if( ref $self->moniker_map eq 'HASH' ) {
805 $moniker = $self->moniker_map->{$table};
806 }
807 elsif( ref $self->moniker_map eq 'CODE' ) {
808 $moniker = $self->moniker_map->($table);
809 }
810
c39e403e 811 $moniker ||= $self->_default_table2moniker($table);
996be9ee 812
813 return $moniker;
814}
815
816sub _load_relationships {
e8ad6491 817 my ($self, $table) = @_;
996be9ee 818
e8ad6491 819 my $tbl_fk_info = $self->_table_fk_info($table);
820 foreach my $fkdef (@$tbl_fk_info) {
821 $fkdef->{remote_source} =
822 $self->monikers->{delete $fkdef->{remote_table}};
996be9ee 823 }
26f1c8c9 824 my $tbl_uniq_info = $self->_table_uniq_info($table);
996be9ee 825
e8ad6491 826 my $local_moniker = $self->monikers->{$table};
26f1c8c9 827 my $rel_stmts = $self->{relbuilder}->generate_code($local_moniker, $tbl_fk_info, $tbl_uniq_info);
996be9ee 828
996be9ee 829 foreach my $src_class (sort keys %$rel_stmts) {
830 my $src_stmts = $rel_stmts->{$src_class};
831 foreach my $stmt (@$src_stmts) {
832 $self->_dbic_stmt($src_class,$stmt->{method},@{$stmt->{args}});
833 }
834 }
835}
836
837# Overload these in driver class:
838
839# Returns an arrayref of column names
840sub _table_columns { croak "ABSTRACT METHOD" }
841
842# Returns arrayref of pk col names
843sub _table_pk_info { croak "ABSTRACT METHOD" }
844
845# Returns an arrayref of uniqs [ [ foo => [ col1, col2 ] ], [ bar => [ ... ] ] ]
846sub _table_uniq_info { croak "ABSTRACT METHOD" }
847
848# Returns an arrayref of foreign key constraints, each
849# being a hashref with 3 keys:
850# local_columns (arrayref), remote_columns (arrayref), remote_table
851sub _table_fk_info { croak "ABSTRACT METHOD" }
852
853# Returns an array of lower case table names
854sub _tables_list { croak "ABSTRACT METHOD" }
855
856# Execute a constructive DBIC class method, with debug/dump_to_dir hooks.
857sub _dbic_stmt {
858 my $self = shift;
859 my $class = shift;
860 my $method = shift;
fbcfebdd 861 if ( $method eq 'table' ) {
862 my ($table) = @_;
863 $self->_pod( $class, "=head1 NAME" );
864 my $table_descr = $class;
865 if ( $self->can('_table_comment') ) {
866 my $comment = $self->_table_comment($table);
867 $table_descr .= " - " . $comment if $comment;
868 }
869 $self->{_class2table}{ $class } = $table;
870 $self->_pod( $class, $table_descr );
871 $self->_pod_cut( $class );
872 } elsif ( $method eq 'add_columns' ) {
873 $self->_pod( $class, "=head1 ACCESSORS" );
874 my $i = 0;
875 foreach ( @_ ) {
876 $i++;
877 next unless $i % 2;
878 $self->_pod( $class, '=head2 ' . $_ );
879 my $comment;
880 $comment = $self->_column_comment( $self->{_class2table}{$class}, ($i - 1) / 2 + 1 ) if $self->can('_column_comment');
881 $self->_pod( $class, $comment ) if $comment;
882 }
883 $self->_pod_cut( $class );
884 } elsif ( $method =~ /^(belongs_to|has_many|might_have)$/ ) {
885 $self->_pod( $class, "=head1 RELATIONS" ) unless $self->{_relations_started} { $class } ;
886 my ( $accessor, $rel_class ) = @_;
887 $self->_pod( $class, "=head2 $accessor" );
888 $self->_pod( $class, 'Type: ' . $method );
889 $self->_pod( $class, "Related object: L<$rel_class>" );
890 $self->_pod_cut( $class );
891 $self->{_relations_started} { $class } = 1;
892 }
996be9ee 893 my $args = dump(@_);
894 $args = '(' . $args . ')' if @_ < 2;
895 my $stmt = $method . $args . q{;};
896
897 warn qq|$class\->$stmt\n| if $self->debug;
996be9ee 898 $self->_raw_stmt($class, '__PACKAGE__->' . $stmt);
fbcfebdd 899 return;
996be9ee 900}
901
fbcfebdd 902# Stores a POD documentation
903sub _pod {
904 my ($self, $class, $stmt) = @_;
905 $self->_raw_stmt( $class, "\n" . $stmt );
906}
907
908sub _pod_cut {
909 my ($self, $class ) = @_;
910 $self->_raw_stmt( $class, "\n=cut\n" );
911}
912
913
996be9ee 914# Store a raw source line for a class (for dumping purposes)
915sub _raw_stmt {
916 my ($self, $class, $stmt) = @_;
af31090c 917 push(@{$self->{_dump_storage}->{$class}}, $stmt);
996be9ee 918}
919
7cab3ab7 920# Like above, but separately for the externally loaded stuff
921sub _ext_stmt {
922 my ($self, $class, $stmt) = @_;
af31090c 923 push(@{$self->{_ext_storage}->{$class}}, $stmt);
7cab3ab7 924}
925
565335e6 926sub _quote_table_name {
927 my ($self, $table) = @_;
928
929 my $qt = $self->schema->storage->sql_maker->quote_char;
930
c177d483 931 return $table unless $qt;
932
565335e6 933 if (ref $qt) {
934 return $qt->[0] . $table . $qt->[1];
935 }
936
937 return $qt . $table . $qt;
938}
939
940sub _is_case_sensitive { 0 }
941
996be9ee 942=head2 monikers
943
8f9d7ce5 944Returns a hashref of loaded table to moniker mappings. There will
996be9ee 945be two entries for each table, the original name and the "normalized"
946name, in the case that the two are different (such as databases
947that like uppercase table names, or preserve your original mixed-case
948definitions, or what-have-you).
949
950=head2 classes
951
8f9d7ce5 952Returns a hashref of table to class mappings. In some cases it will
996be9ee 953contain multiple entries per table for the original and normalized table
954names, as above in L</monikers>.
955
956=head1 SEE ALSO
957
958L<DBIx::Class::Schema::Loader>
959
be80bba7 960=head1 AUTHOR
961
962See L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
963
964=head1 LICENSE
965
966This library is free software; you can redistribute it and/or modify it under
967the same terms as Perl itself.
968
996be9ee 969=cut
970
9711;