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