Merge 'current' into 'back-compat'
[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
b1ad1a84 19our $VERSION = '0.04999_10';
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};
79193756 268 $self->{temp_directory} ||= File::Temp::tempdir( 'dbicXXXX',
af31090c 269 TMPDIR => 1,
270 CLEANUP => 1,
271 );
272
79193756 273 $self->{dump_directory} ||= $self->{temp_directory};
274
e8ad6491 275 $self->{relbuilder} = DBIx::Class::Schema::Loader::RelBuilder->new(
706ef173 276 $self->schema, $self->inflect_plural, $self->inflect_singular
e8ad6491 277 ) if !$self->{skip_relationships};
278
7824616e 279 $self->_check_back_compat;
9c465d2c 280
7824616e 281 $self;
282}
af31090c 283
7824616e 284sub _check_back_compat {
285 my ($self) = @_;
e8ad6491 286
7824616e 287 my $filename = $self->_get_dump_filename($self->schema_class);
288 return unless -e $filename;
289
290 open(my $fh, '<', $filename)
291 or croak "Cannot open '$filename' for reading: $!";
292
293 while (<$fh>) {
294 if (/^# Created by DBIx::Class::Schema::Loader (v\d+)\.(\d+)/) {
295 my $ver = "${1}_${2}";
296 while (1) {
297 my $compat_class = "DBIx::Class::Schema::Loader::Compat::${ver}";
298 if ($self->load_optional_class($compat_class)) {
299 no strict 'refs';
300 my $class = ref $self || $self;
301 unshift @{"${class}::ISA"}, $compat_class;
302 last;
303 }
304 $ver =~ s/\d\z// or last;
305 }
306 last;
307 }
308 }
309 close $fh;
996be9ee 310}
311
419a2eeb 312sub _find_file_in_inc {
313 my ($self, $file) = @_;
314
315 foreach my $prefix (@INC) {
af31090c 316 my $fullpath = File::Spec->catfile($prefix, $file);
317 return $fullpath if -f $fullpath
318 and Cwd::abs_path($fullpath) ne
319 Cwd::abs_path(File::Spec->catfile($self->dump_directory, $file)) || '';
419a2eeb 320 }
321
322 return;
323}
324
996be9ee 325sub _load_external {
f96ef30f 326 my ($self, $class) = @_;
327
328 my $class_path = $class;
329 $class_path =~ s{::}{/}g;
330 $class_path .= '.pm';
331
af31090c 332 my $real_inc_path = $self->_find_file_in_inc($class_path);
f96ef30f 333
af31090c 334 return if !$real_inc_path;
f96ef30f 335
336 # If we make it to here, we loaded an external definition
337 warn qq/# Loaded external class definition for '$class'\n/
338 if $self->debug;
339
f96ef30f 340 croak 'Failed to locate actual external module file for '
341 . "'$class'"
342 if !$real_inc_path;
343 open(my $fh, '<', $real_inc_path)
344 or croak "Failed to open '$real_inc_path' for reading: $!";
345 $self->_ext_stmt($class,
565ca24d 346 qq|# These lines were loaded from '$real_inc_path' found in \@INC.\n|
347 .qq|# They are now part of the custom portion of this file\n|
348 .qq|# for you to hand-edit. If you do not either delete\n|
349 .qq|# this section or remove that file from \@INC, this section\n|
350 .qq|# will be repeated redundantly when you re-create this\n|
351 .qq|# file again via Loader!\n|
f96ef30f 352 );
353 while(<$fh>) {
354 chomp;
355 $self->_ext_stmt($class, $_);
996be9ee 356 }
f96ef30f 357 $self->_ext_stmt($class,
70b72fab 358 qq|# End of lines loaded from '$real_inc_path' |
f96ef30f 359 );
360 close($fh)
361 or croak "Failed to close $real_inc_path: $!";
996be9ee 362}
363
364=head2 load
365
366Does the actual schema-construction work.
367
368=cut
369
370sub load {
371 my $self = shift;
372
b97c2c1e 373 $self->_load_tables($self->_tables_list);
374}
375
376=head2 rescan
377
a60b5b8d 378Arguments: schema
379
b97c2c1e 380Rescan the database for newly added tables. Does
a60b5b8d 381not process drops or changes. Returns a list of
382the newly added table monikers.
383
384The schema argument should be the schema class
385or object to be affected. It should probably
386be derived from the original schema_class used
387during L</load>.
b97c2c1e 388
389=cut
390
391sub rescan {
a60b5b8d 392 my ($self, $schema) = @_;
393
394 $self->{schema} = $schema;
7824616e 395 $self->_relbuilder->{schema} = $schema;
b97c2c1e 396
397 my @created;
398 my @current = $self->_tables_list;
399 foreach my $table ($self->_tables_list) {
400 if(!exists $self->{_tables}->{$table}) {
401 push(@created, $table);
402 }
403 }
404
c39e3507 405 my $loaded = $self->_load_tables(@created);
a60b5b8d 406
c39e3507 407 return map { $self->monikers->{$_} } @$loaded;
b97c2c1e 408}
409
7824616e 410sub _relbuilder {
411 my ($self) = @_;
412 $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder->new(
413 $self->schema, $self->inflect_plural, $self->inflect_singular
414 );
415}
416
b97c2c1e 417sub _load_tables {
418 my ($self, @tables) = @_;
419
f96ef30f 420 # First, use _tables_list with constraint and exclude
421 # to get a list of tables to operate on
422
423 my $constraint = $self->constraint;
424 my $exclude = $self->exclude;
f96ef30f 425
b97c2c1e 426 @tables = grep { /$constraint/ } @tables if $constraint;
427 @tables = grep { ! /$exclude/ } @tables if $exclude;
f96ef30f 428
b97c2c1e 429 # Save the new tables to the tables list
a60b5b8d 430 foreach (@tables) {
431 $self->{_tables}->{$_} = 1;
432 }
f96ef30f 433
af31090c 434 $self->_make_src_class($_) for @tables;
f96ef30f 435 $self->_setup_src_meta($_) for @tables;
436
e8ad6491 437 if(!$self->skip_relationships) {
181cc907 438 # The relationship loader needs a working schema
af31090c 439 $self->{quiet} = 1;
79193756 440 local $self->{dump_directory} = $self->{temp_directory};
181cc907 441 $self->_reload_classes(@tables);
e8ad6491 442 $self->_load_relationships($_) for @tables;
af31090c 443 $self->{quiet} = 0;
79193756 444
445 # Remove that temp dir from INC so it doesn't get reloaded
446 @INC = grep { $_ ne $self->{dump_directory} } @INC;
e8ad6491 447 }
448
f96ef30f 449 $self->_load_external($_)
75451704 450 for map { $self->classes->{$_} } @tables;
f96ef30f 451
181cc907 452 $self->_reload_classes(@tables);
996be9ee 453
5223f24a 454 # Drop temporary cache
455 delete $self->{_cache};
456
c39e3507 457 return \@tables;
996be9ee 458}
459
af31090c 460sub _reload_classes {
181cc907 461 my ($self, @tables) = @_;
462
463 $self->_dump_to_dir(map { $self->classes->{$_} } @tables);
e9b8719e 464
465 unshift @INC, $self->dump_directory;
af31090c 466
706ef173 467 my @to_register;
468 my %have_source = map { $_ => $self->schema->source($_) }
469 $self->schema->sources;
470
181cc907 471 for my $table (@tables) {
472 my $moniker = $self->monikers->{$table};
473 my $class = $self->classes->{$table};
0ae6b65d 474
475 {
476 no warnings 'redefine';
477 local *Class::C3::reinitialize = sub {};
478 use warnings;
479
706ef173 480 Class::Unload->unload($class);
481 my ($source, $resultset_class);
482 if (
483 ($source = $have_source{$moniker})
484 && ($resultset_class = $source->resultset_class)
485 && ($resultset_class ne 'DBIx::Class::ResultSet')
486 ) {
487 my $has_file = Class::Inspector->loaded_filename($resultset_class);
488 Class::Unload->unload($resultset_class);
6ae3f335 489 $self->ensure_class_loaded($resultset_class) if $has_file;
0ae6b65d 490 }
6ae3f335 491 $self->ensure_class_loaded($class);
af31090c 492 }
706ef173 493 push @to_register, [$moniker, $class];
494 }
af31090c 495
706ef173 496 Class::C3->reinitialize;
497 for (@to_register) {
498 $self->schema->register_class(@$_);
af31090c 499 }
500}
501
996be9ee 502sub _get_dump_filename {
503 my ($self, $class) = (@_);
504
505 $class =~ s{::}{/}g;
506 return $self->dump_directory . q{/} . $class . q{.pm};
507}
508
509sub _ensure_dump_subdirs {
510 my ($self, $class) = (@_);
511
512 my @name_parts = split(/::/, $class);
dd03ee1a 513 pop @name_parts; # we don't care about the very last element,
514 # which is a filename
515
996be9ee 516 my $dir = $self->dump_directory;
7cab3ab7 517 while (1) {
518 if(!-d $dir) {
25328cc4 519 mkdir($dir) or croak "mkdir('$dir') failed: $!";
996be9ee 520 }
7cab3ab7 521 last if !@name_parts;
522 $dir = File::Spec->catdir($dir, shift @name_parts);
996be9ee 523 }
524}
525
526sub _dump_to_dir {
af31090c 527 my ($self, @classes) = @_;
996be9ee 528
fc2b71fd 529 my $schema_class = $self->schema_class;
9c9c2f2b 530 my $schema_base_class = $self->schema_base_class || 'DBIx::Class::Schema';
996be9ee 531
e9b8719e 532 my $target_dir = $self->dump_directory;
af31090c 533 warn "Dumping manual schema for $schema_class to directory $target_dir ...\n"
534 unless $self->{dynamic} or $self->{quiet};
996be9ee 535
7cab3ab7 536 my $schema_text =
537 qq|package $schema_class;\n\n|
b4dcbcc5 538 . qq|# Created by DBIx::Class::Schema::Loader\n|
539 . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
7cab3ab7 540 . qq|use strict;\nuse warnings;\n\n|
9c9c2f2b 541 . qq|use base '$schema_base_class';\n\n|;
f44ecc2f 542
f44ecc2f 543 if ($self->use_namespaces) {
544 $schema_text .= qq|__PACKAGE__->load_namespaces|;
545 my $namespace_options;
546 for my $attr (qw(result_namespace
547 resultset_namespace
548 default_resultset_class)) {
549 if ($self->$attr) {
550 $namespace_options .= qq| $attr => '| . $self->$attr . qq|',\n|
551 }
552 }
553 $schema_text .= qq|(\n$namespace_options)| if $namespace_options;
554 $schema_text .= qq|;\n|;
555 }
556 else {
557 $schema_text .= qq|__PACKAGE__->load_classes;\n|;
f44ecc2f 558 }
996be9ee 559
7cab3ab7 560 $self->_write_classfile($schema_class, $schema_text);
996be9ee 561
9c9c2f2b 562 my $result_base_class = $self->result_base_class || 'DBIx::Class';
563
af31090c 564 foreach my $src_class (@classes) {
7cab3ab7 565 my $src_text =
566 qq|package $src_class;\n\n|
b4dcbcc5 567 . qq|# Created by DBIx::Class::Schema::Loader\n|
568 . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
7cab3ab7 569 . qq|use strict;\nuse warnings;\n\n|
9c9c2f2b 570 . qq|use base '$result_base_class';\n\n|;
996be9ee 571
7cab3ab7 572 $self->_write_classfile($src_class, $src_text);
02356864 573 }
996be9ee 574
af31090c 575 warn "Schema dump completed.\n" unless $self->{dynamic} or $self->{quiet};
576
7cab3ab7 577}
578
79193756 579sub _sig_comment {
580 my ($self, $version, $ts) = @_;
581 return qq|\n\n# Created by DBIx::Class::Schema::Loader|
582 . qq| v| . $version
583 . q| @ | . $ts
584 . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
585}
586
7cab3ab7 587sub _write_classfile {
588 my ($self, $class, $text) = @_;
589
590 my $filename = $self->_get_dump_filename($class);
591 $self->_ensure_dump_subdirs($class);
592
28b4691d 593 if (-f $filename && $self->really_erase_my_files) {
7cab3ab7 594 warn "Deleting existing file '$filename' due to "
af31090c 595 . "'really_erase_my_files' setting\n" unless $self->{quiet};
7cab3ab7 596 unlink($filename);
597 }
598
79193756 599 my ($custom_content, $old_md5, $old_ver, $old_ts) = $self->_get_custom_content($class, $filename);
17ca645f 600
7cab3ab7 601 $text .= qq|$_\n|
602 for @{$self->{_dump_storage}->{$class} || []};
603
79193756 604 # Check and see if the dump is infact differnt
605
606 my $compare_to;
607 if ($old_md5) {
608 $compare_to = $text . $self->_sig_comment($old_ver, $old_ts);
609
610
611 if (Digest::MD5::md5_base64($compare_to) eq $old_md5) {
612 return;
613 }
614 }
615
616 $text .= $self->_sig_comment(
617 $DBIx::Class::Schema::Loader::VERSION,
618 POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
619 );
7cab3ab7 620
621 open(my $fh, '>', $filename)
622 or croak "Cannot open '$filename' for writing: $!";
623
624 # Write the top half and its MD5 sum
a4476f41 625 print $fh $text . Digest::MD5::md5_base64($text) . "\n";
7cab3ab7 626
627 # Write out anything loaded via external partial class file in @INC
628 print $fh qq|$_\n|
629 for @{$self->{_ext_storage}->{$class} || []};
630
1eea4fb1 631 # Write out any custom content the user has added
7cab3ab7 632 print $fh $custom_content;
633
634 close($fh)
e9b8719e 635 or croak "Error closing '$filename': $!";
7cab3ab7 636}
637
79193756 638sub _default_custom_content {
639 return qq|\n\n# You can replace this text with custom|
640 . qq| content, and it will be preserved on regeneration|
641 . qq|\n1;\n|;
642}
643
7cab3ab7 644sub _get_custom_content {
645 my ($self, $class, $filename) = @_;
646
79193756 647 return ($self->_default_custom_content) if ! -f $filename;
648
7cab3ab7 649 open(my $fh, '<', $filename)
650 or croak "Cannot open '$filename' for reading: $!";
651
652 my $mark_re =
419a2eeb 653 qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
7cab3ab7 654
7cab3ab7 655 my $buffer = '';
79193756 656 my ($md5, $ts, $ver);
7cab3ab7 657 while(<$fh>) {
79193756 658 if(!$md5 && /$mark_re/) {
659 $md5 = $2;
660 my $line = $1;
661
662 # Pull out the previous version and timestamp
663 ($ver, $ts) = $buffer =~ m/# Created by DBIx::Class::Schema::Loader v(.*?) @ (.*?)$/s;
664
665 $buffer .= $line;
7cab3ab7 666 croak "Checksum mismatch in '$filename'"
79193756 667 if Digest::MD5::md5_base64($buffer) ne $md5;
7cab3ab7 668
669 $buffer = '';
670 }
671 else {
672 $buffer .= $_;
673 }
996be9ee 674 }
675
28b4691d 676 croak "Cannot not overwrite '$filename' without 'really_erase_my_files',"
419a2eeb 677 . " it does not appear to have been generated by Loader"
79193756 678 if !$md5;
5ef3c771 679
79193756 680 # Default custom content:
681 $buffer ||= $self->_default_custom_content;
5ef3c771 682
79193756 683 return ($buffer, $md5, $ver, $ts);
996be9ee 684}
685
686sub _use {
687 my $self = shift;
688 my $target = shift;
689
690 foreach (@_) {
cb54990b 691 warn "$target: use $_;" if $self->debug;
996be9ee 692 $self->_raw_stmt($target, "use $_;");
996be9ee 693 }
694}
695
696sub _inject {
697 my $self = shift;
698 my $target = shift;
699 my $schema_class = $self->schema_class;
700
af31090c 701 my $blist = join(q{ }, @_);
702 warn "$target: use base qw/ $blist /;" if $self->debug && @_;
703 $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
996be9ee 704}
705
f96ef30f 706# Create class with applicable bases, setup monikers, etc
707sub _make_src_class {
708 my ($self, $table) = @_;
996be9ee 709
a13b2803 710 my $schema = $self->schema;
711 my $schema_class = $self->schema_class;
996be9ee 712
f96ef30f 713 my $table_moniker = $self->_table2moniker($table);
f44ecc2f 714 my @result_namespace = ($schema_class);
715 if ($self->use_namespaces) {
716 my $result_namespace = $self->result_namespace || 'Result';
717 if ($result_namespace =~ /^\+(.*)/) {
718 # Fully qualified namespace
719 @result_namespace = ($1)
720 }
721 else {
722 # Relative namespace
723 push @result_namespace, $result_namespace;
724 }
725 }
726 my $table_class = join(q{::}, @result_namespace, $table_moniker);
996be9ee 727
f96ef30f 728 my $table_normalized = lc $table;
729 $self->classes->{$table} = $table_class;
730 $self->classes->{$table_normalized} = $table_class;
731 $self->monikers->{$table} = $table_moniker;
732 $self->monikers->{$table_normalized} = $table_moniker;
996be9ee 733
f96ef30f 734 $self->_use ($table_class, @{$self->additional_classes});
af31090c 735 $self->_inject($table_class, @{$self->left_base_classes});
996be9ee 736
605fcea8 737 $self->_dbic_stmt($table_class, 'load_components', @{$self->components}, 'Core');
996be9ee 738
f96ef30f 739 $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
740 if @{$self->resultset_components};
af31090c 741 $self->_inject($table_class, @{$self->additional_base_classes});
f96ef30f 742}
996be9ee 743
af31090c 744# Set up metadata (cols, pks, etc)
f96ef30f 745sub _setup_src_meta {
746 my ($self, $table) = @_;
996be9ee 747
f96ef30f 748 my $schema = $self->schema;
749 my $schema_class = $self->schema_class;
a13b2803 750
f96ef30f 751 my $table_class = $self->classes->{$table};
752 my $table_moniker = $self->monikers->{$table};
996be9ee 753
ff30991a 754 my $table_name = $table;
755 my $name_sep = $self->schema->storage->sql_maker->name_sep;
756
c177d483 757 if ($name_sep && $table_name =~ /\Q$name_sep\E/) {
ff30991a 758 $table_name = \ $self->_quote_table_name($table_name);
759 }
760
761 $self->_dbic_stmt($table_class,'table',$table_name);
996be9ee 762
f96ef30f 763 my $cols = $self->_table_columns($table);
764 my $col_info;
765 eval { $col_info = $self->_columns_info_for($table) };
766 if($@) {
767 $self->_dbic_stmt($table_class,'add_columns',@$cols);
768 }
769 else {
0906d55b 770 if ($self->_is_case_sensitive) {
771 for my $col (keys %$col_info) {
772 $col_info->{$col}{accessor} = lc $col
773 if $col ne lc($col);
774 }
775 } else {
776 $col_info = { map { lc($_), $col_info->{$_} } keys %$col_info };
c9373b79 777 }
778
e7213f4f 779 my $fks = $self->_table_fk_info($table);
565335e6 780
e7213f4f 781 for my $fkdef (@$fks) {
782 for my $col (@{ $fkdef->{local_columns} }) {
565335e6 783 $col_info->{$col}{is_foreign_key} = 1;
e7213f4f 784 }
785 }
f96ef30f 786 $self->_dbic_stmt(
787 $table_class,
788 'add_columns',
565335e6 789 map { $_, ($col_info->{$_}||{}) } @$cols
f96ef30f 790 );
996be9ee 791 }
792
d70c335f 793 my %uniq_tag; # used to eliminate duplicate uniqs
794
f96ef30f 795 my $pks = $self->_table_pk_info($table) || [];
796 @$pks ? $self->_dbic_stmt($table_class,'set_primary_key',@$pks)
797 : carp("$table has no primary key");
d70c335f 798 $uniq_tag{ join("\0", @$pks) }++ if @$pks; # pk is a uniq
996be9ee 799
f96ef30f 800 my $uniqs = $self->_table_uniq_info($table) || [];
d70c335f 801 for (@$uniqs) {
802 my ($name, $cols) = @$_;
803 next if $uniq_tag{ join("\0", @$cols) }++; # skip duplicates
804 $self->_dbic_stmt($table_class,'add_unique_constraint', $name, $cols);
805 }
806
996be9ee 807}
808
809=head2 tables
810
811Returns a sorted list of loaded tables, using the original database table
812names.
813
814=cut
815
816sub tables {
817 my $self = shift;
818
b97c2c1e 819 return keys %{$self->_tables};
996be9ee 820}
821
822# Make a moniker from a table
c39e403e 823sub _default_table2moniker {
824 my ($self, $table) = @_;
825
826 return join '', map ucfirst, split /[\W_]+/,
827 Lingua::EN::Inflect::Number::to_S(lc $table);
828}
829
996be9ee 830sub _table2moniker {
831 my ( $self, $table ) = @_;
832
833 my $moniker;
834
835 if( ref $self->moniker_map eq 'HASH' ) {
836 $moniker = $self->moniker_map->{$table};
837 }
838 elsif( ref $self->moniker_map eq 'CODE' ) {
839 $moniker = $self->moniker_map->($table);
840 }
841
c39e403e 842 $moniker ||= $self->_default_table2moniker($table);
996be9ee 843
844 return $moniker;
845}
846
847sub _load_relationships {
e8ad6491 848 my ($self, $table) = @_;
996be9ee 849
e8ad6491 850 my $tbl_fk_info = $self->_table_fk_info($table);
851 foreach my $fkdef (@$tbl_fk_info) {
852 $fkdef->{remote_source} =
853 $self->monikers->{delete $fkdef->{remote_table}};
996be9ee 854 }
26f1c8c9 855 my $tbl_uniq_info = $self->_table_uniq_info($table);
996be9ee 856
e8ad6491 857 my $local_moniker = $self->monikers->{$table};
7824616e 858 my $rel_stmts = $self->_relbuilder->generate_code($local_moniker, $tbl_fk_info, $tbl_uniq_info);
996be9ee 859
996be9ee 860 foreach my $src_class (sort keys %$rel_stmts) {
861 my $src_stmts = $rel_stmts->{$src_class};
862 foreach my $stmt (@$src_stmts) {
863 $self->_dbic_stmt($src_class,$stmt->{method},@{$stmt->{args}});
864 }
865 }
866}
867
868# Overload these in driver class:
869
870# Returns an arrayref of column names
871sub _table_columns { croak "ABSTRACT METHOD" }
872
873# Returns arrayref of pk col names
874sub _table_pk_info { croak "ABSTRACT METHOD" }
875
876# Returns an arrayref of uniqs [ [ foo => [ col1, col2 ] ], [ bar => [ ... ] ] ]
877sub _table_uniq_info { croak "ABSTRACT METHOD" }
878
879# Returns an arrayref of foreign key constraints, each
880# being a hashref with 3 keys:
881# local_columns (arrayref), remote_columns (arrayref), remote_table
882sub _table_fk_info { croak "ABSTRACT METHOD" }
883
884# Returns an array of lower case table names
885sub _tables_list { croak "ABSTRACT METHOD" }
886
887# Execute a constructive DBIC class method, with debug/dump_to_dir hooks.
888sub _dbic_stmt {
889 my $self = shift;
890 my $class = shift;
891 my $method = shift;
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);
899}
900
901# Store a raw source line for a class (for dumping purposes)
902sub _raw_stmt {
903 my ($self, $class, $stmt) = @_;
af31090c 904 push(@{$self->{_dump_storage}->{$class}}, $stmt);
996be9ee 905}
906
7cab3ab7 907# Like above, but separately for the externally loaded stuff
908sub _ext_stmt {
909 my ($self, $class, $stmt) = @_;
af31090c 910 push(@{$self->{_ext_storage}->{$class}}, $stmt);
7cab3ab7 911}
912
565335e6 913sub _quote_table_name {
914 my ($self, $table) = @_;
915
916 my $qt = $self->schema->storage->sql_maker->quote_char;
917
c177d483 918 return $table unless $qt;
919
565335e6 920 if (ref $qt) {
921 return $qt->[0] . $table . $qt->[1];
922 }
923
924 return $qt . $table . $qt;
925}
926
927sub _is_case_sensitive { 0 }
928
996be9ee 929=head2 monikers
930
8f9d7ce5 931Returns a hashref of loaded table to moniker mappings. There will
996be9ee 932be two entries for each table, the original name and the "normalized"
933name, in the case that the two are different (such as databases
934that like uppercase table names, or preserve your original mixed-case
935definitions, or what-have-you).
936
937=head2 classes
938
8f9d7ce5 939Returns a hashref of table to class mappings. In some cases it will
996be9ee 940contain multiple entries per table for the original and normalized table
941names, as above in L</monikers>.
942
943=head1 SEE ALSO
944
945L<DBIx::Class::Schema::Loader>
946
947=cut
948
9491;