minor changes
[dbsrgits/DBIx-Class-Schema-Loader.git] / lib / DBIx / Class / Schema / Loader / Base.pm
1 package DBIx::Class::Schema::Loader::Base;
2
3 use strict;
4 use warnings;
5 use base qw/Class::Accessor::Fast Class::C3::Componentised/;
6 use Class::C3;
7 use Carp::Clan qw/^DBIx::Class/;
8 use DBIx::Class::Schema::Loader::RelBuilder;
9 use Data::Dump qw/ dump /;
10 use POSIX qw//;
11 use File::Spec qw//;
12 use Cwd qw//;
13 use Digest::MD5 qw//;
14 use Lingua::EN::Inflect::Number qw//;
15 use File::Temp qw//;
16 use Class::Unload;
17 require DBIx::Class;
18
19 our $VERSION = '0.04999_10';
20
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
32                                 skip_relationships
33                                 moniker_map
34                                 inflect_singular
35                                 inflect_plural
36                                 debug
37                                 dump_directory
38                                 dump_overwrite
39                                 really_erase_my_files
40                                 use_namespaces
41                                 result_namespace
42                                 resultset_namespace
43                                 default_resultset_class
44                                 schema_base_class
45                                 result_base_class
46
47                                 db_schema
48                                 _tables
49                                 classes
50                                 monikers
51                              /);
52
53 __PACKAGE__->mk_accessors(qw/
54                                 version_to_dump
55 /);
56
57 =head1 NAME
58
59 DBIx::Class::Schema::Loader::Base - Base DBIx::Class::Schema::Loader Implementation.
60
61 =head1 SYNOPSIS
62
63 See L<DBIx::Class::Schema::Loader>
64
65 =head1 DESCRIPTION
66
67 This is the base class for the storage-specific C<DBIx::Class::Schema::*>
68 classes, and implements the common functionality between them.
69
70 =head1 CONSTRUCTOR OPTIONS
71
72 These constructor options are the base options for
73 L<DBIx::Class::Schema::Loader/loader_options>.  Available constructor options are:
74
75 =head2 skip_relationships
76
77 Skip setting up relationships.  The default is to attempt the loading
78 of relationships.
79
80 =head2 naming
81
82 Static schemas (ones dumped to disk) will, by default, use the new-style 0.05XXX
83 relationship names and singularized Results, unless you're overwriting an
84 existing dump made by a 0.04XXX version of L<DBIx::Class::Schema::Loader>, in
85 which case the backward compatible RelBuilder will be activated, and
86 singularization will be turned off.
87
88 Specifying
89
90     naming => 'v5'
91
92 will disable the backward-compatible RelBuilder and use
93 the new-style relationship names along with singularized Results, even when
94 overwriting a dump made with an earlier version.
95
96 The option also takes a hashref:
97
98     naming => { relationships => 'v5', results => 'v4' }
99
100 The values can be:
101
102 =over 4
103
104 =item current
105
106 Latest default style, whatever that happens to be.
107
108 =item v5
109
110 Version 0.05XXX style.
111
112 =item v4
113
114 Version 0.04XXX style.
115
116 =back
117
118 Dynamic schemas will always default to the 0.04XXX relationship names and won't
119 singularize Results for backward compatibility, to activate the new RelBuilder
120 and singularization put this in your C<Schema.pm> file:
121
122     __PACKAGE__->naming('current');
123
124 Or if you prefer to use 0.05XXX features but insure that nothing breaks in the
125 next major version upgrade:
126
127     __PACKAGE__->naming('v5');
128
129 =head2 debug
130
131 If set to true, each constructive L<DBIx::Class> statement the loader
132 decides to execute will be C<warn>-ed before execution.
133
134 =head2 db_schema
135
136 Set the name of the schema to load (schema in the sense that your database
137 vendor means it).  Does not currently support loading more than one schema
138 name.
139
140 =head2 constraint
141
142 Only load tables matching regex.  Best specified as a qr// regex.
143
144 =head2 exclude
145
146 Exclude tables matching regex.  Best specified as a qr// regex.
147
148 =head2 moniker_map
149
150 Overrides the default table name to moniker translation.  Can be either
151 a hashref of table keys and moniker values, or a coderef for a translator
152 function taking a single scalar table name argument and returning
153 a scalar moniker.  If the hash entry does not exist, or the function
154 returns a false value, the code falls back to default behavior
155 for that table name.
156
157 The default behavior is: C<join '', map ucfirst, split /[\W_]+/, lc $table>,
158 which is to say: lowercase everything, split up the table name into chunks
159 anywhere a non-alpha-numeric character occurs, change the case of first letter
160 of each chunk to upper case, and put the chunks back together.  Examples:
161
162     Table Name  | Moniker Name
163     ---------------------------
164     luser       | Luser
165     luser_group | LuserGroup
166     luser-opts  | LuserOpts
167
168 =head2 inflect_plural
169
170 Just like L</moniker_map> above (can be hash/code-ref, falls back to default
171 if hash key does not exist or coderef returns false), but acts as a map
172 for pluralizing relationship names.  The default behavior is to utilize
173 L<Lingua::EN::Inflect::Number/to_PL>.
174
175 =head2 inflect_singular
176
177 As L</inflect_plural> above, but for singularizing relationship names.
178 Default behavior is to utilize L<Lingua::EN::Inflect::Number/to_S>.
179
180 =head2 schema_base_class
181
182 Base class for your schema classes. Defaults to 'DBIx::Class::Schema'.
183
184 =head2 result_base_class
185
186 Base class for your table classes (aka result classes). Defaults to 'DBIx::Class'.
187
188 =head2 additional_base_classes
189
190 List of additional base classes all of your table classes will use.
191
192 =head2 left_base_classes
193
194 List of additional base classes all of your table classes will use
195 that need to be leftmost.
196
197 =head2 additional_classes
198
199 List of additional classes which all of your table classes will use.
200
201 =head2 components
202
203 List of additional components to be loaded into all of your table
204 classes.  A good example would be C<ResultSetManager>.
205
206 =head2 resultset_components
207
208 List of additional ResultSet components to be loaded into your table
209 classes.  A good example would be C<AlwaysRS>.  Component
210 C<ResultSetManager> will be automatically added to the above
211 C<components> list if this option is set.
212
213 =head2 use_namespaces
214
215 Generate result class names suitable for
216 L<DBIx::Class::Schema/load_namespaces> and call that instead of
217 L<DBIx::Class::Schema/load_classes>. When using this option you can also
218 specify any of the options for C<load_namespaces> (i.e. C<result_namespace>,
219 C<resultset_namespace>, C<default_resultset_class>), and they will be added
220 to the call (and the generated result class names adjusted appropriately).
221
222 =head2 dump_directory
223
224 This option is designed to be a tool to help you transition from this
225 loader to a manually-defined schema when you decide it's time to do so.
226
227 The value of this option is a perl libdir pathname.  Within
228 that directory this module will create a baseline manual
229 L<DBIx::Class::Schema> module set, based on what it creates at runtime
230 in memory.
231
232 The created schema class will have the same classname as the one on
233 which you are setting this option (and the ResultSource classes will be
234 based on this name as well).
235
236 Normally you wouldn't hard-code this setting in your schema class, as it
237 is meant for one-time manual usage.
238
239 See L<DBIx::Class::Schema::Loader/dump_to_dir> for examples of the
240 recommended way to access this functionality.
241
242 =head2 dump_overwrite
243
244 Deprecated.  See L</really_erase_my_files> below, which does *not* mean
245 the same thing as the old C<dump_overwrite> setting from previous releases.
246
247 =head2 really_erase_my_files
248
249 Default false.  If true, Loader will unconditionally delete any existing
250 files before creating the new ones from scratch when dumping a schema to disk.
251
252 The default behavior is instead to only replace the top portion of the
253 file, up to and including the final stanza which contains
254 C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!>
255 leaving any customizations you placed after that as they were.
256
257 When C<really_erase_my_files> is not set, if the output file already exists,
258 but the aforementioned final stanza is not found, or the checksum
259 contained there does not match the generated contents, Loader will
260 croak and not touch the file.
261
262 You should really be using version control on your schema classes (and all
263 of the rest of your code for that matter).  Don't blame me if a bug in this
264 code wipes something out when it shouldn't have, you've been warned.
265
266 =head1 METHODS
267
268 None of these methods are intended for direct invocation by regular
269 users of L<DBIx::Class::Schema::Loader>.  Anything you can find here
270 can also be found via standard L<DBIx::Class::Schema> methods somehow.
271
272 =cut
273
274 # ensure that a peice of object data is a valid arrayref, creating
275 # an empty one or encapsulating whatever's there.
276 sub _ensure_arrayref {
277     my $self = shift;
278
279     foreach (@_) {
280         $self->{$_} ||= [];
281         $self->{$_} = [ $self->{$_} ]
282             unless ref $self->{$_} eq 'ARRAY';
283     }
284 }
285
286 =head2 new
287
288 Constructor for L<DBIx::Class::Schema::Loader::Base>, used internally
289 by L<DBIx::Class::Schema::Loader>.
290
291 =cut
292
293 sub new {
294     my ( $class, %args ) = @_;
295
296     my $self = { %args };
297
298     bless $self => $class;
299
300     $self->_ensure_arrayref(qw/additional_classes
301                                additional_base_classes
302                                left_base_classes
303                                components
304                                resultset_components
305                               /);
306
307     push(@{$self->{components}}, 'ResultSetManager')
308         if @{$self->{resultset_components}};
309
310     $self->{monikers} = {};
311     $self->{classes} = {};
312
313     $self->{schema_class} ||= ( ref $self->{schema} || $self->{schema} );
314     $self->{schema} ||= $self->{schema_class};
315
316     croak "dump_overwrite is deprecated.  Please read the"
317         . " DBIx::Class::Schema::Loader::Base documentation"
318             if $self->{dump_overwrite};
319
320     $self->{dynamic} = ! $self->{dump_directory};
321     $self->{temp_directory} ||= File::Temp::tempdir( 'dbicXXXX',
322                                                      TMPDIR  => 1,
323                                                      CLEANUP => 1,
324                                                    );
325
326     $self->{dump_directory} ||= $self->{temp_directory};
327
328     $self->version_to_dump($DBIx::Class::Schema::Loader::VERSION);
329
330     $self->_check_back_compat;
331
332     $self;
333 }
334
335 sub _check_back_compat {
336     my ($self) = @_;
337
338 # dynamic schemas will always be in 0.04006 mode
339     if ($self->{dynamic}) {
340         no strict 'refs';
341         my $class = ref $self || $self;
342         require DBIx::Class::Schema::Loader::Compat::v0_040;
343         unshift @{"${class}::ISA"},
344             'DBIx::Class::Schema::Loader::Compat::v0_040';
345         Class::C3::reinitialize;
346 # just in case, though no one is likely to dump a dynamic schema
347         $self->version_to_dump('0.04006');
348         return;
349     }
350
351 # otherwise check if we need backcompat mode for a static schema
352     my $filename = $self->_get_dump_filename($self->schema_class);
353     return unless -e $filename;
354
355     open(my $fh, '<', $filename)
356         or croak "Cannot open '$filename' for reading: $!";
357
358     while (<$fh>) {
359         if (/^# Created by DBIx::Class::Schema::Loader v((\d+)\.(\d+))/) {
360             my $real_ver = $1;
361             my $ver      = "v${2}_${3}";
362             while (1) {
363                 my $compat_class = "DBIx::Class::Schema::Loader::Compat::${ver}";
364                 if ($self->load_optional_class($compat_class)) {
365                     no strict 'refs';
366                     my $class = ref $self || $self;
367                     unshift @{"${class}::ISA"}, $compat_class;
368                     Class::C3::reinitialize;
369                     $self->version_to_dump($real_ver);
370                     last;
371                 }
372                 $ver =~ s/\d\z// or last;
373             }
374             last;
375         }
376     }
377     close $fh;
378 }
379
380 sub _find_file_in_inc {
381     my ($self, $file) = @_;
382
383     foreach my $prefix (@INC) {
384         my $fullpath = File::Spec->catfile($prefix, $file);
385         return $fullpath if -f $fullpath
386             and Cwd::abs_path($fullpath) ne
387                 Cwd::abs_path(File::Spec->catfile($self->dump_directory, $file)) || '';
388     }
389
390     return;
391 }
392
393 sub _class_path {
394     my ($self, $class) = @_;
395
396     my $class_path = $class;
397     $class_path =~ s{::}{/}g;
398     $class_path .= '.pm';
399
400     return $class_path;
401 }
402
403 sub _find_class_in_inc {
404     my ($self, $class) = @_;
405
406     return $self->_find_file_in_inc($self->_class_path($class));
407 }
408
409 sub _load_external {
410     my ($self, $class) = @_;
411
412     my $real_inc_path = $self->_find_class_in_inc($class);
413
414     return if !$real_inc_path;
415
416     # If we make it to here, we loaded an external definition
417     warn qq/# Loaded external class definition for '$class'\n/
418         if $self->debug;
419
420     open(my $fh, '<', $real_inc_path)
421         or croak "Failed to open '$real_inc_path' for reading: $!";
422     $self->_ext_stmt($class,
423          qq|# These lines were loaded from '$real_inc_path' found in \@INC.\n|
424         .qq|# They are now part of the custom portion of this file\n|
425         .qq|# for you to hand-edit.  If you do not either delete\n|
426         .qq|# this section or remove that file from \@INC, this section\n|
427         .qq|# will be repeated redundantly when you re-create this\n|
428         .qq|# file again via Loader!\n|
429     );
430     while(<$fh>) {
431         chomp;
432         $self->_ext_stmt($class, $_);
433     }
434     $self->_ext_stmt($class,
435         qq|# End of lines loaded from '$real_inc_path' |
436     );
437     close($fh)
438         or croak "Failed to close $real_inc_path: $!";
439 }
440
441 =head2 load
442
443 Does the actual schema-construction work.
444
445 =cut
446
447 sub load {
448     my $self = shift;
449
450     $self->_load_tables($self->_tables_list);
451 }
452
453 =head2 rescan
454
455 Arguments: schema
456
457 Rescan the database for newly added tables.  Does
458 not process drops or changes.  Returns a list of
459 the newly added table monikers.
460
461 The schema argument should be the schema class
462 or object to be affected.  It should probably
463 be derived from the original schema_class used
464 during L</load>.
465
466 =cut
467
468 sub rescan {
469     my ($self, $schema) = @_;
470
471     $self->{schema} = $schema;
472     $self->_relbuilder->{schema} = $schema;
473
474     my @created;
475     my @current = $self->_tables_list;
476     foreach my $table ($self->_tables_list) {
477         if(!exists $self->{_tables}->{$table}) {
478             push(@created, $table);
479         }
480     }
481
482     my $loaded = $self->_load_tables(@created);
483
484     return map { $self->monikers->{$_} } @$loaded;
485 }
486
487 sub _relbuilder {
488     my ($self) = @_;
489
490     return if $self->{skip_relationships};
491
492     $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder->new(
493         $self->schema, $self->inflect_plural, $self->inflect_singular
494     );
495 }
496
497 sub _load_tables {
498     my ($self, @tables) = @_;
499
500     # First, use _tables_list with constraint and exclude
501     #  to get a list of tables to operate on
502
503     my $constraint   = $self->constraint;
504     my $exclude      = $self->exclude;
505
506     @tables = grep { /$constraint/ } @tables if $constraint;
507     @tables = grep { ! /$exclude/ } @tables if $exclude;
508
509     # Save the new tables to the tables list
510     foreach (@tables) {
511         $self->{_tables}->{$_} = 1;
512     }
513
514     $self->_make_src_class($_) for @tables;
515     $self->_setup_src_meta($_) for @tables;
516
517     if(!$self->skip_relationships) {
518         # The relationship loader needs a working schema
519         $self->{quiet} = 1;
520         local $self->{dump_directory} = $self->{temp_directory};
521         $self->_reload_classes(@tables);
522         $self->_load_relationships($_) for @tables;
523         $self->{quiet} = 0;
524
525         # Remove that temp dir from INC so it doesn't get reloaded
526         @INC = grep { $_ ne $self->{dump_directory} } @INC;
527     }
528
529     $self->_load_external($_)
530         for map { $self->classes->{$_} } @tables;
531
532     $self->_reload_classes(@tables);
533
534     # Drop temporary cache
535     delete $self->{_cache};
536
537     return \@tables;
538 }
539
540 sub _reload_classes {
541     my ($self, @tables) = @_;
542
543     # so that we don't repeat custom sections
544     @INC = grep $_ ne $self->dump_directory, @INC;
545
546     $self->_dump_to_dir(map { $self->classes->{$_} } @tables);
547
548     unshift @INC, $self->dump_directory;
549     
550     my @to_register;
551     my %have_source = map { $_ => $self->schema->source($_) }
552         $self->schema->sources;
553
554     for my $table (@tables) {
555         my $moniker = $self->monikers->{$table};
556         my $class = $self->classes->{$table};
557         
558         {
559             no warnings 'redefine';
560             local *Class::C3::reinitialize = sub {};
561             use warnings;
562
563             Class::Unload->unload($class);
564             my ($source, $resultset_class);
565             if (
566                 ($source = $have_source{$moniker})
567                 && ($resultset_class = $source->resultset_class)
568                 && ($resultset_class ne 'DBIx::Class::ResultSet')
569             ) {
570                 my $has_file = Class::Inspector->loaded_filename($resultset_class);
571                 Class::Unload->unload($resultset_class);
572                 $self->ensure_class_loaded($resultset_class) if $has_file;
573             }
574             $self->ensure_class_loaded($class);
575         }
576         push @to_register, [$moniker, $class];
577     }
578
579     Class::C3->reinitialize;
580     for (@to_register) {
581         $self->schema->register_class(@$_);
582     }
583 }
584
585 sub _get_dump_filename {
586     my ($self, $class) = (@_);
587
588     $class =~ s{::}{/}g;
589     return $self->dump_directory . q{/} . $class . q{.pm};
590 }
591
592 sub _ensure_dump_subdirs {
593     my ($self, $class) = (@_);
594
595     my @name_parts = split(/::/, $class);
596     pop @name_parts; # we don't care about the very last element,
597                      # which is a filename
598
599     my $dir = $self->dump_directory;
600     while (1) {
601         if(!-d $dir) {
602             mkdir($dir) or croak "mkdir('$dir') failed: $!";
603         }
604         last if !@name_parts;
605         $dir = File::Spec->catdir($dir, shift @name_parts);
606     }
607 }
608
609 sub _dump_to_dir {
610     my ($self, @classes) = @_;
611
612     my $schema_class = $self->schema_class;
613     my $schema_base_class = $self->schema_base_class || 'DBIx::Class::Schema';
614
615     my $target_dir = $self->dump_directory;
616     warn "Dumping manual schema for $schema_class to directory $target_dir ...\n"
617         unless $self->{dynamic} or $self->{quiet};
618
619     my $schema_text =
620           qq|package $schema_class;\n\n|
621         . qq|# Created by DBIx::Class::Schema::Loader\n|
622         . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
623         . qq|use strict;\nuse warnings;\n\n|
624         . qq|use base '$schema_base_class';\n\n|;
625
626     if ($self->use_namespaces) {
627         $schema_text .= qq|__PACKAGE__->load_namespaces|;
628         my $namespace_options;
629         for my $attr (qw(result_namespace
630                          resultset_namespace
631                          default_resultset_class)) {
632             if ($self->$attr) {
633                 $namespace_options .= qq|    $attr => '| . $self->$attr . qq|',\n|
634             }
635         }
636         $schema_text .= qq|(\n$namespace_options)| if $namespace_options;
637         $schema_text .= qq|;\n|;
638     }
639     else {
640         $schema_text .= qq|__PACKAGE__->load_classes;\n|;
641     }
642
643     $self->_write_classfile($schema_class, $schema_text);
644
645     my $result_base_class = $self->result_base_class || 'DBIx::Class';
646
647     foreach my $src_class (@classes) {
648         my $src_text = 
649               qq|package $src_class;\n\n|
650             . qq|# Created by DBIx::Class::Schema::Loader\n|
651             . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
652             . qq|use strict;\nuse warnings;\n\n|
653             . qq|use base '$result_base_class';\n\n|;
654
655         $self->_write_classfile($src_class, $src_text);
656     }
657
658     warn "Schema dump completed.\n" unless $self->{dynamic} or $self->{quiet};
659
660 }
661
662 sub _sig_comment {
663     my ($self, $version, $ts) = @_;
664     return qq|\n\n# Created by DBIx::Class::Schema::Loader|
665          . qq| v| . $version
666          . q| @ | . $ts 
667          . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
668 }
669
670 sub _write_classfile {
671     my ($self, $class, $text) = @_;
672
673     my $filename = $self->_get_dump_filename($class);
674     $self->_ensure_dump_subdirs($class);
675
676     if (-f $filename && $self->really_erase_my_files) {
677         warn "Deleting existing file '$filename' due to "
678             . "'really_erase_my_files' setting\n" unless $self->{quiet};
679         unlink($filename);
680     }    
681
682     my ($custom_content, $old_md5, $old_ver, $old_ts) = $self->_get_custom_content($class, $filename);
683
684     $text .= qq|$_\n|
685         for @{$self->{_dump_storage}->{$class} || []};
686
687     # Check and see if the dump is infact differnt
688
689     my $compare_to;
690     if ($old_md5) {
691       $compare_to = $text . $self->_sig_comment($old_ver, $old_ts);
692       
693
694       if (Digest::MD5::md5_base64($compare_to) eq $old_md5) {
695         return;
696       }
697     }
698
699     $text .= $self->_sig_comment(
700       $self->version_to_dump,
701       POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
702     );
703
704     open(my $fh, '>', $filename)
705         or croak "Cannot open '$filename' for writing: $!";
706
707     # Write the top half and its MD5 sum
708     print $fh $text . Digest::MD5::md5_base64($text) . "\n";
709
710     # Write out anything loaded via external partial class file in @INC
711     print $fh qq|$_\n|
712         for @{$self->{_ext_storage}->{$class} || []};
713
714     # Write out any custom content the user has added
715     print $fh $custom_content;
716
717     close($fh)
718         or croak "Error closing '$filename': $!";
719 }
720
721 sub _default_custom_content {
722     return qq|\n\n# You can replace this text with custom|
723          . qq| content, and it will be preserved on regeneration|
724          . qq|\n1;\n|;
725 }
726
727 sub _get_custom_content {
728     my ($self, $class, $filename) = @_;
729
730     return ($self->_default_custom_content) if ! -f $filename;
731
732     open(my $fh, '<', $filename)
733         or croak "Cannot open '$filename' for reading: $!";
734
735     my $mark_re = 
736         qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
737
738     my $buffer = '';
739     my ($md5, $ts, $ver);
740     while(<$fh>) {
741         if(!$md5 && /$mark_re/) {
742             $md5 = $2;
743             my $line = $1;
744
745             # Pull out the previous version and timestamp
746             ($ver, $ts) = $buffer =~ m/# Created by DBIx::Class::Schema::Loader v(.*?) @ (.*?)$/s;
747
748             $buffer .= $line;
749             croak "Checksum mismatch in '$filename'"
750                 if Digest::MD5::md5_base64($buffer) ne $md5;
751
752             $buffer = '';
753         }
754         else {
755             $buffer .= $_;
756         }
757     }
758
759     croak "Cannot not overwrite '$filename' without 'really_erase_my_files',"
760         . " it does not appear to have been generated by Loader"
761             if !$md5;
762
763     # Default custom content:
764     $buffer ||= $self->_default_custom_content;
765
766     return ($buffer, $md5, $ver, $ts);
767 }
768
769 sub _use {
770     my $self = shift;
771     my $target = shift;
772
773     foreach (@_) {
774         warn "$target: use $_;" if $self->debug;
775         $self->_raw_stmt($target, "use $_;");
776     }
777 }
778
779 sub _inject {
780     my $self = shift;
781     my $target = shift;
782     my $schema_class = $self->schema_class;
783
784     my $blist = join(q{ }, @_);
785     warn "$target: use base qw/ $blist /;" if $self->debug && @_;
786     $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
787 }
788
789 # Create class with applicable bases, setup monikers, etc
790 sub _make_src_class {
791     my ($self, $table) = @_;
792
793     my $schema       = $self->schema;
794     my $schema_class = $self->schema_class;
795
796     my $table_moniker = $self->_table2moniker($table);
797     my @result_namespace = ($schema_class);
798     if ($self->use_namespaces) {
799         my $result_namespace = $self->result_namespace || 'Result';
800         if ($result_namespace =~ /^\+(.*)/) {
801             # Fully qualified namespace
802             @result_namespace =  ($1)
803         }
804         else {
805             # Relative namespace
806             push @result_namespace, $result_namespace;
807         }
808     }
809     my $table_class = join(q{::}, @result_namespace, $table_moniker);
810
811     my $table_normalized = lc $table;
812     $self->classes->{$table} = $table_class;
813     $self->classes->{$table_normalized} = $table_class;
814     $self->monikers->{$table} = $table_moniker;
815     $self->monikers->{$table_normalized} = $table_moniker;
816
817     $self->_use   ($table_class, @{$self->additional_classes});
818     $self->_inject($table_class, @{$self->left_base_classes});
819
820     $self->_dbic_stmt($table_class, 'load_components', @{$self->components}, 'Core');
821
822     $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
823         if @{$self->resultset_components};
824     $self->_inject($table_class, @{$self->additional_base_classes});
825 }
826
827 # Set up metadata (cols, pks, etc)
828 sub _setup_src_meta {
829     my ($self, $table) = @_;
830
831     my $schema       = $self->schema;
832     my $schema_class = $self->schema_class;
833
834     my $table_class = $self->classes->{$table};
835     my $table_moniker = $self->monikers->{$table};
836
837     my $table_name = $table;
838     my $name_sep   = $self->schema->storage->sql_maker->name_sep;
839
840     if ($name_sep && $table_name =~ /\Q$name_sep\E/) {
841         $table_name = \ $self->_quote_table_name($table_name);
842     }
843
844     $self->_dbic_stmt($table_class,'table',$table_name);
845
846     my $cols = $self->_table_columns($table);
847     my $col_info;
848     eval { $col_info = $self->_columns_info_for($table) };
849     if($@) {
850         $self->_dbic_stmt($table_class,'add_columns',@$cols);
851     }
852     else {
853         if ($self->_is_case_sensitive) {
854             for my $col (keys %$col_info) {
855                 $col_info->{$col}{accessor} = lc $col
856                     if $col ne lc($col);
857             }
858         } else {
859             $col_info = { map { lc($_), $col_info->{$_} } keys %$col_info };
860         }
861
862         my $fks = $self->_table_fk_info($table);
863
864         for my $fkdef (@$fks) {
865             for my $col (@{ $fkdef->{local_columns} }) {
866                 $col_info->{$col}{is_foreign_key} = 1;
867             }
868         }
869         $self->_dbic_stmt(
870             $table_class,
871             'add_columns',
872             map { $_, ($col_info->{$_}||{}) } @$cols
873         );
874     }
875
876     my %uniq_tag; # used to eliminate duplicate uniqs
877
878     my $pks = $self->_table_pk_info($table) || [];
879     @$pks ? $self->_dbic_stmt($table_class,'set_primary_key',@$pks)
880           : carp("$table has no primary key");
881     $uniq_tag{ join("\0", @$pks) }++ if @$pks; # pk is a uniq
882
883     my $uniqs = $self->_table_uniq_info($table) || [];
884     for (@$uniqs) {
885         my ($name, $cols) = @$_;
886         next if $uniq_tag{ join("\0", @$cols) }++; # skip duplicates
887         $self->_dbic_stmt($table_class,'add_unique_constraint', $name, $cols);
888     }
889
890 }
891
892 =head2 tables
893
894 Returns a sorted list of loaded tables, using the original database table
895 names.
896
897 =cut
898
899 sub tables {
900     my $self = shift;
901
902     return keys %{$self->_tables};
903 }
904
905 # Make a moniker from a table
906 sub _default_table2moniker {
907     my ($self, $table) = @_;
908
909     return join '', map ucfirst, split /[\W_]+/,
910         Lingua::EN::Inflect::Number::to_S(lc $table);
911 }
912
913 sub _table2moniker {
914     my ( $self, $table ) = @_;
915
916     my $moniker;
917
918     if( ref $self->moniker_map eq 'HASH' ) {
919         $moniker = $self->moniker_map->{$table};
920     }
921     elsif( ref $self->moniker_map eq 'CODE' ) {
922         $moniker = $self->moniker_map->($table);
923     }
924
925     $moniker ||= $self->_default_table2moniker($table);
926
927     return $moniker;
928 }
929
930 sub _load_relationships {
931     my ($self, $table) = @_;
932
933     my $tbl_fk_info = $self->_table_fk_info($table);
934     foreach my $fkdef (@$tbl_fk_info) {
935         $fkdef->{remote_source} =
936             $self->monikers->{delete $fkdef->{remote_table}};
937     }
938     my $tbl_uniq_info = $self->_table_uniq_info($table);
939
940     my $local_moniker = $self->monikers->{$table};
941     my $rel_stmts = $self->_relbuilder->generate_code($local_moniker, $tbl_fk_info, $tbl_uniq_info);
942
943     foreach my $src_class (sort keys %$rel_stmts) {
944         my $src_stmts = $rel_stmts->{$src_class};
945         foreach my $stmt (@$src_stmts) {
946             $self->_dbic_stmt($src_class,$stmt->{method},@{$stmt->{args}});
947         }
948     }
949 }
950
951 # Overload these in driver class:
952
953 # Returns an arrayref of column names
954 sub _table_columns { croak "ABSTRACT METHOD" }
955
956 # Returns arrayref of pk col names
957 sub _table_pk_info { croak "ABSTRACT METHOD" }
958
959 # Returns an arrayref of uniqs [ [ foo => [ col1, col2 ] ], [ bar => [ ... ] ] ]
960 sub _table_uniq_info { croak "ABSTRACT METHOD" }
961
962 # Returns an arrayref of foreign key constraints, each
963 #   being a hashref with 3 keys:
964 #   local_columns (arrayref), remote_columns (arrayref), remote_table
965 sub _table_fk_info { croak "ABSTRACT METHOD" }
966
967 # Returns an array of lower case table names
968 sub _tables_list { croak "ABSTRACT METHOD" }
969
970 # Execute a constructive DBIC class method, with debug/dump_to_dir hooks.
971 sub _dbic_stmt {
972     my $self = shift;
973     my $class = shift;
974     my $method = shift;
975
976     my $args = dump(@_);
977     $args = '(' . $args . ')' if @_ < 2;
978     my $stmt = $method . $args . q{;};
979
980     warn qq|$class\->$stmt\n| if $self->debug;
981     $self->_raw_stmt($class, '__PACKAGE__->' . $stmt);
982 }
983
984 # Store a raw source line for a class (for dumping purposes)
985 sub _raw_stmt {
986     my ($self, $class, $stmt) = @_;
987     push(@{$self->{_dump_storage}->{$class}}, $stmt);
988 }
989
990 # Like above, but separately for the externally loaded stuff
991 sub _ext_stmt {
992     my ($self, $class, $stmt) = @_;
993     push(@{$self->{_ext_storage}->{$class}}, $stmt);
994 }
995
996 sub _quote_table_name {
997     my ($self, $table) = @_;
998
999     my $qt = $self->schema->storage->sql_maker->quote_char;
1000
1001     return $table unless $qt;
1002
1003     if (ref $qt) {
1004         return $qt->[0] . $table . $qt->[1];
1005     }
1006
1007     return $qt . $table . $qt;
1008 }
1009
1010 sub _is_case_sensitive { 0 }
1011
1012 =head2 monikers
1013
1014 Returns a hashref of loaded table to moniker mappings.  There will
1015 be two entries for each table, the original name and the "normalized"
1016 name, in the case that the two are different (such as databases
1017 that like uppercase table names, or preserve your original mixed-case
1018 definitions, or what-have-you).
1019
1020 =head2 classes
1021
1022 Returns a hashref of table to class mappings.  In some cases it will
1023 contain multiple entries per table for the original and normalized table
1024 names, as above in L</monikers>.
1025
1026 =head1 SEE ALSO
1027
1028 L<DBIx::Class::Schema::Loader>
1029
1030 =head1 AUTHOR
1031
1032 See L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
1033
1034 =head1 LICENSE
1035
1036 This library is free software; you can redistribute it and/or modify it under
1037 the same terms as Perl itself.
1038
1039 =cut
1040
1041 1;