1c49a0da13af321abbedee4c821330583e0b35ff
[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 use DBIx::Class::Schema::Loader::Utils 'split_name';
24 require DBIx::Class;
25
26 our $VERSION = '0.07000';
27
28 __PACKAGE__->mk_group_ro_accessors('simple', qw/
29                                 schema
30                                 schema_class
31
32                                 exclude
33                                 constraint
34                                 additional_classes
35                                 additional_base_classes
36                                 left_base_classes
37                                 components
38                                 resultset_components
39                                 skip_relationships
40                                 skip_load_external
41                                 moniker_map
42                                 custom_column_info
43                                 inflect_singular
44                                 inflect_plural
45                                 debug
46                                 dump_directory
47                                 dump_overwrite
48                                 really_erase_my_files
49                                 resultset_namespace
50                                 default_resultset_class
51                                 schema_base_class
52                                 result_base_class
53                                 overwrite_modifications
54
55                                 relationship_attrs
56
57                                 db_schema
58                                 _tables
59                                 classes
60                                 _upgrading_classes
61                                 monikers
62                                 dynamic
63                                 naming
64                                 datetime_timezone
65                                 datetime_locale
66                                 config_file
67                                 loader_class
68 /);
69
70
71 __PACKAGE__->mk_group_accessors('simple', qw/
72                                 version_to_dump
73                                 schema_version_to_dump
74                                 _upgrading_from
75                                 _upgrading_from_load_classes
76                                 _downgrading_to_load_classes
77                                 _rewriting_result_namespace
78                                 use_namespaces
79                                 result_namespace
80                                 generate_pod
81                                 pod_comment_mode
82                                 pod_comment_spillover_length
83                                 preserve_case
84 /);
85
86 =head1 NAME
87
88 DBIx::Class::Schema::Loader::Base - Base DBIx::Class::Schema::Loader Implementation.
89
90 =head1 SYNOPSIS
91
92 See L<DBIx::Class::Schema::Loader>
93
94 =head1 DESCRIPTION
95
96 This is the base class for the storage-specific C<DBIx::Class::Schema::*>
97 classes, and implements the common functionality between them.
98
99 =head1 CONSTRUCTOR OPTIONS
100
101 These constructor options are the base options for
102 L<DBIx::Class::Schema::Loader/loader_options>.  Available constructor options are:
103
104 =head2 skip_relationships
105
106 Skip setting up relationships.  The default is to attempt the loading
107 of relationships.
108
109 =head2 skip_load_external
110
111 Skip loading of other classes in @INC. The default is to merge all other classes
112 with the same name found in @INC into the schema file we are creating.
113
114 =head2 naming
115
116 Static schemas (ones dumped to disk) will, by default, use the new-style
117 relationship names and singularized Results, unless you're overwriting an
118 existing dump made by an older version of L<DBIx::Class::Schema::Loader>, in
119 which case the backward compatible RelBuilder will be activated, and the
120 appropriate monikerization used.
121
122 Specifying
123
124     naming => 'current'
125
126 will disable the backward-compatible RelBuilder and use
127 the new-style relationship names along with singularized Results, even when
128 overwriting a dump made with an earlier version.
129
130 The option also takes a hashref:
131
132     naming => { relationships => 'v6', monikers => 'v6' }
133
134 The keys are:
135
136 =over 4
137
138 =item relationships
139
140 How to name relationship accessors.
141
142 =item monikers
143
144 How to name Result classes.
145
146 =item column_accessors
147
148 How to name column accessors in Result classes.
149
150 =back
151
152 The values can be:
153
154 =over 4
155
156 =item current
157
158 Latest style, whatever that happens to be.
159
160 =item v4
161
162 Unsingularlized monikers, C<has_many> only relationships with no _id stripping.
163
164 =item v5
165
166 Monikers singularized as whole words, C<might_have> relationships for FKs on
167 C<UNIQUE> constraints, C<_id> stripping for belongs_to relationships.
168
169 Some of the C<_id> stripping edge cases in C<0.05003> have been reverted for
170 the v5 RelBuilder.
171
172 =item v6
173
174 All monikers and relationships are inflected using
175 L<Lingua::EN::Inflect::Phrase>, and there is more aggressive C<_id> stripping
176 from relationship names.
177
178 In general, there is very little difference between v5 and v6 schemas.
179
180 =item v7
181
182 This mode is identical to C<v6> mode, except that monikerization of CamelCase
183 table names is also done correctly.
184
185 CamelCase column names in case-preserving mode will also be handled correctly
186 for relationship name inflection. See L</preserve_case>.
187
188 In this mode, CamelCase L</column_accessors> are normalized based on case
189 transition instead of just being lowercased, so C<FooId> becomes C<foo_id>.
190
191 If you don't have any CamelCase table or column names, you can upgrade without
192 breaking any of your code.
193
194 =back
195
196 Dynamic schemas will always default to the 0.04XXX relationship names and won't
197 singularize Results for backward compatibility, to activate the new RelBuilder
198 and singularization put this in your C<Schema.pm> file:
199
200     __PACKAGE__->naming('current');
201
202 Or if you prefer to use 0.05XXX features but insure that nothing breaks in the
203 next major version upgrade:
204
205     __PACKAGE__->naming('v5');
206
207 =head2 generate_pod
208
209 By default POD will be generated for columns and relationships, using database
210 metadata for the text if available and supported.
211
212 Reading database metadata (e.g. C<COMMENT ON TABLE some_table ...>) is only
213 supported for Postgres right now.
214
215 Set this to C<0> to turn off all POD generation.
216
217 =head2 pod_comment_mode
218
219 Controls where table comments appear in the generated POD. Smaller table
220 comments are appended to the C<NAME> section of the documentation, and larger
221 ones are inserted into C<DESCRIPTION> instead. You can force a C<DESCRIPTION>
222 section to be generated with the comment always, only use C<NAME>, or choose
223 the length threshold at which the comment is forced into the description.
224
225 =over 4
226
227 =item name
228
229 Use C<NAME> section only.
230
231 =item description
232
233 Force C<DESCRIPTION> always.
234
235 =item auto
236
237 Use C<DESCRIPTION> if length > L</pod_comment_spillover_length>, this is the
238 default.
239
240 =back
241
242 =head2 pod_comment_spillover_length
243
244 When pod_comment_mode is set to C<auto>, this is the length of the comment at
245 which it will be forced into a separate description section.
246
247 The default is C<60>
248
249 =head2 relationship_attrs
250
251 Hashref of attributes to pass to each generated relationship, listed
252 by type.  Also supports relationship type 'all', containing options to
253 pass to all generated relationships.  Attributes set for more specific
254 relationship types override those set in 'all'.
255
256 For example:
257
258   relationship_attrs => {
259     belongs_to => { is_deferrable => 1 },
260   },
261
262 use this to make your foreign key constraints DEFERRABLE.
263
264 =head2 debug
265
266 If set to true, each constructive L<DBIx::Class> statement the loader
267 decides to execute will be C<warn>-ed before execution.
268
269 =head2 db_schema
270
271 Set the name of the schema to load (schema in the sense that your database
272 vendor means it).  Does not currently support loading more than one schema
273 name.
274
275 =head2 constraint
276
277 Only load tables matching regex.  Best specified as a qr// regex.
278
279 =head2 exclude
280
281 Exclude tables matching regex.  Best specified as a qr// regex.
282
283 =head2 moniker_map
284
285 Overrides the default table name to moniker translation.  Can be either
286 a hashref of table keys and moniker values, or a coderef for a translator
287 function taking a single scalar table name argument and returning
288 a scalar moniker.  If the hash entry does not exist, or the function
289 returns a false value, the code falls back to default behavior
290 for that table name.
291
292 The default behavior is to split on case transition and non-alphanumeric
293 boundaries, singularize the resulting phrase, then join the titlecased words
294 together. Examples:
295
296     Table Name       | Moniker Name
297     ---------------------------------
298     luser            | Luser
299     luser_group      | LuserGroup
300     luser-opts       | LuserOpt
301     stations_visited | StationVisited
302     routeChange      | RouteChange
303
304 =head2 inflect_plural
305
306 Just like L</moniker_map> above (can be hash/code-ref, falls back to default
307 if hash key does not exist or coderef returns false), but acts as a map
308 for pluralizing relationship names.  The default behavior is to utilize
309 L<Lingua::EN::Inflect::Number/to_PL>.
310
311 =head2 inflect_singular
312
313 As L</inflect_plural> above, but for singularizing relationship names.
314 Default behavior is to utilize L<Lingua::EN::Inflect::Number/to_S>.
315
316 =head2 schema_base_class
317
318 Base class for your schema classes. Defaults to 'DBIx::Class::Schema'.
319
320 =head2 result_base_class
321
322 Base class for your table classes (aka result classes). Defaults to
323 'DBIx::Class::Core'.
324
325 =head2 additional_base_classes
326
327 List of additional base classes all of your table classes will use.
328
329 =head2 left_base_classes
330
331 List of additional base classes all of your table classes will use
332 that need to be leftmost.
333
334 =head2 additional_classes
335
336 List of additional classes which all of your table classes will use.
337
338 =head2 components
339
340 List of additional components to be loaded into all of your table
341 classes.  A good example would be
342 L<InflateColumn::DateTime|DBIx::Class::InflateColumn::DateTime>
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         my $code = slurp $old_real_inc_path;
856
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         $code = $self->_rewrite_old_classnames($code);
866
867         if ($self->dynamic) {
868             warn <<"EOF";
869
870 Detected external content in '$old_real_inc_path', a class name that would have
871 been used by an older version of the Loader.
872
873 * PLEASE RENAME THIS CLASS: from '$old_class' to '$class', as that is the
874 new name of the Result.
875 EOF
876             # kill redefined warnings
877             my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
878             local $SIG{__WARN__} = sub {
879                 $warn_handler->(@_)
880                     unless $_[0] =~ /^Subroutine \S+ redefined/;
881             };
882             eval $code;
883             die $@ if $@;
884         }
885
886         chomp $code;
887         $self->_ext_stmt($class, $code);
888         $self->_ext_stmt($class,
889             qq|# End of lines loaded from '$old_real_inc_path' |
890         );
891     }
892 }
893
894 =head2 load
895
896 Does the actual schema-construction work.
897
898 =cut
899
900 sub load {
901     my $self = shift;
902
903     $self->_load_tables(
904         $self->_tables_list({ constraint => $self->constraint, exclude => $self->exclude })
905     );
906 }
907
908 =head2 rescan
909
910 Arguments: schema
911
912 Rescan the database for changes. Returns a list of the newly added table
913 monikers.
914
915 The schema argument should be the schema class or object to be affected.  It
916 should probably be derived from the original schema_class used during L</load>.
917
918 =cut
919
920 sub rescan {
921     my ($self, $schema) = @_;
922
923     $self->{schema} = $schema;
924     $self->_relbuilder->{schema} = $schema;
925
926     my @created;
927     my @current = $self->_tables_list({ constraint => $self->constraint, exclude => $self->exclude });
928
929     foreach my $table (@current) {
930         if(!exists $self->{_tables}->{$table}) {
931             push(@created, $table);
932         }
933     }
934
935     my %current;
936     @current{@current} = ();
937     foreach my $table (keys %{ $self->{_tables} }) {
938         if (not exists $current{$table}) {
939             $self->_unregister_source_for_table($table);
940         }
941     }
942
943     delete $self->{_dump_storage};
944     delete $self->{_relations_started};
945
946     my $loaded = $self->_load_tables(@current);
947
948     return map { $self->monikers->{$_} } @created;
949 }
950
951 sub _relbuilder {
952     no warnings 'uninitialized';
953     my ($self) = @_;
954
955     return if $self->{skip_relationships};
956
957     if ($self->naming->{relationships} eq 'v4') {
958         require DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_040;
959         return $self->{relbuilder} ||=
960             DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_040->new(
961                 $self->schema,
962                 $self->inflect_plural,
963                 $self->inflect_singular,
964                 $self->relationship_attrs,
965             );
966     }
967     elsif ($self->naming->{relationships} eq 'v5') {
968         require DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_05;
969         return $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_05->new (
970              $self->schema,
971              $self->inflect_plural,
972              $self->inflect_singular,
973              $self->relationship_attrs,
974         );
975     }
976     elsif ($self->naming->{relationships} eq 'v6') {
977         require DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_06;
978         return $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_06->new (
979              $self->schema,
980              $self->inflect_plural,
981              $self->inflect_singular,
982              $self->relationship_attrs,
983         );
984     }
985
986     return $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder->new (
987              $self->schema,
988              $self->inflect_plural,
989              $self->inflect_singular,
990              $self->relationship_attrs,
991     );
992 }
993
994 sub _load_tables {
995     my ($self, @tables) = @_;
996
997     # Save the new tables to the tables list
998     foreach (@tables) {
999         $self->{_tables}->{$_} = 1;
1000     }
1001
1002     $self->_make_src_class($_) for @tables;
1003
1004     # sanity-check for moniker clashes
1005     my $inverse_moniker_idx;
1006     for (keys %{$self->monikers}) {
1007       push @{$inverse_moniker_idx->{$self->monikers->{$_}}}, $_;
1008     }
1009
1010     my @clashes;
1011     for (keys %$inverse_moniker_idx) {
1012       my $tables = $inverse_moniker_idx->{$_};
1013       if (@$tables > 1) {
1014         push @clashes, sprintf ("tables %s reduced to the same source moniker '%s'",
1015           join (', ', map { "'$_'" } @$tables),
1016           $_,
1017         );
1018       }
1019     }
1020
1021     if (@clashes) {
1022       die   'Unable to load schema - chosen moniker/class naming style results in moniker clashes. '
1023           . 'Either change the naming style, or supply an explicit moniker_map: '
1024           . join ('; ', @clashes)
1025           . "\n"
1026       ;
1027     }
1028
1029
1030     $self->_setup_src_meta($_) for @tables;
1031
1032     if(!$self->skip_relationships) {
1033         # The relationship loader needs a working schema
1034         $self->{quiet} = 1;
1035         local $self->{dump_directory} = $self->{temp_directory};
1036         $self->_reload_classes(\@tables);
1037         $self->_load_relationships($_) for @tables;
1038         $self->{quiet} = 0;
1039
1040         # Remove that temp dir from INC so it doesn't get reloaded
1041         @INC = grep $_ ne $self->dump_directory, @INC;
1042     }
1043
1044     $self->_load_external($_)
1045         for map { $self->classes->{$_} } @tables;
1046
1047     # Reload without unloading first to preserve any symbols from external
1048     # packages.
1049     $self->_reload_classes(\@tables, 0);
1050
1051     # Drop temporary cache
1052     delete $self->{_cache};
1053
1054     return \@tables;
1055 }
1056
1057 sub _reload_classes {
1058     my ($self, $tables, $unload) = @_;
1059
1060     my @tables = @$tables;
1061     $unload = 1 unless defined $unload;
1062
1063     # so that we don't repeat custom sections
1064     @INC = grep $_ ne $self->dump_directory, @INC;
1065
1066     $self->_dump_to_dir(map { $self->classes->{$_} } @tables);
1067
1068     unshift @INC, $self->dump_directory;
1069     
1070     my @to_register;
1071     my %have_source = map { $_ => $self->schema->source($_) }
1072         $self->schema->sources;
1073
1074     for my $table (@tables) {
1075         my $moniker = $self->monikers->{$table};
1076         my $class = $self->classes->{$table};
1077         
1078         {
1079             no warnings 'redefine';
1080             local *Class::C3::reinitialize = sub {};
1081             use warnings;
1082
1083             Class::Unload->unload($class) if $unload;
1084             my ($source, $resultset_class);
1085             if (
1086                 ($source = $have_source{$moniker})
1087                 && ($resultset_class = $source->resultset_class)
1088                 && ($resultset_class ne 'DBIx::Class::ResultSet')
1089             ) {
1090                 my $has_file = Class::Inspector->loaded_filename($resultset_class);
1091                 Class::Unload->unload($resultset_class) if $unload;
1092                 $self->_reload_class($resultset_class) if $has_file;
1093             }
1094             $self->_reload_class($class);
1095         }
1096         push @to_register, [$moniker, $class];
1097     }
1098
1099     Class::C3->reinitialize;
1100     for (@to_register) {
1101         $self->schema->register_class(@$_);
1102     }
1103 }
1104
1105 # We use this instead of ensure_class_loaded when there are package symbols we
1106 # want to preserve.
1107 sub _reload_class {
1108     my ($self, $class) = @_;
1109
1110     my $class_path = $self->_class_path($class);
1111     delete $INC{ $class_path };
1112
1113 # kill redefined warnings
1114     my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
1115     local $SIG{__WARN__} = sub {
1116         $warn_handler->(@_)
1117             unless $_[0] =~ /^Subroutine \S+ redefined/;
1118     };
1119     eval "require $class;";
1120 }
1121
1122 sub _get_dump_filename {
1123     my ($self, $class) = (@_);
1124
1125     $class =~ s{::}{/}g;
1126     return $self->dump_directory . q{/} . $class . q{.pm};
1127 }
1128
1129 sub _ensure_dump_subdirs {
1130     my ($self, $class) = (@_);
1131
1132     my @name_parts = split(/::/, $class);
1133     pop @name_parts; # we don't care about the very last element,
1134                      # which is a filename
1135
1136     my $dir = $self->dump_directory;
1137     while (1) {
1138         if(!-d $dir) {
1139             mkdir($dir) or croak "mkdir('$dir') failed: $!";
1140         }
1141         last if !@name_parts;
1142         $dir = File::Spec->catdir($dir, shift @name_parts);
1143     }
1144 }
1145
1146 sub _dump_to_dir {
1147     my ($self, @classes) = @_;
1148
1149     my $schema_class = $self->schema_class;
1150     my $schema_base_class = $self->schema_base_class || 'DBIx::Class::Schema';
1151
1152     my $target_dir = $self->dump_directory;
1153     warn "Dumping manual schema for $schema_class to directory $target_dir ...\n"
1154         unless $self->{dynamic} or $self->{quiet};
1155
1156     my $schema_text =
1157           qq|package $schema_class;\n\n|
1158         . qq|# Created by DBIx::Class::Schema::Loader\n|
1159         . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
1160         . qq|use strict;\nuse warnings;\n\n|
1161         . qq|use base '$schema_base_class';\n\n|;
1162
1163     if ($self->use_namespaces) {
1164         $schema_text .= qq|__PACKAGE__->load_namespaces|;
1165         my $namespace_options;
1166
1167         my @attr = qw/resultset_namespace default_resultset_class/;
1168
1169         unshift @attr, 'result_namespace' unless (not $self->result_namespace) || $self->result_namespace eq 'Result';
1170
1171         for my $attr (@attr) {
1172             if ($self->$attr) {
1173                 $namespace_options .= qq|    $attr => '| . $self->$attr . qq|',\n|
1174             }
1175         }
1176         $schema_text .= qq|(\n$namespace_options)| if $namespace_options;
1177         $schema_text .= qq|;\n|;
1178     }
1179     else {
1180         $schema_text .= qq|__PACKAGE__->load_classes;\n|;
1181     }
1182
1183     {
1184         local $self->{version_to_dump} = $self->schema_version_to_dump;
1185         $self->_write_classfile($schema_class, $schema_text, 1);
1186     }
1187
1188     my $result_base_class = $self->result_base_class || 'DBIx::Class::Core';
1189
1190     foreach my $src_class (@classes) {
1191         my $src_text = 
1192               qq|package $src_class;\n\n|
1193             . qq|# Created by DBIx::Class::Schema::Loader\n|
1194             . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
1195             . qq|use strict;\nuse warnings;\n\n|
1196             . qq|use base '$result_base_class';\n\n|;
1197
1198         $self->_write_classfile($src_class, $src_text);
1199     }
1200
1201     # remove Result dir if downgrading from use_namespaces, and there are no
1202     # files left.
1203     if (my $result_ns = $self->_downgrading_to_load_classes
1204                         || $self->_rewriting_result_namespace) {
1205         my $result_namespace = $self->_result_namespace(
1206             $schema_class,
1207             $result_ns,
1208         );
1209
1210         (my $result_dir = $result_namespace) =~ s{::}{/}g;
1211         $result_dir = $self->dump_directory . '/' . $result_dir;
1212
1213         unless (my @files = glob "$result_dir/*") {
1214             rmdir $result_dir;
1215         }
1216     }
1217
1218     warn "Schema dump completed.\n" unless $self->{dynamic} or $self->{quiet};
1219
1220 }
1221
1222 sub _sig_comment {
1223     my ($self, $version, $ts) = @_;
1224     return qq|\n\n# Created by DBIx::Class::Schema::Loader|
1225          . qq| v| . $version
1226          . q| @ | . $ts 
1227          . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
1228 }
1229
1230 sub _write_classfile {
1231     my ($self, $class, $text, $is_schema) = @_;
1232
1233     my $filename = $self->_get_dump_filename($class);
1234     $self->_ensure_dump_subdirs($class);
1235
1236     if (-f $filename && $self->really_erase_my_files) {
1237         warn "Deleting existing file '$filename' due to "
1238             . "'really_erase_my_files' setting\n" unless $self->{quiet};
1239         unlink($filename);
1240     }    
1241
1242     my ($custom_content, $old_md5, $old_ver, $old_ts) = $self->_get_custom_content($class, $filename);
1243
1244     if (my $old_class = $self->_upgrading_classes->{$class}) {
1245         my $old_filename = $self->_get_dump_filename($old_class);
1246
1247         my ($old_custom_content) = $self->_get_custom_content(
1248             $old_class, $old_filename, 0 # do not add default comment
1249         );
1250
1251         $old_custom_content =~ s/\n\n# You can replace.*\n1;\n//;
1252
1253         if ($old_custom_content) {
1254             $custom_content =
1255                 "\n" . $old_custom_content . "\n" . $custom_content;
1256         }
1257
1258         unlink $old_filename;
1259     }
1260
1261     $custom_content = $self->_rewrite_old_classnames($custom_content);
1262
1263     $text .= qq|$_\n|
1264         for @{$self->{_dump_storage}->{$class} || []};
1265
1266     # Check and see if the dump is infact differnt
1267
1268     my $compare_to;
1269     if ($old_md5) {
1270       $compare_to = $text . $self->_sig_comment($old_ver, $old_ts);
1271       
1272
1273       if (Digest::MD5::md5_base64($compare_to) eq $old_md5) {
1274         return unless $self->_upgrading_from && $is_schema;
1275       }
1276     }
1277
1278     $text .= $self->_sig_comment(
1279       $self->version_to_dump,
1280       POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
1281     );
1282
1283     open(my $fh, '>', $filename)
1284         or croak "Cannot open '$filename' for writing: $!";
1285
1286     # Write the top half and its MD5 sum
1287     print $fh $text . Digest::MD5::md5_base64($text) . "\n";
1288
1289     # Write out anything loaded via external partial class file in @INC
1290     print $fh qq|$_\n|
1291         for @{$self->{_ext_storage}->{$class} || []};
1292
1293     # Write out any custom content the user has added
1294     print $fh $custom_content;
1295
1296     close($fh)
1297         or croak "Error closing '$filename': $!";
1298 }
1299
1300 sub _default_custom_content {
1301     return qq|\n\n# You can replace this text with custom|
1302          . qq| content, and it will be preserved on regeneration|
1303          . qq|\n1;\n|;
1304 }
1305
1306 sub _get_custom_content {
1307     my ($self, $class, $filename, $add_default) = @_;
1308
1309     $add_default = 1 unless defined $add_default;
1310
1311     return ($self->_default_custom_content) if ! -f $filename;
1312
1313     open(my $fh, '<', $filename)
1314         or croak "Cannot open '$filename' for reading: $!";
1315
1316     my $mark_re = 
1317         qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
1318
1319     my $buffer = '';
1320     my ($md5, $ts, $ver);
1321     while(<$fh>) {
1322         if(!$md5 && /$mark_re/) {
1323             $md5 = $2;
1324             my $line = $1;
1325
1326             # Pull out the previous version and timestamp
1327             ($ver, $ts) = $buffer =~ m/# Created by DBIx::Class::Schema::Loader v(.*?) @ (.*?)$/s;
1328
1329             $buffer .= $line;
1330             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"
1331                 if !$self->overwrite_modifications && Digest::MD5::md5_base64($buffer) ne $md5;
1332
1333             $buffer = '';
1334         }
1335         else {
1336             $buffer .= $_;
1337         }
1338     }
1339
1340     croak "Cannot not overwrite '$filename' without 'really_erase_my_files',"
1341         . " it does not appear to have been generated by Loader"
1342             if !$md5;
1343
1344     # Default custom content:
1345     $buffer ||= $self->_default_custom_content if $add_default;
1346
1347     return ($buffer, $md5, $ver, $ts);
1348 }
1349
1350 sub _use {
1351     my $self = shift;
1352     my $target = shift;
1353
1354     foreach (@_) {
1355         warn "$target: use $_;" if $self->debug;
1356         $self->_raw_stmt($target, "use $_;");
1357     }
1358 }
1359
1360 sub _inject {
1361     my $self = shift;
1362     my $target = shift;
1363     my $schema_class = $self->schema_class;
1364
1365     my $blist = join(q{ }, @_);
1366     warn "$target: use base qw/ $blist /;" if $self->debug && @_;
1367     $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
1368 }
1369
1370 sub _result_namespace {
1371     my ($self, $schema_class, $ns) = @_;
1372     my @result_namespace;
1373
1374     if ($ns =~ /^\+(.*)/) {
1375         # Fully qualified namespace
1376         @result_namespace = ($1)
1377     }
1378     else {
1379         # Relative namespace
1380         @result_namespace = ($schema_class, $ns);
1381     }
1382
1383     return wantarray ? @result_namespace : join '::', @result_namespace;
1384 }
1385
1386 # Create class with applicable bases, setup monikers, etc
1387 sub _make_src_class {
1388     my ($self, $table) = @_;
1389
1390     my $schema       = $self->schema;
1391     my $schema_class = $self->schema_class;
1392
1393     my $table_moniker = $self->_table2moniker($table);
1394     my @result_namespace = ($schema_class);
1395     if ($self->use_namespaces) {
1396         my $result_namespace = $self->result_namespace || 'Result';
1397         @result_namespace = $self->_result_namespace(
1398             $schema_class,
1399             $result_namespace,
1400         );
1401     }
1402     my $table_class = join(q{::}, @result_namespace, $table_moniker);
1403
1404     if ((my $upgrading_v = $self->_upgrading_from)
1405             || $self->_rewriting) {
1406         local $self->naming->{monikers} = $upgrading_v
1407             if $upgrading_v;
1408
1409         my @result_namespace = @result_namespace;
1410         if ($self->_upgrading_from_load_classes) {
1411             @result_namespace = ($schema_class);
1412         }
1413         elsif (my $ns = $self->_downgrading_to_load_classes) {
1414             @result_namespace = $self->_result_namespace(
1415                 $schema_class,
1416                 $ns,
1417             );
1418         }
1419         elsif ($ns = $self->_rewriting_result_namespace) {
1420             @result_namespace = $self->_result_namespace(
1421                 $schema_class,
1422                 $ns,
1423             );
1424         }
1425
1426         my $old_class = join(q{::}, @result_namespace,
1427             $self->_table2moniker($table));
1428
1429         $self->_upgrading_classes->{$table_class} = $old_class
1430             unless $table_class eq $old_class;
1431     }
1432
1433 # this was a bad idea, should be ok now without it
1434 #    my $table_normalized = lc $table;
1435 #    $self->classes->{$table_normalized} = $table_class;
1436 #    $self->monikers->{$table_normalized} = $table_moniker;
1437
1438     $self->classes->{$table} = $table_class;
1439     $self->monikers->{$table} = $table_moniker;
1440
1441     $self->_use   ($table_class, @{$self->additional_classes});
1442     $self->_inject($table_class, @{$self->left_base_classes});
1443
1444     if (my @components = @{ $self->components }) {
1445         $self->_dbic_stmt($table_class, 'load_components', @components);
1446     }
1447
1448     $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
1449         if @{$self->resultset_components};
1450     $self->_inject($table_class, @{$self->additional_base_classes});
1451 }
1452
1453 sub _resolve_col_accessor_collisions {
1454     my ($self, $col_info) = @_;
1455
1456     my $base       = $self->result_base_class || 'DBIx::Class::Core';
1457     my @components = map "DBIx::Class::$_", @{ $self->components || [] };
1458
1459     my @methods;
1460
1461     for my $class ($base, @components) {
1462         eval "require ${class};";
1463         die $@ if $@;
1464
1465         push @methods, @{ Class::Inspector->methods($class) || [] };
1466     }
1467
1468     my %methods;
1469     @methods{@methods} = ();
1470
1471     while (my ($col, $info) = each %$col_info) {
1472         my $accessor = $info->{accessor} || $col;
1473
1474         next if $accessor eq 'id'; # special case (very common column)
1475
1476         if (exists $methods{$accessor}) {
1477             $info->{accessor} = undef;
1478         }
1479     }
1480 }
1481
1482 sub _make_column_accessor_name {
1483     my ($self, $column_name) = @_;
1484
1485     return join '_', map lc, split_name $column_name;
1486 }
1487
1488 # Set up metadata (cols, pks, etc)
1489 sub _setup_src_meta {
1490     my ($self, $table) = @_;
1491
1492     my $schema       = $self->schema;
1493     my $schema_class = $self->schema_class;
1494
1495     my $table_class = $self->classes->{$table};
1496     my $table_moniker = $self->monikers->{$table};
1497
1498     my $table_name = $table;
1499     my $name_sep   = $self->schema->storage->sql_maker->name_sep;
1500
1501     if ($name_sep && $table_name =~ /\Q$name_sep\E/) {
1502         $table_name = \ $self->_quote_table_name($table_name);
1503     }
1504
1505     $self->_dbic_stmt($table_class,'table',$table_name);
1506
1507     my $cols = $self->_table_columns($table);
1508     my $col_info = $self->__columns_info_for($table);
1509
1510     while (my ($col, $info) = each %$col_info) {
1511         if ($col =~ /\W/) {
1512             ($info->{accessor} = $col) =~ s/\W+/_/g;
1513         }
1514     }
1515
1516     if ($self->preserve_case) {
1517         while (my ($col, $info) = each %$col_info) {
1518             if ($col ne lc($col)) {
1519                 if ((not exists $self->naming->{column_accessors}) || (($self->naming->{column_accessors} =~ /(\d+)/)[0] >= 7)) {
1520                     $info->{accessor} = $self->_make_column_accessor_name($info->{accessor} || $col);
1521                 }
1522                 else {
1523                     $info->{accessor} = lc($info->{accessor} || $col);
1524                 }
1525             }
1526         }
1527     }
1528     else {
1529         # XXX this needs to go away
1530         $col_info = { map { lc($_), $col_info->{$_} } keys %$col_info };
1531     }
1532
1533     $self->_resolve_col_accessor_collisions($col_info);
1534
1535     my $fks = $self->_table_fk_info($table);
1536
1537     for my $fkdef (@$fks) {
1538         for my $col (@{ $fkdef->{local_columns} }) {
1539             $col_info->{$col}{is_foreign_key} = 1;
1540         }
1541     }
1542     $self->_dbic_stmt(
1543         $table_class,
1544         'add_columns',
1545         map { $_, ($col_info->{$_}||{}) } @$cols
1546     );
1547
1548     my %uniq_tag; # used to eliminate duplicate uniqs
1549
1550     my $pks = $self->_table_pk_info($table) || [];
1551     @$pks ? $self->_dbic_stmt($table_class,'set_primary_key',@$pks)
1552           : carp("$table has no primary key");
1553     $uniq_tag{ join("\0", @$pks) }++ if @$pks; # pk is a uniq
1554
1555     my $uniqs = $self->_table_uniq_info($table) || [];
1556     for (@$uniqs) {
1557         my ($name, $cols) = @$_;
1558         next if $uniq_tag{ join("\0", @$cols) }++; # skip duplicates
1559         $self->_dbic_stmt($table_class,'add_unique_constraint', $name, $cols);
1560     }
1561
1562 }
1563
1564 sub __columns_info_for {
1565     my ($self, $table) = @_;
1566
1567     my $result = $self->_columns_info_for($table);
1568
1569     while (my ($col, $info) = each %$result) {
1570         $info = { %$info, %{ $self->_custom_column_info  ($table, $col, $info) } };
1571         $info = { %$info, %{ $self->_datetime_column_info($table, $col, $info) } };
1572
1573         $result->{$col} = $info;
1574     }
1575
1576     return $result;
1577 }
1578
1579 =head2 tables
1580
1581 Returns a sorted list of loaded tables, using the original database table
1582 names.
1583
1584 =cut
1585
1586 sub tables {
1587     my $self = shift;
1588
1589     return keys %{$self->_tables};
1590 }
1591
1592 # Make a moniker from a table
1593 sub _default_table2moniker {
1594     no warnings 'uninitialized';
1595     my ($self, $table) = @_;
1596
1597     if ($self->naming->{monikers} eq 'v4') {
1598         return join '', map ucfirst, split /[\W_]+/, lc $table;
1599     }
1600     elsif ($self->naming->{monikers} eq 'v5') {
1601         return join '', map ucfirst, split /[\W_]+/,
1602             Lingua::EN::Inflect::Number::to_S(lc $table);
1603     }
1604     elsif ($self->naming->{monikers} eq 'v6') {
1605         (my $as_phrase = lc $table) =~ s/_+/ /g;
1606         my $inflected = Lingua::EN::Inflect::Phrase::to_S($as_phrase);
1607
1608         return join '', map ucfirst, split /\W+/, $inflected;
1609     }
1610
1611     my @words = map lc, split_name $table;
1612     my $as_phrase = join ' ', @words;
1613
1614     my $inflected = Lingua::EN::Inflect::Phrase::to_S($as_phrase);
1615
1616     return join '', map ucfirst, split /\W+/, $inflected;
1617 }
1618
1619 sub _table2moniker {
1620     my ( $self, $table ) = @_;
1621
1622     my $moniker;
1623
1624     if( ref $self->moniker_map eq 'HASH' ) {
1625         $moniker = $self->moniker_map->{$table};
1626     }
1627     elsif( ref $self->moniker_map eq 'CODE' ) {
1628         $moniker = $self->moniker_map->($table);
1629     }
1630
1631     $moniker ||= $self->_default_table2moniker($table);
1632
1633     return $moniker;
1634 }
1635
1636 sub _load_relationships {
1637     my ($self, $table) = @_;
1638
1639     my $tbl_fk_info = $self->_table_fk_info($table);
1640     foreach my $fkdef (@$tbl_fk_info) {
1641         $fkdef->{remote_source} =
1642             $self->monikers->{delete $fkdef->{remote_table}};
1643     }
1644     my $tbl_uniq_info = $self->_table_uniq_info($table);
1645
1646     my $local_moniker = $self->monikers->{$table};
1647     my $rel_stmts = $self->_relbuilder->generate_code($local_moniker, $tbl_fk_info, $tbl_uniq_info);
1648
1649     foreach my $src_class (sort keys %$rel_stmts) {
1650         my $src_stmts = $rel_stmts->{$src_class};
1651         foreach my $stmt (@$src_stmts) {
1652             $self->_dbic_stmt($src_class,$stmt->{method},@{$stmt->{args}});
1653         }
1654     }
1655 }
1656
1657 # Overload these in driver class:
1658
1659 # Returns an arrayref of column names
1660 sub _table_columns { croak "ABSTRACT METHOD" }
1661
1662 # Returns arrayref of pk col names
1663 sub _table_pk_info { croak "ABSTRACT METHOD" }
1664
1665 # Returns an arrayref of uniqs [ [ foo => [ col1, col2 ] ], [ bar => [ ... ] ] ]
1666 sub _table_uniq_info { croak "ABSTRACT METHOD" }
1667
1668 # Returns an arrayref of foreign key constraints, each
1669 #   being a hashref with 3 keys:
1670 #   local_columns (arrayref), remote_columns (arrayref), remote_table
1671 sub _table_fk_info { croak "ABSTRACT METHOD" }
1672
1673 # Returns an array of lower case table names
1674 sub _tables_list { croak "ABSTRACT METHOD" }
1675
1676 # Execute a constructive DBIC class method, with debug/dump_to_dir hooks.
1677 sub _dbic_stmt {
1678     my $self   = shift;
1679     my $class  = shift;
1680     my $method = shift;
1681
1682     # generate the pod for this statement, storing it with $self->_pod
1683     $self->_make_pod( $class, $method, @_ ) if $self->generate_pod;
1684
1685     my $args = dump(@_);
1686     $args = '(' . $args . ')' if @_ < 2;
1687     my $stmt = $method . $args . q{;};
1688
1689     warn qq|$class\->$stmt\n| if $self->debug;
1690     $self->_raw_stmt($class, '__PACKAGE__->' . $stmt);
1691     return;
1692 }
1693
1694 # generates the accompanying pod for a DBIC class method statement,
1695 # storing it with $self->_pod
1696 sub _make_pod {
1697     my $self   = shift;
1698     my $class  = shift;
1699     my $method = shift;
1700
1701     if ( $method eq 'table' ) {
1702         my ($table) = @_;
1703         my $pcm = $self->pod_comment_mode;
1704         my ($comment, $comment_overflows, $comment_in_name, $comment_in_desc);
1705         if ( $self->can('_table_comment') ) {
1706             $comment = $self->_table_comment($table);
1707             $comment_overflows = ($comment and length $comment > $self->pod_comment_spillover_length);
1708             $comment_in_name   = ($pcm eq 'name' or ($pcm eq 'auto' and !$comment_overflows));
1709             $comment_in_desc   = ($pcm eq 'description' or ($pcm eq 'auto' and $comment_overflows));
1710         }
1711         $self->_pod( $class, "=head1 NAME" );
1712         my $table_descr = $class;
1713         $table_descr .= " - " . $comment if $comment and $comment_in_name;
1714         $self->{_class2table}{ $class } = $table;
1715         $self->_pod( $class, $table_descr );
1716         if ($comment and $comment_in_desc) {
1717             $self->_pod( $class, "=head1 DESCRIPTION" );
1718             $self->_pod( $class, $comment );
1719         }
1720         $self->_pod_cut( $class );
1721     } elsif ( $method eq 'add_columns' ) {
1722         $self->_pod( $class, "=head1 ACCESSORS" );
1723         my $col_counter = 0;
1724         my @cols = @_;
1725         while( my ($name,$attrs) = splice @cols,0,2 ) {
1726             $col_counter++;
1727             $self->_pod( $class, '=head2 ' . $name  );
1728             $self->_pod( $class,
1729                          join "\n", map {
1730                              my $s = $attrs->{$_};
1731                              $s = !defined $s         ? 'undef'          :
1732                                   length($s) == 0     ? '(empty string)' :
1733                                   ref($s) eq 'SCALAR' ? $$s :
1734                                   ref($s)             ? do {
1735                                                         my $dd = Dumper;
1736                                                         $dd->Indent(0);
1737                                                         $dd->Values([$s]);
1738                                                         $dd->Dump;
1739                                                       } :
1740                                   looks_like_number($s) ? $s :
1741                                                         qq{'$s'}
1742                                   ;
1743
1744                              "  $_: $s"
1745                          } sort keys %$attrs,
1746                        );
1747
1748             if( $self->can('_column_comment')
1749                 and my $comment = $self->_column_comment( $self->{_class2table}{$class}, $col_counter)
1750               ) {
1751                 $self->_pod( $class, $comment );
1752             }
1753         }
1754         $self->_pod_cut( $class );
1755     } elsif ( $method =~ /^(belongs_to|has_many|might_have)$/ ) {
1756         $self->_pod( $class, "=head1 RELATIONS" ) unless $self->{_relations_started} { $class } ;
1757         my ( $accessor, $rel_class ) = @_;
1758         $self->_pod( $class, "=head2 $accessor" );
1759         $self->_pod( $class, 'Type: ' . $method );
1760         $self->_pod( $class, "Related object: L<$rel_class>" );
1761         $self->_pod_cut( $class );
1762         $self->{_relations_started} { $class } = 1;
1763     }
1764 }
1765
1766 # Stores a POD documentation
1767 sub _pod {
1768     my ($self, $class, $stmt) = @_;
1769     $self->_raw_stmt( $class, "\n" . $stmt  );
1770 }
1771
1772 sub _pod_cut {
1773     my ($self, $class ) = @_;
1774     $self->_raw_stmt( $class, "\n=cut\n" );
1775 }
1776
1777 # Store a raw source line for a class (for dumping purposes)
1778 sub _raw_stmt {
1779     my ($self, $class, $stmt) = @_;
1780     push(@{$self->{_dump_storage}->{$class}}, $stmt);
1781 }
1782
1783 # Like above, but separately for the externally loaded stuff
1784 sub _ext_stmt {
1785     my ($self, $class, $stmt) = @_;
1786     push(@{$self->{_ext_storage}->{$class}}, $stmt);
1787 }
1788
1789 sub _quote_table_name {
1790     my ($self, $table) = @_;
1791
1792     my $qt = $self->schema->storage->sql_maker->quote_char;
1793
1794     return $table unless $qt;
1795
1796     if (ref $qt) {
1797         return $qt->[0] . $table . $qt->[1];
1798     }
1799
1800     return $qt . $table . $qt;
1801 }
1802
1803 sub _custom_column_info {
1804     my ( $self, $table_name, $column_name, $column_info ) = @_;
1805
1806     if (my $code = $self->custom_column_info) {
1807         return $code->($table_name, $column_name, $column_info) || {};
1808     }
1809     return {};
1810 }
1811
1812 sub _datetime_column_info {
1813     my ( $self, $table_name, $column_name, $column_info ) = @_;
1814     my $result = {};
1815     my $type = $column_info->{data_type} || '';
1816     if ((grep $_, @{ $column_info }{map "inflate_$_", qw/date datetime timestamp/})
1817             or ($type =~ /date|timestamp/i)) {
1818         $result->{timezone} = $self->datetime_timezone if $self->datetime_timezone;
1819         $result->{locale}   = $self->datetime_locale   if $self->datetime_locale;
1820     }
1821     return $result;
1822 }
1823
1824 sub _lc {
1825     my ($self, $name) = @_;
1826
1827     return $self->preserve_case ? $name : lc($name);
1828 }
1829
1830 sub _uc {
1831     my ($self, $name) = @_;
1832
1833     return $self->preserve_case ? $name : uc($name);
1834 }
1835
1836 sub _unregister_source_for_table {
1837     my ($self, $table) = @_;
1838
1839     eval {
1840         local $@;
1841         my $schema = $self->schema;
1842         # in older DBIC it's a private method
1843         my $unregister = $schema->can('unregister_source') || $schema->can('_unregister_source');
1844         $schema->$unregister($self->_table2moniker($table));
1845         delete $self->monikers->{$table};
1846         delete $self->classes->{$table};
1847         delete $self->_upgrading_classes->{$table};
1848         delete $self->{_tables}{$table};
1849     };
1850 }
1851
1852 # remove the dump dir from @INC on destruction
1853 sub DESTROY {
1854     my $self = shift;
1855
1856     @INC = grep $_ ne $self->dump_directory, @INC;
1857 }
1858
1859 =head2 monikers
1860
1861 Returns a hashref of loaded table to moniker mappings.  There will
1862 be two entries for each table, the original name and the "normalized"
1863 name, in the case that the two are different (such as databases
1864 that like uppercase table names, or preserve your original mixed-case
1865 definitions, or what-have-you).
1866
1867 =head2 classes
1868
1869 Returns a hashref of table to class mappings.  In some cases it will
1870 contain multiple entries per table for the original and normalized table
1871 names, as above in L</monikers>.
1872
1873 =head1 SEE ALSO
1874
1875 L<DBIx::Class::Schema::Loader>
1876
1877 =head1 AUTHOR
1878
1879 See L<DBIx::Class::Schema::Loader/AUTHOR> and L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
1880
1881 =head1 LICENSE
1882
1883 This library is free software; you can redistribute it and/or modify it under
1884 the same terms as Perl itself.
1885
1886 =cut
1887
1888 1;
1889 # vim:et sts=4 sw=4 tw=0: