normalize accessor names for CamelCase columns in v7 mode
[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::Grouped Class::C3::Componentised/;
6 use namespace::autoclean;
7 use Class::C3;
8 use Carp::Clan qw/^DBIx::Class/;
9 use DBIx::Class::Schema::Loader::RelBuilder;
10 use Data::Dump qw/ dump /;
11 use POSIX qw//;
12 use File::Spec qw//;
13 use Cwd qw//;
14 use Digest::MD5 qw//;
15 use Lingua::EN::Inflect::Number qw//;
16 use Lingua::EN::Inflect::Phrase qw//;
17 use File::Temp qw//;
18 use Class::Unload;
19 use Class::Inspector ();
20 use Data::Dumper::Concise;
21 use Scalar::Util 'looks_like_number';
22 use File::Slurp 'slurp';
23 require DBIx::Class;
24
25 our $VERSION = '0.07000';
26
27 __PACKAGE__->mk_group_ro_accessors('simple', qw/
28                                 schema
29                                 schema_class
30
31                                 exclude
32                                 constraint
33                                 additional_classes
34                                 additional_base_classes
35                                 left_base_classes
36                                 components
37                                 resultset_components
38                                 skip_relationships
39                                 skip_load_external
40                                 moniker_map
41                                 custom_column_info
42                                 inflect_singular
43                                 inflect_plural
44                                 debug
45                                 dump_directory
46                                 dump_overwrite
47                                 really_erase_my_files
48                                 resultset_namespace
49                                 default_resultset_class
50                                 schema_base_class
51                                 result_base_class
52                                 overwrite_modifications
53
54                                 relationship_attrs
55
56                                 db_schema
57                                 _tables
58                                 classes
59                                 _upgrading_classes
60                                 monikers
61                                 dynamic
62                                 naming
63                                 datetime_timezone
64                                 datetime_locale
65                                 config_file
66                                 loader_class
67 /);
68
69
70 __PACKAGE__->mk_group_accessors('simple', qw/
71                                 version_to_dump
72                                 schema_version_to_dump
73                                 _upgrading_from
74                                 _upgrading_from_load_classes
75                                 _downgrading_to_load_classes
76                                 _rewriting_result_namespace
77                                 use_namespaces
78                                 result_namespace
79                                 generate_pod
80                                 pod_comment_mode
81                                 pod_comment_spillover_length
82                                 preserve_case
83 /);
84
85 =head1 NAME
86
87 DBIx::Class::Schema::Loader::Base - Base DBIx::Class::Schema::Loader Implementation.
88
89 =head1 SYNOPSIS
90
91 See L<DBIx::Class::Schema::Loader>
92
93 =head1 DESCRIPTION
94
95 This is the base class for the storage-specific C<DBIx::Class::Schema::*>
96 classes, and implements the common functionality between them.
97
98 =head1 CONSTRUCTOR OPTIONS
99
100 These constructor options are the base options for
101 L<DBIx::Class::Schema::Loader/loader_options>.  Available constructor options are:
102
103 =head2 skip_relationships
104
105 Skip setting up relationships.  The default is to attempt the loading
106 of relationships.
107
108 =head2 skip_load_external
109
110 Skip loading of other classes in @INC. The default is to merge all other classes
111 with the same name found in @INC into the schema file we are creating.
112
113 =head2 naming
114
115 Static schemas (ones dumped to disk) will, by default, use the new-style
116 relationship names and singularized Results, unless you're overwriting an
117 existing dump made by an older version of L<DBIx::Class::Schema::Loader>, in
118 which case the backward compatible RelBuilder will be activated, and the
119 appropriate monikerization used.
120
121 Specifying
122
123     naming => 'current'
124
125 will disable the backward-compatible RelBuilder and use
126 the new-style relationship names along with singularized Results, even when
127 overwriting a dump made with an earlier version.
128
129 The option also takes a hashref:
130
131     naming => { relationships => 'v6', monikers => 'v6' }
132
133 The keys are:
134
135 =over 4
136
137 =item relationships
138
139 How to name relationship accessors.
140
141 =item monikers
142
143 How to name Result classes.
144
145 =item column_accessors
146
147 How to name column accessors in Result classes.
148
149 =back
150
151 The values can be:
152
153 =over 4
154
155 =item current
156
157 Latest style, whatever that happens to be.
158
159 =item v4
160
161 Unsingularlized monikers, C<has_many> only relationships with no _id stripping.
162
163 =item v5
164
165 Monikers singularized as whole words, C<might_have> relationships for FKs on
166 C<UNIQUE> constraints, C<_id> stripping for belongs_to relationships.
167
168 Some of the C<_id> stripping edge cases in C<0.05003> have been reverted for
169 the v5 RelBuilder.
170
171 =item v6
172
173 All monikers and relationships are inflected using
174 L<Lingua::EN::Inflect::Phrase>, and there is more aggressive C<_id> stripping
175 from relationship names.
176
177 In general, there is very little difference between v5 and v6 schemas.
178
179 =item v7
180
181 This mode is identical to C<v6> mode, except that monikerization of CamelCase
182 table names is also done correctly.
183
184 CamelCase column names in case-preserving mode will also be handled correctly
185 for relationship name inflection. See L</preserve_case>.
186
187 In this mode, CamelCase L</column_accessors> are normalized based on case
188 transition instead of just being lowercased, so C<FooId> becomes C<foo_id>.
189
190 If you don't have any CamelCase table or column names, you can upgrade without
191 breaking any of your code.
192
193 =back
194
195 Dynamic schemas will always default to the 0.04XXX relationship names and won't
196 singularize Results for backward compatibility, to activate the new RelBuilder
197 and singularization put this in your C<Schema.pm> file:
198
199     __PACKAGE__->naming('current');
200
201 Or if you prefer to use 0.05XXX features but insure that nothing breaks in the
202 next major version upgrade:
203
204     __PACKAGE__->naming('v5');
205
206 =head2 generate_pod
207
208 By default POD will be generated for columns and relationships, using database
209 metadata for the text if available and supported.
210
211 Reading database metadata (e.g. C<COMMENT ON TABLE some_table ...>) is only
212 supported for Postgres right now.
213
214 Set this to C<0> to turn off all POD generation.
215
216 =head2 pod_comment_mode
217
218 Controls where table comments appear in the generated POD. Smaller table
219 comments are appended to the C<NAME> section of the documentation, and larger
220 ones are inserted into C<DESCRIPTION> instead. You can force a C<DESCRIPTION>
221 section to be generated with the comment always, only use C<NAME>, or choose
222 the length threshold at which the comment is forced into the description.
223
224 =over 4
225
226 =item name
227
228 Use C<NAME> section only.
229
230 =item description
231
232 Force C<DESCRIPTION> always.
233
234 =item auto
235
236 Use C<DESCRIPTION> if length > L</pod_comment_spillover_length>, this is the
237 default.
238
239 =back
240
241 =head2 pod_comment_spillover_length
242
243 When pod_comment_mode is set to C<auto>, this is the length of the comment at
244 which it will be forced into a separate description section.
245
246 The default is C<60>
247
248 =head2 relationship_attrs
249
250 Hashref of attributes to pass to each generated relationship, listed
251 by type.  Also supports relationship type 'all', containing options to
252 pass to all generated relationships.  Attributes set for more specific
253 relationship types override those set in 'all'.
254
255 For example:
256
257   relationship_attrs => {
258     all      => { cascade_delete => 0 },
259     has_many => { cascade_delete => 1 },
260   },
261
262 will set the C<cascade_delete> option to 0 for all generated relationships,
263 except for C<has_many>, which will have cascade_delete as 1.
264
265 =head2 debug
266
267 If set to true, each constructive L<DBIx::Class> statement the loader
268 decides to execute will be C<warn>-ed before execution.
269
270 =head2 db_schema
271
272 Set the name of the schema to load (schema in the sense that your database
273 vendor means it).  Does not currently support loading more than one schema
274 name.
275
276 =head2 constraint
277
278 Only load tables matching regex.  Best specified as a qr// regex.
279
280 =head2 exclude
281
282 Exclude tables matching regex.  Best specified as a qr// regex.
283
284 =head2 moniker_map
285
286 Overrides the default table name to moniker translation.  Can be either
287 a hashref of table keys and moniker values, or a coderef for a translator
288 function taking a single scalar table name argument and returning
289 a scalar moniker.  If the hash entry does not exist, or the function
290 returns a false value, the code falls back to default behavior
291 for that table name.
292
293 The default behavior is to split on case transition and non-alphanumeric
294 boundaries, singularize the resulting phrase, then join the titlecased words
295 together. Examples:
296
297     Table Name       | Moniker Name
298     ---------------------------------
299     luser            | Luser
300     luser_group      | LuserGroup
301     luser-opts       | LuserOpt
302     stations_visited | StationVisited
303     routeChange      | RouteChange
304
305 =head2 inflect_plural
306
307 Just like L</moniker_map> above (can be hash/code-ref, falls back to default
308 if hash key does not exist or coderef returns false), but acts as a map
309 for pluralizing relationship names.  The default behavior is to utilize
310 L<Lingua::EN::Inflect::Number/to_PL>.
311
312 =head2 inflect_singular
313
314 As L</inflect_plural> above, but for singularizing relationship names.
315 Default behavior is to utilize L<Lingua::EN::Inflect::Number/to_S>.
316
317 =head2 schema_base_class
318
319 Base class for your schema classes. Defaults to 'DBIx::Class::Schema'.
320
321 =head2 result_base_class
322
323 Base class for your table classes (aka result classes). Defaults to
324 'DBIx::Class::Core'.
325
326 =head2 additional_base_classes
327
328 List of additional base classes all of your table classes will use.
329
330 =head2 left_base_classes
331
332 List of additional base classes all of your table classes will use
333 that need to be leftmost.
334
335 =head2 additional_classes
336
337 List of additional classes which all of your table classes will use.
338
339 =head2 components
340
341 List of additional components to be loaded into all of your table
342 classes.  A good example would be C<ResultSetManager>.
343
344 =head2 resultset_components
345
346 List of additional ResultSet components to be loaded into your table
347 classes.  A good example would be C<AlwaysRS>.  Component
348 C<ResultSetManager> will be automatically added to the above
349 C<components> list if this option is set.
350
351 =head2 use_namespaces
352
353 This is now the default, to go back to L<DBIx::Class::Schema/load_classes> pass
354 a C<0>.
355
356 Generate result class names suitable for
357 L<DBIx::Class::Schema/load_namespaces> and call that instead of
358 L<DBIx::Class::Schema/load_classes>. When using this option you can also
359 specify any of the options for C<load_namespaces> (i.e. C<result_namespace>,
360 C<resultset_namespace>, C<default_resultset_class>), and they will be added
361 to the call (and the generated result class names adjusted appropriately).
362
363 =head2 dump_directory
364
365 This option is designed to be a tool to help you transition from this
366 loader to a manually-defined schema when you decide it's time to do so.
367
368 The value of this option is a perl libdir pathname.  Within
369 that directory this module will create a baseline manual
370 L<DBIx::Class::Schema> module set, based on what it creates at runtime
371 in memory.
372
373 The created schema class will have the same classname as the one on
374 which you are setting this option (and the ResultSource classes will be
375 based on this name as well).
376
377 Normally you wouldn't hard-code this setting in your schema class, as it
378 is meant for one-time manual usage.
379
380 See L<DBIx::Class::Schema::Loader/dump_to_dir> for examples of the
381 recommended way to access this functionality.
382
383 =head2 dump_overwrite
384
385 Deprecated.  See L</really_erase_my_files> below, which does *not* mean
386 the same thing as the old C<dump_overwrite> setting from previous releases.
387
388 =head2 really_erase_my_files
389
390 Default false.  If true, Loader will unconditionally delete any existing
391 files before creating the new ones from scratch when dumping a schema to disk.
392
393 The default behavior is instead to only replace the top portion of the
394 file, up to and including the final stanza which contains
395 C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!>
396 leaving any customizations you placed after that as they were.
397
398 When C<really_erase_my_files> is not set, if the output file already exists,
399 but the aforementioned final stanza is not found, or the checksum
400 contained there does not match the generated contents, Loader will
401 croak and not touch the file.
402
403 You should really be using version control on your schema classes (and all
404 of the rest of your code for that matter).  Don't blame me if a bug in this
405 code wipes something out when it shouldn't have, you've been warned.
406
407 =head2 overwrite_modifications
408
409 Default false.  If false, when updating existing files, Loader will
410 refuse to modify any Loader-generated code that has been modified
411 since its last run (as determined by the checksum Loader put in its
412 comment lines).
413
414 If true, Loader will discard any manual modifications that have been
415 made to Loader-generated code.
416
417 Again, you should be using version control on your schema classes.  Be
418 careful with this option.
419
420 =head2 custom_column_info
421
422 Hook for adding extra attributes to the
423 L<column_info|DBIx::Class::ResultSource/column_info> for a column.
424
425 Must be a coderef that returns a hashref with the extra attributes.
426
427 Receives the table name, column name and column_info.
428
429 For example:
430
431   custom_column_info => sub {
432       my ($table_name, $column_name, $column_info) = @_;
433
434       if ($column_name eq 'dog' && $column_info->{default_value} eq 'snoopy') {
435           return { is_snoopy => 1 };
436       }
437   },
438
439 This attribute can also be used to set C<inflate_datetime> on a non-datetime
440 column so it also receives the L</datetime_timezone> and/or L</datetime_locale>.
441
442 =head2 datetime_timezone
443
444 Sets the timezone attribute for L<DBIx::Class::InflateColumn::DateTime> for all
445 columns with the DATE/DATETIME/TIMESTAMP data_types.
446
447 =head2 datetime_locale
448
449 Sets the locale attribute for L<DBIx::Class::InflateColumn::DateTime> for all
450 columns with the DATE/DATETIME/TIMESTAMP data_types.
451
452 =head1 config_file
453
454 File in Perl format, which should return a HASH reference, from which to read
455 loader options.
456
457 =head1 preserve_case
458
459 Usually column names are lowercased, to make them easier to work with in
460 L<DBIx::Class>. This option lets you turn this behavior off, if the driver
461 supports it.
462
463 Drivers for case sensitive databases like Sybase ASE or MSSQL with a
464 case-sensitive collation will turn this option on unconditionally.
465
466 Currently the drivers for SQLite, mysql, MSSQL and Firebird/InterBase support
467 setting this option.
468
469 =head1 METHODS
470
471 None of these methods are intended for direct invocation by regular
472 users of L<DBIx::Class::Schema::Loader>. Some are proxied via
473 L<DBIx::Class::Schema::Loader>.
474
475 =cut
476
477 my $CURRENT_V = 'v7';
478
479 my @CLASS_ARGS = qw(
480     schema_base_class result_base_class additional_base_classes
481     left_base_classes additional_classes components resultset_components
482 );
483
484 # ensure that a peice of object data is a valid arrayref, creating
485 # an empty one or encapsulating whatever's there.
486 sub _ensure_arrayref {
487     my $self = shift;
488
489     foreach (@_) {
490         $self->{$_} ||= [];
491         $self->{$_} = [ $self->{$_} ]
492             unless ref $self->{$_} eq 'ARRAY';
493     }
494 }
495
496 =head2 new
497
498 Constructor for L<DBIx::Class::Schema::Loader::Base>, used internally
499 by L<DBIx::Class::Schema::Loader>.
500
501 =cut
502
503 sub new {
504     my ( $class, %args ) = @_;
505
506     my $self = { %args };
507
508     bless $self => $class;
509
510     if (my $config_file = $self->config_file) {
511         my $config_opts = do $config_file;
512
513         croak "Error reading config from $config_file: $@" if $@;
514
515         croak "Config file $config_file must be a hashref" unless ref($config_opts) eq 'HASH';
516
517         while (my ($k, $v) = each %$config_opts) {
518             $self->{$k} = $v unless exists $self->{$k};
519         }
520     }
521
522     $self->_ensure_arrayref(qw/additional_classes
523                                additional_base_classes
524                                left_base_classes
525                                components
526                                resultset_components
527                               /);
528
529     $self->_validate_class_args;
530
531     push(@{$self->{components}}, 'ResultSetManager')
532         if @{$self->{resultset_components}};
533
534     $self->{monikers} = {};
535     $self->{classes} = {};
536     $self->{_upgrading_classes} = {};
537
538     $self->{schema_class} ||= ( ref $self->{schema} || $self->{schema} );
539     $self->{schema} ||= $self->{schema_class};
540
541     croak "dump_overwrite is deprecated.  Please read the"
542         . " DBIx::Class::Schema::Loader::Base documentation"
543             if $self->{dump_overwrite};
544
545     $self->{dynamic} = ! $self->{dump_directory};
546     $self->{temp_directory} ||= File::Temp::tempdir( 'dbicXXXX',
547                                                      TMPDIR  => 1,
548                                                      CLEANUP => 1,
549                                                    );
550
551     $self->{dump_directory} ||= $self->{temp_directory};
552
553     $self->version_to_dump($DBIx::Class::Schema::Loader::VERSION);
554     $self->schema_version_to_dump($DBIx::Class::Schema::Loader::VERSION);
555
556     if ((not ref $self->naming) && defined $self->naming) {
557         my $naming_ver = $self->naming;
558         $self->{naming} = {
559             relationships => $naming_ver,
560             monikers => $naming_ver,
561             column_accessors => $naming_ver,
562         };
563     }
564
565     if ($self->naming) {
566         for (values %{ $self->naming }) {
567             $_ = $CURRENT_V if $_ eq 'current';
568         }
569     }
570     $self->{naming} ||= {};
571
572     if ($self->custom_column_info && ref $self->custom_column_info ne 'CODE') {
573         croak 'custom_column_info must be a CODE ref';
574     }
575
576     $self->_check_back_compat;
577
578     $self->use_namespaces(1) unless defined $self->use_namespaces;
579     $self->generate_pod(1)   unless defined $self->generate_pod;
580     $self->pod_comment_mode('auto')         unless defined $self->pod_comment_mode;
581     $self->pod_comment_spillover_length(60) unless defined $self->pod_comment_spillover_length;
582
583     $self;
584 }
585
586 sub _check_back_compat {
587     my ($self) = @_;
588
589 # dynamic schemas will always be in 0.04006 mode, unless overridden
590     if ($self->dynamic) {
591 # just in case, though no one is likely to dump a dynamic schema
592         $self->schema_version_to_dump('0.04006');
593
594         if (not %{ $self->naming }) {
595             warn <<EOF unless $ENV{SCHEMA_LOADER_BACKCOMPAT};
596
597 Dynamic schema detected, will run in 0.04006 mode.
598
599 Set the 'naming' attribute or the SCHEMA_LOADER_BACKCOMPAT environment variable
600 to disable this warning.
601
602 Also consider setting 'use_namespaces => 1' if/when upgrading.
603
604 See perldoc DBIx::Class::Schema::Loader::Manual::UpgradingFromV4 for more
605 details.
606 EOF
607         }
608         else {
609             $self->_upgrading_from('v4');
610         }
611
612         $self->naming->{relationships} ||= 'v4';
613         $self->naming->{monikers}      ||= 'v4';
614
615         if ($self->use_namespaces) {
616             $self->_upgrading_from_load_classes(1);
617         }
618         else {
619             $self->use_namespaces(0);
620         }
621
622         return;
623     }
624
625 # otherwise check if we need backcompat mode for a static schema
626     my $filename = $self->_get_dump_filename($self->schema_class);
627     return unless -e $filename;
628
629     open(my $fh, '<', $filename)
630         or croak "Cannot open '$filename' for reading: $!";
631
632     my $load_classes     = 0;
633     my $result_namespace = '';
634
635     while (<$fh>) {
636         if (/^__PACKAGE__->load_classes;/) {
637             $load_classes = 1;
638         } elsif (/result_namespace => '([^']+)'/) {
639             $result_namespace = $1;
640         } elsif (my ($real_ver) =
641                 /^# Created by DBIx::Class::Schema::Loader v(\d+\.\d+)/) {
642
643             if ($load_classes && (not defined $self->use_namespaces)) {
644                 warn <<"EOF"  unless $ENV{SCHEMA_LOADER_BACKCOMPAT};
645
646 'load_classes;' static schema detected, turning off 'use_namespaces'.
647
648 Set the 'use_namespaces' attribute or the SCHEMA_LOADER_BACKCOMPAT environment
649 variable to disable this warning.
650
651 See perldoc DBIx::Class::Schema::Loader::Manual::UpgradingFromV4 for more
652 details.
653 EOF
654                 $self->use_namespaces(0);
655             }
656             elsif ($load_classes && $self->use_namespaces) {
657                 $self->_upgrading_from_load_classes(1);
658             }
659             elsif ((not $load_classes) && defined $self->use_namespaces
660                                        && (not $self->use_namespaces)) {
661                 $self->_downgrading_to_load_classes(
662                     $result_namespace || 'Result'
663                 );
664             }
665             elsif ((not defined $self->use_namespaces)
666                    || $self->use_namespaces) {
667                 if (not $self->result_namespace) {
668                     $self->result_namespace($result_namespace || 'Result');
669                 }
670                 elsif ($result_namespace ne $self->result_namespace) {
671                     $self->_rewriting_result_namespace(
672                         $result_namespace || 'Result'
673                     );
674                 }
675             }
676
677             # XXX when we go past .0 this will need fixing
678             my ($v) = $real_ver =~ /([1-9])/;
679             $v = "v$v";
680
681             last if $v eq $CURRENT_V || $real_ver =~ /^0\.\d\d999/;
682
683             if (not %{ $self->naming }) {
684                 warn <<"EOF" unless $ENV{SCHEMA_LOADER_BACKCOMPAT};
685
686 Version $real_ver static schema detected, turning on backcompat mode.
687
688 Set the 'naming' attribute or the SCHEMA_LOADER_BACKCOMPAT environment variable
689 to disable this warning.
690
691 See: 'naming' in perldoc DBIx::Class::Schema::Loader::Base .
692
693 See perldoc DBIx::Class::Schema::Loader::Manual::UpgradingFromV4 if upgrading
694 from version 0.04006.
695 EOF
696             }
697             else {
698                 $self->_upgrading_from($v);
699                 last;
700             }
701
702             $self->naming->{relationships}    ||= $v;
703             $self->naming->{monikers}         ||= $v;
704             $self->naming->{column_accessors} ||= $v;
705
706             $self->schema_version_to_dump($real_ver);
707
708             last;
709         }
710     }
711     close $fh;
712 }
713
714 sub _validate_class_args {
715     my $self = shift;
716     my $args = shift;
717     
718     foreach my $k (@CLASS_ARGS) {
719         next unless $self->$k;
720
721         my @classes = ref $self->$k eq 'ARRAY' ? @{ $self->$k } : $self->$k;
722         foreach my $c (@classes) {
723             # components default to being under the DBIx::Class namespace unless they
724             # are preceeded with a '+'
725             if ( $k =~ m/components$/ && $c !~ s/^\+// ) {
726                 $c = 'DBIx::Class::' . $c;
727             }
728
729             # 1 == installed, 0 == not installed, undef == invalid classname
730             my $installed = Class::Inspector->installed($c);
731             if ( defined($installed) ) {
732                 if ( $installed == 0 ) {
733                     croak qq/$c, as specified in the loader option "$k", is not installed/;
734                 }
735             } else {
736                 croak qq/$c, as specified in the loader option "$k", is an invalid class name/;
737             }
738         }
739     }
740 }
741
742 sub _find_file_in_inc {
743     my ($self, $file) = @_;
744
745     foreach my $prefix (@INC) {
746         my $fullpath = File::Spec->catfile($prefix, $file);
747         return $fullpath if -f $fullpath
748             # abs_path throws on Windows for nonexistant files
749             and eval { Cwd::abs_path($fullpath) } ne
750                (eval { Cwd::abs_path(File::Spec->catfile($self->dump_directory, $file)) } || '');
751     }
752
753     return;
754 }
755
756 sub _class_path {
757     my ($self, $class) = @_;
758
759     my $class_path = $class;
760     $class_path =~ s{::}{/}g;
761     $class_path .= '.pm';
762
763     return $class_path;
764 }
765
766 sub _find_class_in_inc {
767     my ($self, $class) = @_;
768
769     return $self->_find_file_in_inc($self->_class_path($class));
770 }
771
772 sub _rewriting {
773     my $self = shift;
774
775     return $self->_upgrading_from
776         || $self->_upgrading_from_load_classes
777         || $self->_downgrading_to_load_classes
778         || $self->_rewriting_result_namespace
779     ;
780 }
781
782 sub _rewrite_old_classnames {
783     my ($self, $code) = @_;
784
785     return $code unless $self->_rewriting;
786
787     my %old_classes = reverse %{ $self->_upgrading_classes };
788
789     my $re = join '|', keys %old_classes;
790     $re = qr/\b($re)\b/;
791
792     $code =~ s/$re/$old_classes{$1} || $1/eg;
793
794     return $code;
795 }
796
797 sub _load_external {
798     my ($self, $class) = @_;
799
800     return if $self->{skip_load_external};
801
802     # so that we don't load our own classes, under any circumstances
803     local *INC = [ grep $_ ne $self->dump_directory, @INC ];
804
805     my $real_inc_path = $self->_find_class_in_inc($class);
806
807     my $old_class = $self->_upgrading_classes->{$class}
808         if $self->_rewriting;
809
810     my $old_real_inc_path = $self->_find_class_in_inc($old_class)
811         if $old_class && $old_class ne $class;
812
813     return unless $real_inc_path || $old_real_inc_path;
814
815     if ($real_inc_path) {
816         # If we make it to here, we loaded an external definition
817         warn qq/# Loaded external class definition for '$class'\n/
818             if $self->debug;
819
820         open(my $fh, '<', $real_inc_path)
821             or croak "Failed to open '$real_inc_path' for reading: $!";
822         my $code = do { local $/; <$fh> };
823         close($fh)
824             or croak "Failed to close $real_inc_path: $!";
825         $code = $self->_rewrite_old_classnames($code);
826
827         if ($self->dynamic) { # load the class too
828             # kill redefined warnings
829             my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
830             local $SIG{__WARN__} = sub {
831                 $warn_handler->(@_)
832                     unless $_[0] =~ /^Subroutine \S+ redefined/;
833             };
834             eval $code;
835             die $@ if $@;
836         }
837
838         $self->_ext_stmt($class,
839           qq|# These lines were loaded from '$real_inc_path' found in \@INC.\n|
840          .qq|# They are now part of the custom portion of this file\n|
841          .qq|# for you to hand-edit.  If you do not either delete\n|
842          .qq|# this section or remove that file from \@INC, this section\n|
843          .qq|# will be repeated redundantly when you re-create this\n|
844          .qq|# file again via Loader!  See skip_load_external to disable\n|
845          .qq|# this feature.\n|
846         );
847         chomp $code;
848         $self->_ext_stmt($class, $code);
849         $self->_ext_stmt($class,
850             qq|# End of lines loaded from '$real_inc_path' |
851         );
852     }
853
854     if ($old_real_inc_path) {
855         open(my $fh, '<', $old_real_inc_path)
856             or croak "Failed to open '$old_real_inc_path' for reading: $!";
857         $self->_ext_stmt($class, <<"EOF");
858
859 # These lines were loaded from '$old_real_inc_path',
860 # based on the Result class name that would have been created by an older
861 # version of the Loader. For a static schema, this happens only once during
862 # upgrade. See skip_load_external to disable this feature.
863 EOF
864
865         my $code = slurp $old_real_inc_path;
866         $code = $self->_rewrite_old_classnames($code);
867
868         if ($self->dynamic) {
869             warn <<"EOF";
870
871 Detected external content in '$old_real_inc_path', a class name that would have
872 been used by an older version of the Loader.
873
874 * PLEASE RENAME THIS CLASS: from '$old_class' to '$class', as that is the
875 new name of the Result.
876 EOF
877             # kill redefined warnings
878             my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
879             local $SIG{__WARN__} = sub {
880                 $warn_handler->(@_)
881                     unless $_[0] =~ /^Subroutine \S+ redefined/;
882             };
883             eval $code;
884             die $@ if $@;
885         }
886
887         chomp $code;
888         $self->_ext_stmt($class, $code);
889         $self->_ext_stmt($class,
890             qq|# End of lines loaded from '$old_real_inc_path' |
891         );
892     }
893 }
894
895 =head2 load
896
897 Does the actual schema-construction work.
898
899 =cut
900
901 sub load {
902     my $self = shift;
903
904     $self->_load_tables(
905         $self->_tables_list({ constraint => $self->constraint, exclude => $self->exclude })
906     );
907 }
908
909 =head2 rescan
910
911 Arguments: schema
912
913 Rescan the database for newly added tables.  Does
914 not process drops or changes.  Returns a list of
915 the newly added table monikers.
916
917 The schema argument should be the schema class
918 or object to be affected.  It should probably
919 be derived from the original schema_class used
920 during L</load>.
921
922 =cut
923
924 sub rescan {
925     my ($self, $schema) = @_;
926
927     $self->{schema} = $schema;
928     $self->_relbuilder->{schema} = $schema;
929
930     my @created;
931     my @current = $self->_tables_list({ constraint => $self->constraint, exclude => $self->exclude });
932
933     foreach my $table (@current) {
934         if(!exists $self->{_tables}->{$table}) {
935             push(@created, $table);
936         }
937     }
938
939     my %current;
940     @current{@current} = ();
941     foreach my $table (keys %{ $self->{_tables} }) {
942         if (not exists $current{$table}) {
943             $self->_unregister_source_for_table($table);
944         }
945     }
946
947     my $loaded = $self->_load_tables(@created);
948
949     return map { $self->monikers->{$_} } @$loaded;
950 }
951
952 sub _relbuilder {
953     no warnings 'uninitialized';
954     my ($self) = @_;
955
956     return if $self->{skip_relationships};
957
958     if ($self->naming->{relationships} eq 'v4') {
959         require DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_040;
960         return $self->{relbuilder} ||=
961             DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_040->new(
962                 $self->schema,
963                 $self->inflect_plural,
964                 $self->inflect_singular,
965                 $self->relationship_attrs,
966             );
967     }
968     elsif ($self->naming->{relationships} eq 'v5') {
969         require DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_05;
970         return $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_05->new (
971              $self->schema,
972              $self->inflect_plural,
973              $self->inflect_singular,
974              $self->relationship_attrs,
975         );
976     }
977     elsif ($self->naming->{relationships} eq 'v6') {
978         require DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_06;
979         return $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_06->new (
980              $self->schema,
981              $self->inflect_plural,
982              $self->inflect_singular,
983              $self->relationship_attrs,
984         );
985     }
986
987     return $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder->new (
988              $self->schema,
989              $self->inflect_plural,
990              $self->inflect_singular,
991              $self->relationship_attrs,
992     );
993 }
994
995 sub _load_tables {
996     my ($self, @tables) = @_;
997
998     # Save the new tables to the tables list
999     foreach (@tables) {
1000         $self->{_tables}->{$_} = 1;
1001     }
1002
1003     $self->_make_src_class($_) for @tables;
1004
1005     # sanity-check for moniker clashes
1006     my $inverse_moniker_idx;
1007     for (keys %{$self->monikers}) {
1008       push @{$inverse_moniker_idx->{$self->monikers->{$_}}}, $_;
1009     }
1010
1011     my @clashes;
1012     for (keys %$inverse_moniker_idx) {
1013       my $tables = $inverse_moniker_idx->{$_};
1014       if (@$tables > 1) {
1015         push @clashes, sprintf ("tables %s reduced to the same source moniker '%s'",
1016           join (', ', map { "'$_'" } @$tables),
1017           $_,
1018         );
1019       }
1020     }
1021
1022     if (@clashes) {
1023       die   'Unable to load schema - chosen moniker/class naming style results in moniker clashes. '
1024           . 'Either change the naming style, or supply an explicit moniker_map: '
1025           . join ('; ', @clashes)
1026           . "\n"
1027       ;
1028     }
1029
1030
1031     $self->_setup_src_meta($_) for @tables;
1032
1033     if(!$self->skip_relationships) {
1034         # The relationship loader needs a working schema
1035         $self->{quiet} = 1;
1036         local $self->{dump_directory} = $self->{temp_directory};
1037         $self->_reload_classes(\@tables);
1038         $self->_load_relationships($_) for @tables;
1039         $self->{quiet} = 0;
1040
1041         # Remove that temp dir from INC so it doesn't get reloaded
1042         @INC = grep $_ ne $self->dump_directory, @INC;
1043     }
1044
1045     $self->_load_external($_)
1046         for map { $self->classes->{$_} } @tables;
1047
1048     # Reload without unloading first to preserve any symbols from external
1049     # packages.
1050     $self->_reload_classes(\@tables, 0);
1051
1052     # Drop temporary cache
1053     delete $self->{_cache};
1054
1055     return \@tables;
1056 }
1057
1058 sub _reload_classes {
1059     my ($self, $tables, $unload) = @_;
1060
1061     my @tables = @$tables;
1062     $unload = 1 unless defined $unload;
1063
1064     # so that we don't repeat custom sections
1065     @INC = grep $_ ne $self->dump_directory, @INC;
1066
1067     $self->_dump_to_dir(map { $self->classes->{$_} } @tables);
1068
1069     unshift @INC, $self->dump_directory;
1070     
1071     my @to_register;
1072     my %have_source = map { $_ => $self->schema->source($_) }
1073         $self->schema->sources;
1074
1075     for my $table (@tables) {
1076         my $moniker = $self->monikers->{$table};
1077         my $class = $self->classes->{$table};
1078         
1079         {
1080             no warnings 'redefine';
1081             local *Class::C3::reinitialize = sub {};
1082             use warnings;
1083
1084             Class::Unload->unload($class) if $unload;
1085             my ($source, $resultset_class);
1086             if (
1087                 ($source = $have_source{$moniker})
1088                 && ($resultset_class = $source->resultset_class)
1089                 && ($resultset_class ne 'DBIx::Class::ResultSet')
1090             ) {
1091                 my $has_file = Class::Inspector->loaded_filename($resultset_class);
1092                 Class::Unload->unload($resultset_class) if $unload;
1093                 $self->_reload_class($resultset_class) if $has_file;
1094             }
1095             $self->_reload_class($class);
1096         }
1097         push @to_register, [$moniker, $class];
1098     }
1099
1100     Class::C3->reinitialize;
1101     for (@to_register) {
1102         $self->schema->register_class(@$_);
1103     }
1104 }
1105
1106 # We use this instead of ensure_class_loaded when there are package symbols we
1107 # want to preserve.
1108 sub _reload_class {
1109     my ($self, $class) = @_;
1110
1111     my $class_path = $self->_class_path($class);
1112     delete $INC{ $class_path };
1113
1114 # kill redefined warnings
1115     my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
1116     local $SIG{__WARN__} = sub {
1117         $warn_handler->(@_)
1118             unless $_[0] =~ /^Subroutine \S+ redefined/;
1119     };
1120     eval "require $class;";
1121 }
1122
1123 sub _get_dump_filename {
1124     my ($self, $class) = (@_);
1125
1126     $class =~ s{::}{/}g;
1127     return $self->dump_directory . q{/} . $class . q{.pm};
1128 }
1129
1130 sub _ensure_dump_subdirs {
1131     my ($self, $class) = (@_);
1132
1133     my @name_parts = split(/::/, $class);
1134     pop @name_parts; # we don't care about the very last element,
1135                      # which is a filename
1136
1137     my $dir = $self->dump_directory;
1138     while (1) {
1139         if(!-d $dir) {
1140             mkdir($dir) or croak "mkdir('$dir') failed: $!";
1141         }
1142         last if !@name_parts;
1143         $dir = File::Spec->catdir($dir, shift @name_parts);
1144     }
1145 }
1146
1147 sub _dump_to_dir {
1148     my ($self, @classes) = @_;
1149
1150     my $schema_class = $self->schema_class;
1151     my $schema_base_class = $self->schema_base_class || 'DBIx::Class::Schema';
1152
1153     my $target_dir = $self->dump_directory;
1154     warn "Dumping manual schema for $schema_class to directory $target_dir ...\n"
1155         unless $self->{dynamic} or $self->{quiet};
1156
1157     my $schema_text =
1158           qq|package $schema_class;\n\n|
1159         . qq|# Created by DBIx::Class::Schema::Loader\n|
1160         . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
1161         . qq|use strict;\nuse warnings;\n\n|
1162         . qq|use base '$schema_base_class';\n\n|;
1163
1164     if ($self->use_namespaces) {
1165         $schema_text .= qq|__PACKAGE__->load_namespaces|;
1166         my $namespace_options;
1167
1168         my @attr = qw/resultset_namespace default_resultset_class/;
1169
1170         unshift @attr, 'result_namespace' unless (not $self->result_namespace) || $self->result_namespace eq 'Result';
1171
1172         for my $attr (@attr) {
1173             if ($self->$attr) {
1174                 $namespace_options .= qq|    $attr => '| . $self->$attr . qq|',\n|
1175             }
1176         }
1177         $schema_text .= qq|(\n$namespace_options)| if $namespace_options;
1178         $schema_text .= qq|;\n|;
1179     }
1180     else {
1181         $schema_text .= qq|__PACKAGE__->load_classes;\n|;
1182     }
1183
1184     {
1185         local $self->{version_to_dump} = $self->schema_version_to_dump;
1186         $self->_write_classfile($schema_class, $schema_text, 1);
1187     }
1188
1189     my $result_base_class = $self->result_base_class || 'DBIx::Class::Core';
1190
1191     foreach my $src_class (@classes) {
1192         my $src_text = 
1193               qq|package $src_class;\n\n|
1194             . qq|# Created by DBIx::Class::Schema::Loader\n|
1195             . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
1196             . qq|use strict;\nuse warnings;\n\n|
1197             . qq|use base '$result_base_class';\n\n|;
1198
1199         $self->_write_classfile($src_class, $src_text);
1200     }
1201
1202     # remove Result dir if downgrading from use_namespaces, and there are no
1203     # files left.
1204     if (my $result_ns = $self->_downgrading_to_load_classes
1205                         || $self->_rewriting_result_namespace) {
1206         my $result_namespace = $self->_result_namespace(
1207             $schema_class,
1208             $result_ns,
1209         );
1210
1211         (my $result_dir = $result_namespace) =~ s{::}{/}g;
1212         $result_dir = $self->dump_directory . '/' . $result_dir;
1213
1214         unless (my @files = glob "$result_dir/*") {
1215             rmdir $result_dir;
1216         }
1217     }
1218
1219     warn "Schema dump completed.\n" unless $self->{dynamic} or $self->{quiet};
1220
1221 }
1222
1223 sub _sig_comment {
1224     my ($self, $version, $ts) = @_;
1225     return qq|\n\n# Created by DBIx::Class::Schema::Loader|
1226          . qq| v| . $version
1227          . q| @ | . $ts 
1228          . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
1229 }
1230
1231 sub _write_classfile {
1232     my ($self, $class, $text, $is_schema) = @_;
1233
1234     my $filename = $self->_get_dump_filename($class);
1235     $self->_ensure_dump_subdirs($class);
1236
1237     if (-f $filename && $self->really_erase_my_files) {
1238         warn "Deleting existing file '$filename' due to "
1239             . "'really_erase_my_files' setting\n" unless $self->{quiet};
1240         unlink($filename);
1241     }    
1242
1243     my ($custom_content, $old_md5, $old_ver, $old_ts) = $self->_get_custom_content($class, $filename);
1244
1245     if (my $old_class = $self->_upgrading_classes->{$class}) {
1246         my $old_filename = $self->_get_dump_filename($old_class);
1247
1248         my ($old_custom_content) = $self->_get_custom_content(
1249             $old_class, $old_filename, 0 # do not add default comment
1250         );
1251
1252         $old_custom_content =~ s/\n\n# You can replace.*\n1;\n//;
1253
1254         if ($old_custom_content) {
1255             $custom_content =
1256                 "\n" . $old_custom_content . "\n" . $custom_content;
1257         }
1258
1259         unlink $old_filename;
1260     }
1261
1262     $custom_content = $self->_rewrite_old_classnames($custom_content);
1263
1264     $text .= qq|$_\n|
1265         for @{$self->{_dump_storage}->{$class} || []};
1266
1267     # Check and see if the dump is infact differnt
1268
1269     my $compare_to;
1270     if ($old_md5) {
1271       $compare_to = $text . $self->_sig_comment($old_ver, $old_ts);
1272       
1273
1274       if (Digest::MD5::md5_base64($compare_to) eq $old_md5) {
1275         return unless $self->_upgrading_from && $is_schema;
1276       }
1277     }
1278
1279     $text .= $self->_sig_comment(
1280       $self->version_to_dump,
1281       POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
1282     );
1283
1284     open(my $fh, '>', $filename)
1285         or croak "Cannot open '$filename' for writing: $!";
1286
1287     # Write the top half and its MD5 sum
1288     print $fh $text . Digest::MD5::md5_base64($text) . "\n";
1289
1290     # Write out anything loaded via external partial class file in @INC
1291     print $fh qq|$_\n|
1292         for @{$self->{_ext_storage}->{$class} || []};
1293
1294     # Write out any custom content the user has added
1295     print $fh $custom_content;
1296
1297     close($fh)
1298         or croak "Error closing '$filename': $!";
1299 }
1300
1301 sub _default_custom_content {
1302     return qq|\n\n# You can replace this text with custom|
1303          . qq| content, and it will be preserved on regeneration|
1304          . qq|\n1;\n|;
1305 }
1306
1307 sub _get_custom_content {
1308     my ($self, $class, $filename, $add_default) = @_;
1309
1310     $add_default = 1 unless defined $add_default;
1311
1312     return ($self->_default_custom_content) if ! -f $filename;
1313
1314     open(my $fh, '<', $filename)
1315         or croak "Cannot open '$filename' for reading: $!";
1316
1317     my $mark_re = 
1318         qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
1319
1320     my $buffer = '';
1321     my ($md5, $ts, $ver);
1322     while(<$fh>) {
1323         if(!$md5 && /$mark_re/) {
1324             $md5 = $2;
1325             my $line = $1;
1326
1327             # Pull out the previous version and timestamp
1328             ($ver, $ts) = $buffer =~ m/# Created by DBIx::Class::Schema::Loader v(.*?) @ (.*?)$/s;
1329
1330             $buffer .= $line;
1331             croak "Checksum mismatch in '$filename', the auto-generated part of the file has been modified outside of this loader.  Aborting.\nIf you want to overwrite these modifications, set the 'overwrite_modifications' loader option.\n"
1332                 if !$self->overwrite_modifications && Digest::MD5::md5_base64($buffer) ne $md5;
1333
1334             $buffer = '';
1335         }
1336         else {
1337             $buffer .= $_;
1338         }
1339     }
1340
1341     croak "Cannot not overwrite '$filename' without 'really_erase_my_files',"
1342         . " it does not appear to have been generated by Loader"
1343             if !$md5;
1344
1345     # Default custom content:
1346     $buffer ||= $self->_default_custom_content if $add_default;
1347
1348     return ($buffer, $md5, $ver, $ts);
1349 }
1350
1351 sub _use {
1352     my $self = shift;
1353     my $target = shift;
1354
1355     foreach (@_) {
1356         warn "$target: use $_;" if $self->debug;
1357         $self->_raw_stmt($target, "use $_;");
1358     }
1359 }
1360
1361 sub _inject {
1362     my $self = shift;
1363     my $target = shift;
1364     my $schema_class = $self->schema_class;
1365
1366     my $blist = join(q{ }, @_);
1367     warn "$target: use base qw/ $blist /;" if $self->debug && @_;
1368     $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
1369 }
1370
1371 sub _result_namespace {
1372     my ($self, $schema_class, $ns) = @_;
1373     my @result_namespace;
1374
1375     if ($ns =~ /^\+(.*)/) {
1376         # Fully qualified namespace
1377         @result_namespace = ($1)
1378     }
1379     else {
1380         # Relative namespace
1381         @result_namespace = ($schema_class, $ns);
1382     }
1383
1384     return wantarray ? @result_namespace : join '::', @result_namespace;
1385 }
1386
1387 # Create class with applicable bases, setup monikers, etc
1388 sub _make_src_class {
1389     my ($self, $table) = @_;
1390
1391     my $schema       = $self->schema;
1392     my $schema_class = $self->schema_class;
1393
1394     my $table_moniker = $self->_table2moniker($table);
1395     my @result_namespace = ($schema_class);
1396     if ($self->use_namespaces) {
1397         my $result_namespace = $self->result_namespace || 'Result';
1398         @result_namespace = $self->_result_namespace(
1399             $schema_class,
1400             $result_namespace,
1401         );
1402     }
1403     my $table_class = join(q{::}, @result_namespace, $table_moniker);
1404
1405     if ((my $upgrading_v = $self->_upgrading_from)
1406             || $self->_rewriting) {
1407         local $self->naming->{monikers} = $upgrading_v
1408             if $upgrading_v;
1409
1410         my @result_namespace = @result_namespace;
1411         if ($self->_upgrading_from_load_classes) {
1412             @result_namespace = ($schema_class);
1413         }
1414         elsif (my $ns = $self->_downgrading_to_load_classes) {
1415             @result_namespace = $self->_result_namespace(
1416                 $schema_class,
1417                 $ns,
1418             );
1419         }
1420         elsif ($ns = $self->_rewriting_result_namespace) {
1421             @result_namespace = $self->_result_namespace(
1422                 $schema_class,
1423                 $ns,
1424             );
1425         }
1426
1427         my $old_class = join(q{::}, @result_namespace,
1428             $self->_table2moniker($table));
1429
1430         $self->_upgrading_classes->{$table_class} = $old_class
1431             unless $table_class eq $old_class;
1432     }
1433
1434 # this was a bad idea, should be ok now without it
1435 #    my $table_normalized = lc $table;
1436 #    $self->classes->{$table_normalized} = $table_class;
1437 #    $self->monikers->{$table_normalized} = $table_moniker;
1438
1439     $self->classes->{$table} = $table_class;
1440     $self->monikers->{$table} = $table_moniker;
1441
1442     $self->_use   ($table_class, @{$self->additional_classes});
1443     $self->_inject($table_class, @{$self->left_base_classes});
1444
1445     if (my @components = @{ $self->components }) {
1446         $self->_dbic_stmt($table_class, 'load_components', @components);
1447     }
1448
1449     $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
1450         if @{$self->resultset_components};
1451     $self->_inject($table_class, @{$self->additional_base_classes});
1452 }
1453
1454 sub _resolve_col_accessor_collisions {
1455     my ($self, $col_info) = @_;
1456
1457     my $base       = $self->result_base_class || 'DBIx::Class::Core';
1458     my @components = map "DBIx::Class::$_", @{ $self->components || [] };
1459
1460     my @methods;
1461
1462     for my $class ($base, @components) {
1463         eval "require ${class};";
1464         die $@ if $@;
1465
1466         push @methods, @{ Class::Inspector->methods($class) || [] };
1467     }
1468
1469     my %methods;
1470     @methods{@methods} = ();
1471
1472     while (my ($col, $info) = each %$col_info) {
1473         my $accessor = $info->{accessor} || $col;
1474
1475         next if $accessor eq 'id'; # special case (very common column)
1476
1477         if (exists $methods{$accessor}) {
1478             $info->{accessor} = undef;
1479         }
1480     }
1481 }
1482
1483 sub _make_column_accessor_name {
1484     my ($self, $column_name) = @_;
1485
1486     return join '_', map lc, split /(?<=[[:lower:]])[\W_]*(?=[[:upper:]])|[\W_]+/, $column_name;
1487 }
1488
1489 # Set up metadata (cols, pks, etc)
1490 sub _setup_src_meta {
1491     my ($self, $table) = @_;
1492
1493     my $schema       = $self->schema;
1494     my $schema_class = $self->schema_class;
1495
1496     my $table_class = $self->classes->{$table};
1497     my $table_moniker = $self->monikers->{$table};
1498
1499     my $table_name = $table;
1500     my $name_sep   = $self->schema->storage->sql_maker->name_sep;
1501
1502     if ($name_sep && $table_name =~ /\Q$name_sep\E/) {
1503         $table_name = \ $self->_quote_table_name($table_name);
1504     }
1505
1506     $self->_dbic_stmt($table_class,'table',$table_name);
1507
1508     my $cols = $self->_table_columns($table);
1509     my $col_info = $self->__columns_info_for($table);
1510     if ($self->preserve_case) {
1511         for my $col (keys %$col_info) {
1512             if ($col ne lc($col)) {
1513                 if ((not exists $self->naming->{column_accessors}) || (($self->naming->{column_accessors} =~ /(\d+)/)[0] >= 7)) {
1514                     $col_info->{$col}{accessor} = $self->_make_column_accessor_name($col);
1515                 }
1516                 else {
1517                     $col_info->{$col}{accessor} = lc $col;
1518                 }
1519             }
1520         }
1521     }
1522     else {
1523         # XXX this needs to go away
1524         $col_info = { map { lc($_), $col_info->{$_} } keys %$col_info };
1525     }
1526
1527     $self->_resolve_col_accessor_collisions($col_info);
1528
1529     my $fks = $self->_table_fk_info($table);
1530
1531     for my $fkdef (@$fks) {
1532         for my $col (@{ $fkdef->{local_columns} }) {
1533             $col_info->{$col}{is_foreign_key} = 1;
1534         }
1535     }
1536     $self->_dbic_stmt(
1537         $table_class,
1538         'add_columns',
1539         map { $_, ($col_info->{$_}||{}) } @$cols
1540     );
1541
1542     my %uniq_tag; # used to eliminate duplicate uniqs
1543
1544     my $pks = $self->_table_pk_info($table) || [];
1545     @$pks ? $self->_dbic_stmt($table_class,'set_primary_key',@$pks)
1546           : carp("$table has no primary key");
1547     $uniq_tag{ join("\0", @$pks) }++ if @$pks; # pk is a uniq
1548
1549     my $uniqs = $self->_table_uniq_info($table) || [];
1550     for (@$uniqs) {
1551         my ($name, $cols) = @$_;
1552         next if $uniq_tag{ join("\0", @$cols) }++; # skip duplicates
1553         $self->_dbic_stmt($table_class,'add_unique_constraint', $name, $cols);
1554     }
1555
1556 }
1557
1558 sub __columns_info_for {
1559     my ($self, $table) = @_;
1560
1561     my $result = $self->_columns_info_for($table);
1562
1563     while (my ($col, $info) = each %$result) {
1564         $info = { %$info, %{ $self->_custom_column_info  ($table, $col, $info) } };
1565         $info = { %$info, %{ $self->_datetime_column_info($table, $col, $info) } };
1566
1567         $result->{$col} = $info;
1568     }
1569
1570     return $result;
1571 }
1572
1573 =head2 tables
1574
1575 Returns a sorted list of loaded tables, using the original database table
1576 names.
1577
1578 =cut
1579
1580 sub tables {
1581     my $self = shift;
1582
1583     return keys %{$self->_tables};
1584 }
1585
1586 # Make a moniker from a table
1587 sub _default_table2moniker {
1588     no warnings 'uninitialized';
1589     my ($self, $table) = @_;
1590
1591     if ($self->naming->{monikers} eq 'v4') {
1592         return join '', map ucfirst, split /[\W_]+/, lc $table;
1593     }
1594     elsif ($self->naming->{monikers} eq 'v5') {
1595         return join '', map ucfirst, split /[\W_]+/,
1596             Lingua::EN::Inflect::Number::to_S(lc $table);
1597     }
1598     elsif ($self->naming->{monikers} eq 'v6') {
1599         (my $as_phrase = lc $table) =~ s/_+/ /g;
1600         my $inflected = Lingua::EN::Inflect::Phrase::to_S($as_phrase);
1601
1602         return join '', map ucfirst, split /\W+/, $inflected;
1603     }
1604
1605     my @words = map lc, split /(?<=[[:lower:]])[\W_]*(?=[[:upper:]])|[\W_]+/, $table;
1606     my $as_phrase = join ' ', @words;
1607
1608     my $inflected = Lingua::EN::Inflect::Phrase::to_S($as_phrase);
1609
1610     return join '', map ucfirst, split /\W+/, $inflected;
1611 }
1612
1613 sub _table2moniker {
1614     my ( $self, $table ) = @_;
1615
1616     my $moniker;
1617
1618     if( ref $self->moniker_map eq 'HASH' ) {
1619         $moniker = $self->moniker_map->{$table};
1620     }
1621     elsif( ref $self->moniker_map eq 'CODE' ) {
1622         $moniker = $self->moniker_map->($table);
1623     }
1624
1625     $moniker ||= $self->_default_table2moniker($table);
1626
1627     return $moniker;
1628 }
1629
1630 sub _load_relationships {
1631     my ($self, $table) = @_;
1632
1633     my $tbl_fk_info = $self->_table_fk_info($table);
1634     foreach my $fkdef (@$tbl_fk_info) {
1635         $fkdef->{remote_source} =
1636             $self->monikers->{delete $fkdef->{remote_table}};
1637     }
1638     my $tbl_uniq_info = $self->_table_uniq_info($table);
1639
1640     my $local_moniker = $self->monikers->{$table};
1641     my $rel_stmts = $self->_relbuilder->generate_code($local_moniker, $tbl_fk_info, $tbl_uniq_info);
1642
1643     foreach my $src_class (sort keys %$rel_stmts) {
1644         my $src_stmts = $rel_stmts->{$src_class};
1645         foreach my $stmt (@$src_stmts) {
1646             $self->_dbic_stmt($src_class,$stmt->{method},@{$stmt->{args}});
1647         }
1648     }
1649 }
1650
1651 # Overload these in driver class:
1652
1653 # Returns an arrayref of column names
1654 sub _table_columns { croak "ABSTRACT METHOD" }
1655
1656 # Returns arrayref of pk col names
1657 sub _table_pk_info { croak "ABSTRACT METHOD" }
1658
1659 # Returns an arrayref of uniqs [ [ foo => [ col1, col2 ] ], [ bar => [ ... ] ] ]
1660 sub _table_uniq_info { croak "ABSTRACT METHOD" }
1661
1662 # Returns an arrayref of foreign key constraints, each
1663 #   being a hashref with 3 keys:
1664 #   local_columns (arrayref), remote_columns (arrayref), remote_table
1665 sub _table_fk_info { croak "ABSTRACT METHOD" }
1666
1667 # Returns an array of lower case table names
1668 sub _tables_list { croak "ABSTRACT METHOD" }
1669
1670 # Execute a constructive DBIC class method, with debug/dump_to_dir hooks.
1671 sub _dbic_stmt {
1672     my $self   = shift;
1673     my $class  = shift;
1674     my $method = shift;
1675
1676     # generate the pod for this statement, storing it with $self->_pod
1677     $self->_make_pod( $class, $method, @_ ) if $self->generate_pod;
1678
1679     my $args = dump(@_);
1680     $args = '(' . $args . ')' if @_ < 2;
1681     my $stmt = $method . $args . q{;};
1682
1683     warn qq|$class\->$stmt\n| if $self->debug;
1684     $self->_raw_stmt($class, '__PACKAGE__->' . $stmt);
1685     return;
1686 }
1687
1688 # generates the accompanying pod for a DBIC class method statement,
1689 # storing it with $self->_pod
1690 sub _make_pod {
1691     my $self   = shift;
1692     my $class  = shift;
1693     my $method = shift;
1694
1695     if ( $method eq 'table' ) {
1696         my ($table) = @_;
1697         my $pcm = $self->pod_comment_mode;
1698         my ($comment, $comment_overflows, $comment_in_name, $comment_in_desc);
1699         if ( $self->can('_table_comment') ) {
1700             $comment = $self->_table_comment($table);
1701             $comment_overflows = ($comment and length $comment > $self->pod_comment_spillover_length);
1702             $comment_in_name   = ($pcm eq 'name' or ($pcm eq 'auto' and !$comment_overflows));
1703             $comment_in_desc   = ($pcm eq 'description' or ($pcm eq 'auto' and $comment_overflows));
1704         }
1705         $self->_pod( $class, "=head1 NAME" );
1706         my $table_descr = $class;
1707         $table_descr .= " - " . $comment if $comment and $comment_in_name;
1708         $self->{_class2table}{ $class } = $table;
1709         $self->_pod( $class, $table_descr );
1710         if ($comment and $comment_in_desc) {
1711             $self->_pod( $class, "=head1 DESCRIPTION" );
1712             $self->_pod( $class, $comment );
1713         }
1714         $self->_pod_cut( $class );
1715     } elsif ( $method eq 'add_columns' ) {
1716         $self->_pod( $class, "=head1 ACCESSORS" );
1717         my $col_counter = 0;
1718         my @cols = @_;
1719         while( my ($name,$attrs) = splice @cols,0,2 ) {
1720             $col_counter++;
1721             $self->_pod( $class, '=head2 ' . $name  );
1722             $self->_pod( $class,
1723                          join "\n", map {
1724                              my $s = $attrs->{$_};
1725                              $s = !defined $s         ? 'undef'          :
1726                                   length($s) == 0     ? '(empty string)' :
1727                                   ref($s) eq 'SCALAR' ? $$s :
1728                                   ref($s)             ? do {
1729                                                         my $dd = Dumper;
1730                                                         $dd->Indent(0);
1731                                                         $dd->Values([$s]);
1732                                                         $dd->Dump;
1733                                                       } :
1734                                   looks_like_number($s) ? $s :
1735                                                         qq{'$s'}
1736                                   ;
1737
1738                              "  $_: $s"
1739                          } sort keys %$attrs,
1740                        );
1741
1742             if( $self->can('_column_comment')
1743                 and my $comment = $self->_column_comment( $self->{_class2table}{$class}, $col_counter)
1744               ) {
1745                 $self->_pod( $class, $comment );
1746             }
1747         }
1748         $self->_pod_cut( $class );
1749     } elsif ( $method =~ /^(belongs_to|has_many|might_have)$/ ) {
1750         $self->_pod( $class, "=head1 RELATIONS" ) unless $self->{_relations_started} { $class } ;
1751         my ( $accessor, $rel_class ) = @_;
1752         $self->_pod( $class, "=head2 $accessor" );
1753         $self->_pod( $class, 'Type: ' . $method );
1754         $self->_pod( $class, "Related object: L<$rel_class>" );
1755         $self->_pod_cut( $class );
1756         $self->{_relations_started} { $class } = 1;
1757     }
1758 }
1759
1760 # Stores a POD documentation
1761 sub _pod {
1762     my ($self, $class, $stmt) = @_;
1763     $self->_raw_stmt( $class, "\n" . $stmt  );
1764 }
1765
1766 sub _pod_cut {
1767     my ($self, $class ) = @_;
1768     $self->_raw_stmt( $class, "\n=cut\n" );
1769 }
1770
1771 # Store a raw source line for a class (for dumping purposes)
1772 sub _raw_stmt {
1773     my ($self, $class, $stmt) = @_;
1774     push(@{$self->{_dump_storage}->{$class}}, $stmt);
1775 }
1776
1777 # Like above, but separately for the externally loaded stuff
1778 sub _ext_stmt {
1779     my ($self, $class, $stmt) = @_;
1780     push(@{$self->{_ext_storage}->{$class}}, $stmt);
1781 }
1782
1783 sub _quote_table_name {
1784     my ($self, $table) = @_;
1785
1786     my $qt = $self->schema->storage->sql_maker->quote_char;
1787
1788     return $table unless $qt;
1789
1790     if (ref $qt) {
1791         return $qt->[0] . $table . $qt->[1];
1792     }
1793
1794     return $qt . $table . $qt;
1795 }
1796
1797 sub _custom_column_info {
1798     my ( $self, $table_name, $column_name, $column_info ) = @_;
1799
1800     if (my $code = $self->custom_column_info) {
1801         return $code->($table_name, $column_name, $column_info) || {};
1802     }
1803     return {};
1804 }
1805
1806 sub _datetime_column_info {
1807     my ( $self, $table_name, $column_name, $column_info ) = @_;
1808     my $result = {};
1809     my $type = $column_info->{data_type} || '';
1810     if ((grep $_, @{ $column_info }{map "inflate_$_", qw/date datetime timestamp/})
1811             or ($type =~ /date|timestamp/i)) {
1812         $result->{timezone} = $self->datetime_timezone if $self->datetime_timezone;
1813         $result->{locale}   = $self->datetime_locale   if $self->datetime_locale;
1814     }
1815     return $result;
1816 }
1817
1818 sub _lc {
1819     my ($self, $name) = @_;
1820
1821     return $self->preserve_case ? $name : lc($name);
1822 }
1823
1824 sub _uc {
1825     my ($self, $name) = @_;
1826
1827     return $self->preserve_case ? $name : uc($name);
1828 }
1829
1830 sub _unregister_source_for_table {
1831     my ($self, $table) = @_;
1832
1833     eval {
1834         local $@;
1835         my $schema = $self->schema;
1836         # in older DBIC it's a private method
1837         my $unregister = $schema->can('unregister_source') || $schema->can('_unregister_source');
1838         $schema->$unregister($self->_table2moniker($table));
1839         delete $self->monikers->{$table};
1840         delete $self->classes->{$table};
1841         delete $self->_upgrading_classes->{$table};
1842         delete $self->{_tables}{$table};
1843     };
1844 }
1845
1846 # remove the dump dir from @INC on destruction
1847 sub DESTROY {
1848     my $self = shift;
1849
1850     @INC = grep $_ ne $self->dump_directory, @INC;
1851 }
1852
1853 =head2 monikers
1854
1855 Returns a hashref of loaded table to moniker mappings.  There will
1856 be two entries for each table, the original name and the "normalized"
1857 name, in the case that the two are different (such as databases
1858 that like uppercase table names, or preserve your original mixed-case
1859 definitions, or what-have-you).
1860
1861 =head2 classes
1862
1863 Returns a hashref of table to class mappings.  In some cases it will
1864 contain multiple entries per table for the original and normalized table
1865 names, as above in L</monikers>.
1866
1867 =head1 SEE ALSO
1868
1869 L<DBIx::Class::Schema::Loader>
1870
1871 =head1 AUTHOR
1872
1873 See L<DBIx::Class::Schema::Loader/AUTHOR> and L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
1874
1875 =head1 LICENSE
1876
1877 This library is free software; you can redistribute it and/or modify it under
1878 the same terms as Perl itself.
1879
1880 =cut
1881
1882 1;
1883 # vim:et sts=4 sw=4 tw=0: