try to support bizarre column names
[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         open(my $fh, '<', $old_real_inc_path)
856             or croak "Failed to open '$old_real_inc_path' for reading: $!";
857         $self->_ext_stmt($class, <<"EOF");
858
859 # These lines were loaded from '$old_real_inc_path',
860 # based on the Result class name that would have been created by an older
861 # version of the Loader. For a static schema, this happens only once during
862 # upgrade. See skip_load_external to disable this feature.
863 EOF
864
865         my $code = slurp $old_real_inc_path;
866         $code = $self->_rewrite_old_classnames($code);
867
868         if ($self->dynamic) {
869             warn <<"EOF";
870
871 Detected external content in '$old_real_inc_path', a class name that would have
872 been used by an older version of the Loader.
873
874 * PLEASE RENAME THIS CLASS: from '$old_class' to '$class', as that is the
875 new name of the Result.
876 EOF
877             # kill redefined warnings
878             my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
879             local $SIG{__WARN__} = sub {
880                 $warn_handler->(@_)
881                     unless $_[0] =~ /^Subroutine \S+ redefined/;
882             };
883             eval $code;
884             die $@ if $@;
885         }
886
887         chomp $code;
888         $self->_ext_stmt($class, $code);
889         $self->_ext_stmt($class,
890             qq|# End of lines loaded from '$old_real_inc_path' |
891         );
892     }
893 }
894
895 =head2 load
896
897 Does the actual schema-construction work.
898
899 =cut
900
901 sub load {
902     my $self = shift;
903
904     $self->_load_tables(
905         $self->_tables_list({ constraint => $self->constraint, exclude => $self->exclude })
906     );
907 }
908
909 =head2 rescan
910
911 Arguments: schema
912
913 Rescan the database for newly added tables.  Does
914 not process drops or changes.  Returns a list of
915 the newly added table monikers.
916
917 The schema argument should be the schema class
918 or object to be affected.  It should probably
919 be derived from the original schema_class used
920 during L</load>.
921
922 =cut
923
924 sub rescan {
925     my ($self, $schema) = @_;
926
927     $self->{schema} = $schema;
928     $self->_relbuilder->{schema} = $schema;
929
930     my @created;
931     my @current = $self->_tables_list({ constraint => $self->constraint, exclude => $self->exclude });
932
933     foreach my $table (@current) {
934         if(!exists $self->{_tables}->{$table}) {
935             push(@created, $table);
936         }
937     }
938
939     my %current;
940     @current{@current} = ();
941     foreach my $table (keys %{ $self->{_tables} }) {
942         if (not exists $current{$table}) {
943             $self->_unregister_source_for_table($table);
944         }
945     }
946
947     my $loaded = $self->_load_tables(@created);
948
949     return map { $self->monikers->{$_} } @$loaded;
950 }
951
952 sub _relbuilder {
953     no warnings 'uninitialized';
954     my ($self) = @_;
955
956     return if $self->{skip_relationships};
957
958     if ($self->naming->{relationships} eq 'v4') {
959         require DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_040;
960         return $self->{relbuilder} ||=
961             DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_040->new(
962                 $self->schema,
963                 $self->inflect_plural,
964                 $self->inflect_singular,
965                 $self->relationship_attrs,
966             );
967     }
968     elsif ($self->naming->{relationships} eq 'v5') {
969         require DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_05;
970         return $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_05->new (
971              $self->schema,
972              $self->inflect_plural,
973              $self->inflect_singular,
974              $self->relationship_attrs,
975         );
976     }
977     elsif ($self->naming->{relationships} eq 'v6') {
978         require DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_06;
979         return $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_06->new (
980              $self->schema,
981              $self->inflect_plural,
982              $self->inflect_singular,
983              $self->relationship_attrs,
984         );
985     }
986
987     return $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder->new (
988              $self->schema,
989              $self->inflect_plural,
990              $self->inflect_singular,
991              $self->relationship_attrs,
992     );
993 }
994
995 sub _load_tables {
996     my ($self, @tables) = @_;
997
998     # Save the new tables to the tables list
999     foreach (@tables) {
1000         $self->{_tables}->{$_} = 1;
1001     }
1002
1003     $self->_make_src_class($_) for @tables;
1004
1005     # sanity-check for moniker clashes
1006     my $inverse_moniker_idx;
1007     for (keys %{$self->monikers}) {
1008       push @{$inverse_moniker_idx->{$self->monikers->{$_}}}, $_;
1009     }
1010
1011     my @clashes;
1012     for (keys %$inverse_moniker_idx) {
1013       my $tables = $inverse_moniker_idx->{$_};
1014       if (@$tables > 1) {
1015         push @clashes, sprintf ("tables %s reduced to the same source moniker '%s'",
1016           join (', ', map { "'$_'" } @$tables),
1017           $_,
1018         );
1019       }
1020     }
1021
1022     if (@clashes) {
1023       die   'Unable to load schema - chosen moniker/class naming style results in moniker clashes. '
1024           . 'Either change the naming style, or supply an explicit moniker_map: '
1025           . join ('; ', @clashes)
1026           . "\n"
1027       ;
1028     }
1029
1030
1031     $self->_setup_src_meta($_) for @tables;
1032
1033     if(!$self->skip_relationships) {
1034         # The relationship loader needs a working schema
1035         $self->{quiet} = 1;
1036         local $self->{dump_directory} = $self->{temp_directory};
1037         $self->_reload_classes(\@tables);
1038         $self->_load_relationships($_) for @tables;
1039         $self->{quiet} = 0;
1040
1041         # Remove that temp dir from INC so it doesn't get reloaded
1042         @INC = grep $_ ne $self->dump_directory, @INC;
1043     }
1044
1045     $self->_load_external($_)
1046         for map { $self->classes->{$_} } @tables;
1047
1048     # Reload without unloading first to preserve any symbols from external
1049     # packages.
1050     $self->_reload_classes(\@tables, 0);
1051
1052     # Drop temporary cache
1053     delete $self->{_cache};
1054
1055     return \@tables;
1056 }
1057
1058 sub _reload_classes {
1059     my ($self, $tables, $unload) = @_;
1060
1061     my @tables = @$tables;
1062     $unload = 1 unless defined $unload;
1063
1064     # so that we don't repeat custom sections
1065     @INC = grep $_ ne $self->dump_directory, @INC;
1066
1067     $self->_dump_to_dir(map { $self->classes->{$_} } @tables);
1068
1069     unshift @INC, $self->dump_directory;
1070     
1071     my @to_register;
1072     my %have_source = map { $_ => $self->schema->source($_) }
1073         $self->schema->sources;
1074
1075     for my $table (@tables) {
1076         my $moniker = $self->monikers->{$table};
1077         my $class = $self->classes->{$table};
1078         
1079         {
1080             no warnings 'redefine';
1081             local *Class::C3::reinitialize = sub {};
1082             use warnings;
1083
1084             Class::Unload->unload($class) if $unload;
1085             my ($source, $resultset_class);
1086             if (
1087                 ($source = $have_source{$moniker})
1088                 && ($resultset_class = $source->resultset_class)
1089                 && ($resultset_class ne 'DBIx::Class::ResultSet')
1090             ) {
1091                 my $has_file = Class::Inspector->loaded_filename($resultset_class);
1092                 Class::Unload->unload($resultset_class) if $unload;
1093                 $self->_reload_class($resultset_class) if $has_file;
1094             }
1095             $self->_reload_class($class);
1096         }
1097         push @to_register, [$moniker, $class];
1098     }
1099
1100     Class::C3->reinitialize;
1101     for (@to_register) {
1102         $self->schema->register_class(@$_);
1103     }
1104 }
1105
1106 # We use this instead of ensure_class_loaded when there are package symbols we
1107 # want to preserve.
1108 sub _reload_class {
1109     my ($self, $class) = @_;
1110
1111     my $class_path = $self->_class_path($class);
1112     delete $INC{ $class_path };
1113
1114 # kill redefined warnings
1115     my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
1116     local $SIG{__WARN__} = sub {
1117         $warn_handler->(@_)
1118             unless $_[0] =~ /^Subroutine \S+ redefined/;
1119     };
1120     eval "require $class;";
1121 }
1122
1123 sub _get_dump_filename {
1124     my ($self, $class) = (@_);
1125
1126     $class =~ s{::}{/}g;
1127     return $self->dump_directory . q{/} . $class . q{.pm};
1128 }
1129
1130 sub _ensure_dump_subdirs {
1131     my ($self, $class) = (@_);
1132
1133     my @name_parts = split(/::/, $class);
1134     pop @name_parts; # we don't care about the very last element,
1135                      # which is a filename
1136
1137     my $dir = $self->dump_directory;
1138     while (1) {
1139         if(!-d $dir) {
1140             mkdir($dir) or croak "mkdir('$dir') failed: $!";
1141         }
1142         last if !@name_parts;
1143         $dir = File::Spec->catdir($dir, shift @name_parts);
1144     }
1145 }
1146
1147 sub _dump_to_dir {
1148     my ($self, @classes) = @_;
1149
1150     my $schema_class = $self->schema_class;
1151     my $schema_base_class = $self->schema_base_class || 'DBIx::Class::Schema';
1152
1153     my $target_dir = $self->dump_directory;
1154     warn "Dumping manual schema for $schema_class to directory $target_dir ...\n"
1155         unless $self->{dynamic} or $self->{quiet};
1156
1157     my $schema_text =
1158           qq|package $schema_class;\n\n|
1159         . qq|# Created by DBIx::Class::Schema::Loader\n|
1160         . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
1161         . qq|use strict;\nuse warnings;\n\n|
1162         . qq|use base '$schema_base_class';\n\n|;
1163
1164     if ($self->use_namespaces) {
1165         $schema_text .= qq|__PACKAGE__->load_namespaces|;
1166         my $namespace_options;
1167
1168         my @attr = qw/resultset_namespace default_resultset_class/;
1169
1170         unshift @attr, 'result_namespace' unless (not $self->result_namespace) || $self->result_namespace eq 'Result';
1171
1172         for my $attr (@attr) {
1173             if ($self->$attr) {
1174                 $namespace_options .= qq|    $attr => '| . $self->$attr . qq|',\n|
1175             }
1176         }
1177         $schema_text .= qq|(\n$namespace_options)| if $namespace_options;
1178         $schema_text .= qq|;\n|;
1179     }
1180     else {
1181         $schema_text .= qq|__PACKAGE__->load_classes;\n|;
1182     }
1183
1184     {
1185         local $self->{version_to_dump} = $self->schema_version_to_dump;
1186         $self->_write_classfile($schema_class, $schema_text, 1);
1187     }
1188
1189     my $result_base_class = $self->result_base_class || 'DBIx::Class::Core';
1190
1191     foreach my $src_class (@classes) {
1192         my $src_text = 
1193               qq|package $src_class;\n\n|
1194             . qq|# Created by DBIx::Class::Schema::Loader\n|
1195             . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
1196             . qq|use strict;\nuse warnings;\n\n|
1197             . qq|use base '$result_base_class';\n\n|;
1198
1199         $self->_write_classfile($src_class, $src_text);
1200     }
1201
1202     # remove Result dir if downgrading from use_namespaces, and there are no
1203     # files left.
1204     if (my $result_ns = $self->_downgrading_to_load_classes
1205                         || $self->_rewriting_result_namespace) {
1206         my $result_namespace = $self->_result_namespace(
1207             $schema_class,
1208             $result_ns,
1209         );
1210
1211         (my $result_dir = $result_namespace) =~ s{::}{/}g;
1212         $result_dir = $self->dump_directory . '/' . $result_dir;
1213
1214         unless (my @files = glob "$result_dir/*") {
1215             rmdir $result_dir;
1216         }
1217     }
1218
1219     warn "Schema dump completed.\n" unless $self->{dynamic} or $self->{quiet};
1220
1221 }
1222
1223 sub _sig_comment {
1224     my ($self, $version, $ts) = @_;
1225     return qq|\n\n# Created by DBIx::Class::Schema::Loader|
1226          . qq| v| . $version
1227          . q| @ | . $ts 
1228          . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
1229 }
1230
1231 sub _write_classfile {
1232     my ($self, $class, $text, $is_schema) = @_;
1233
1234     my $filename = $self->_get_dump_filename($class);
1235     $self->_ensure_dump_subdirs($class);
1236
1237     if (-f $filename && $self->really_erase_my_files) {
1238         warn "Deleting existing file '$filename' due to "
1239             . "'really_erase_my_files' setting\n" unless $self->{quiet};
1240         unlink($filename);
1241     }    
1242
1243     my ($custom_content, $old_md5, $old_ver, $old_ts) = $self->_get_custom_content($class, $filename);
1244
1245     if (my $old_class = $self->_upgrading_classes->{$class}) {
1246         my $old_filename = $self->_get_dump_filename($old_class);
1247
1248         my ($old_custom_content) = $self->_get_custom_content(
1249             $old_class, $old_filename, 0 # do not add default comment
1250         );
1251
1252         $old_custom_content =~ s/\n\n# You can replace.*\n1;\n//;
1253
1254         if ($old_custom_content) {
1255             $custom_content =
1256                 "\n" . $old_custom_content . "\n" . $custom_content;
1257         }
1258
1259         unlink $old_filename;
1260     }
1261
1262     $custom_content = $self->_rewrite_old_classnames($custom_content);
1263
1264     $text .= qq|$_\n|
1265         for @{$self->{_dump_storage}->{$class} || []};
1266
1267     # Check and see if the dump is infact differnt
1268
1269     my $compare_to;
1270     if ($old_md5) {
1271       $compare_to = $text . $self->_sig_comment($old_ver, $old_ts);
1272       
1273
1274       if (Digest::MD5::md5_base64($compare_to) eq $old_md5) {
1275         return unless $self->_upgrading_from && $is_schema;
1276       }
1277     }
1278
1279     $text .= $self->_sig_comment(
1280       $self->version_to_dump,
1281       POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
1282     );
1283
1284     open(my $fh, '>', $filename)
1285         or croak "Cannot open '$filename' for writing: $!";
1286
1287     # Write the top half and its MD5 sum
1288     print $fh $text . Digest::MD5::md5_base64($text) . "\n";
1289
1290     # Write out anything loaded via external partial class file in @INC
1291     print $fh qq|$_\n|
1292         for @{$self->{_ext_storage}->{$class} || []};
1293
1294     # Write out any custom content the user has added
1295     print $fh $custom_content;
1296
1297     close($fh)
1298         or croak "Error closing '$filename': $!";
1299 }
1300
1301 sub _default_custom_content {
1302     return qq|\n\n# You can replace this text with custom|
1303          . qq| content, and it will be preserved on regeneration|
1304          . qq|\n1;\n|;
1305 }
1306
1307 sub _get_custom_content {
1308     my ($self, $class, $filename, $add_default) = @_;
1309
1310     $add_default = 1 unless defined $add_default;
1311
1312     return ($self->_default_custom_content) if ! -f $filename;
1313
1314     open(my $fh, '<', $filename)
1315         or croak "Cannot open '$filename' for reading: $!";
1316
1317     my $mark_re = 
1318         qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
1319
1320     my $buffer = '';
1321     my ($md5, $ts, $ver);
1322     while(<$fh>) {
1323         if(!$md5 && /$mark_re/) {
1324             $md5 = $2;
1325             my $line = $1;
1326
1327             # Pull out the previous version and timestamp
1328             ($ver, $ts) = $buffer =~ m/# Created by DBIx::Class::Schema::Loader v(.*?) @ (.*?)$/s;
1329
1330             $buffer .= $line;
1331             croak "Checksum mismatch in '$filename', the auto-generated part of the file has been modified outside of this loader.  Aborting.\nIf you want to overwrite these modifications, set the 'overwrite_modifications' loader option.\n"
1332                 if !$self->overwrite_modifications && Digest::MD5::md5_base64($buffer) ne $md5;
1333
1334             $buffer = '';
1335         }
1336         else {
1337             $buffer .= $_;
1338         }
1339     }
1340
1341     croak "Cannot not overwrite '$filename' without 'really_erase_my_files',"
1342         . " it does not appear to have been generated by Loader"
1343             if !$md5;
1344
1345     # Default custom content:
1346     $buffer ||= $self->_default_custom_content if $add_default;
1347
1348     return ($buffer, $md5, $ver, $ts);
1349 }
1350
1351 sub _use {
1352     my $self = shift;
1353     my $target = shift;
1354
1355     foreach (@_) {
1356         warn "$target: use $_;" if $self->debug;
1357         $self->_raw_stmt($target, "use $_;");
1358     }
1359 }
1360
1361 sub _inject {
1362     my $self = shift;
1363     my $target = shift;
1364     my $schema_class = $self->schema_class;
1365
1366     my $blist = join(q{ }, @_);
1367     warn "$target: use base qw/ $blist /;" if $self->debug && @_;
1368     $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
1369 }
1370
1371 sub _result_namespace {
1372     my ($self, $schema_class, $ns) = @_;
1373     my @result_namespace;
1374
1375     if ($ns =~ /^\+(.*)/) {
1376         # Fully qualified namespace
1377         @result_namespace = ($1)
1378     }
1379     else {
1380         # Relative namespace
1381         @result_namespace = ($schema_class, $ns);
1382     }
1383
1384     return wantarray ? @result_namespace : join '::', @result_namespace;
1385 }
1386
1387 # Create class with applicable bases, setup monikers, etc
1388 sub _make_src_class {
1389     my ($self, $table) = @_;
1390
1391     my $schema       = $self->schema;
1392     my $schema_class = $self->schema_class;
1393
1394     my $table_moniker = $self->_table2moniker($table);
1395     my @result_namespace = ($schema_class);
1396     if ($self->use_namespaces) {
1397         my $result_namespace = $self->result_namespace || 'Result';
1398         @result_namespace = $self->_result_namespace(
1399             $schema_class,
1400             $result_namespace,
1401         );
1402     }
1403     my $table_class = join(q{::}, @result_namespace, $table_moniker);
1404
1405     if ((my $upgrading_v = $self->_upgrading_from)
1406             || $self->_rewriting) {
1407         local $self->naming->{monikers} = $upgrading_v
1408             if $upgrading_v;
1409
1410         my @result_namespace = @result_namespace;
1411         if ($self->_upgrading_from_load_classes) {
1412             @result_namespace = ($schema_class);
1413         }
1414         elsif (my $ns = $self->_downgrading_to_load_classes) {
1415             @result_namespace = $self->_result_namespace(
1416                 $schema_class,
1417                 $ns,
1418             );
1419         }
1420         elsif ($ns = $self->_rewriting_result_namespace) {
1421             @result_namespace = $self->_result_namespace(
1422                 $schema_class,
1423                 $ns,
1424             );
1425         }
1426
1427         my $old_class = join(q{::}, @result_namespace,
1428             $self->_table2moniker($table));
1429
1430         $self->_upgrading_classes->{$table_class} = $old_class
1431             unless $table_class eq $old_class;
1432     }
1433
1434 # this was a bad idea, should be ok now without it
1435 #    my $table_normalized = lc $table;
1436 #    $self->classes->{$table_normalized} = $table_class;
1437 #    $self->monikers->{$table_normalized} = $table_moniker;
1438
1439     $self->classes->{$table} = $table_class;
1440     $self->monikers->{$table} = $table_moniker;
1441
1442     $self->_use   ($table_class, @{$self->additional_classes});
1443     $self->_inject($table_class, @{$self->left_base_classes});
1444
1445     if (my @components = @{ $self->components }) {
1446         $self->_dbic_stmt($table_class, 'load_components', @components);
1447     }
1448
1449     $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
1450         if @{$self->resultset_components};
1451     $self->_inject($table_class, @{$self->additional_base_classes});
1452 }
1453
1454 sub _resolve_col_accessor_collisions {
1455     my ($self, $col_info) = @_;
1456
1457     my $base       = $self->result_base_class || 'DBIx::Class::Core';
1458     my @components = map "DBIx::Class::$_", @{ $self->components || [] };
1459
1460     my @methods;
1461
1462     for my $class ($base, @components) {
1463         eval "require ${class};";
1464         die $@ if $@;
1465
1466         push @methods, @{ Class::Inspector->methods($class) || [] };
1467     }
1468
1469     my %methods;
1470     @methods{@methods} = ();
1471
1472     while (my ($col, $info) = each %$col_info) {
1473         my $accessor = $info->{accessor} || $col;
1474
1475         next if $accessor eq 'id'; # special case (very common column)
1476
1477         if (exists $methods{$accessor}) {
1478             $info->{accessor} = undef;
1479         }
1480     }
1481 }
1482
1483 sub _make_column_accessor_name {
1484     my ($self, $column_name) = @_;
1485
1486     return join '_', map lc, split_name $column_name;
1487 }
1488
1489 # Set up metadata (cols, pks, etc)
1490 sub _setup_src_meta {
1491     my ($self, $table) = @_;
1492
1493     my $schema       = $self->schema;
1494     my $schema_class = $self->schema_class;
1495
1496     my $table_class = $self->classes->{$table};
1497     my $table_moniker = $self->monikers->{$table};
1498
1499     my $table_name = $table;
1500     my $name_sep   = $self->schema->storage->sql_maker->name_sep;
1501
1502     if ($name_sep && $table_name =~ /\Q$name_sep\E/) {
1503         $table_name = \ $self->_quote_table_name($table_name);
1504     }
1505
1506     $self->_dbic_stmt($table_class,'table',$table_name);
1507
1508     my $cols = $self->_table_columns($table);
1509     my $col_info = $self->__columns_info_for($table);
1510
1511     while (my ($col, $info) = each %$col_info) {
1512         if ($col =~ /\W/) {
1513             ($info->{accessor} = $col) =~ s/\W+/_/g;
1514         }
1515     }
1516
1517     if ($self->preserve_case) {
1518         while (my ($col, $info) = each %$col_info) {
1519             if ($col ne lc($col)) {
1520                 if ((not exists $self->naming->{column_accessors}) || (($self->naming->{column_accessors} =~ /(\d+)/)[0] >= 7)) {
1521                     $info->{accessor} = $self->_make_column_accessor_name($info->{accessor} || $col);
1522                 }
1523                 else {
1524                     $info->{accessor} = lc($info->{accessor} || $col);
1525                 }
1526             }
1527         }
1528     }
1529     else {
1530         # XXX this needs to go away
1531         $col_info = { map { lc($_), $col_info->{$_} } keys %$col_info };
1532     }
1533
1534     $self->_resolve_col_accessor_collisions($col_info);
1535
1536     my $fks = $self->_table_fk_info($table);
1537
1538     for my $fkdef (@$fks) {
1539         for my $col (@{ $fkdef->{local_columns} }) {
1540             $col_info->{$col}{is_foreign_key} = 1;
1541         }
1542     }
1543     $self->_dbic_stmt(
1544         $table_class,
1545         'add_columns',
1546         map { $_, ($col_info->{$_}||{}) } @$cols
1547     );
1548
1549     my %uniq_tag; # used to eliminate duplicate uniqs
1550
1551     my $pks = $self->_table_pk_info($table) || [];
1552     @$pks ? $self->_dbic_stmt($table_class,'set_primary_key',@$pks)
1553           : carp("$table has no primary key");
1554     $uniq_tag{ join("\0", @$pks) }++ if @$pks; # pk is a uniq
1555
1556     my $uniqs = $self->_table_uniq_info($table) || [];
1557     for (@$uniqs) {
1558         my ($name, $cols) = @$_;
1559         next if $uniq_tag{ join("\0", @$cols) }++; # skip duplicates
1560         $self->_dbic_stmt($table_class,'add_unique_constraint', $name, $cols);
1561     }
1562
1563 }
1564
1565 sub __columns_info_for {
1566     my ($self, $table) = @_;
1567
1568     my $result = $self->_columns_info_for($table);
1569
1570     while (my ($col, $info) = each %$result) {
1571         $info = { %$info, %{ $self->_custom_column_info  ($table, $col, $info) } };
1572         $info = { %$info, %{ $self->_datetime_column_info($table, $col, $info) } };
1573
1574         $result->{$col} = $info;
1575     }
1576
1577     return $result;
1578 }
1579
1580 =head2 tables
1581
1582 Returns a sorted list of loaded tables, using the original database table
1583 names.
1584
1585 =cut
1586
1587 sub tables {
1588     my $self = shift;
1589
1590     return keys %{$self->_tables};
1591 }
1592
1593 # Make a moniker from a table
1594 sub _default_table2moniker {
1595     no warnings 'uninitialized';
1596     my ($self, $table) = @_;
1597
1598     if ($self->naming->{monikers} eq 'v4') {
1599         return join '', map ucfirst, split /[\W_]+/, lc $table;
1600     }
1601     elsif ($self->naming->{monikers} eq 'v5') {
1602         return join '', map ucfirst, split /[\W_]+/,
1603             Lingua::EN::Inflect::Number::to_S(lc $table);
1604     }
1605     elsif ($self->naming->{monikers} eq 'v6') {
1606         (my $as_phrase = lc $table) =~ s/_+/ /g;
1607         my $inflected = Lingua::EN::Inflect::Phrase::to_S($as_phrase);
1608
1609         return join '', map ucfirst, split /\W+/, $inflected;
1610     }
1611
1612     my @words = map lc, split_name $table;
1613     my $as_phrase = join ' ', @words;
1614
1615     my $inflected = Lingua::EN::Inflect::Phrase::to_S($as_phrase);
1616
1617     return join '', map ucfirst, split /\W+/, $inflected;
1618 }
1619
1620 sub _table2moniker {
1621     my ( $self, $table ) = @_;
1622
1623     my $moniker;
1624
1625     if( ref $self->moniker_map eq 'HASH' ) {
1626         $moniker = $self->moniker_map->{$table};
1627     }
1628     elsif( ref $self->moniker_map eq 'CODE' ) {
1629         $moniker = $self->moniker_map->($table);
1630     }
1631
1632     $moniker ||= $self->_default_table2moniker($table);
1633
1634     return $moniker;
1635 }
1636
1637 sub _load_relationships {
1638     my ($self, $table) = @_;
1639
1640     my $tbl_fk_info = $self->_table_fk_info($table);
1641     foreach my $fkdef (@$tbl_fk_info) {
1642         $fkdef->{remote_source} =
1643             $self->monikers->{delete $fkdef->{remote_table}};
1644     }
1645     my $tbl_uniq_info = $self->_table_uniq_info($table);
1646
1647     my $local_moniker = $self->monikers->{$table};
1648     my $rel_stmts = $self->_relbuilder->generate_code($local_moniker, $tbl_fk_info, $tbl_uniq_info);
1649
1650     foreach my $src_class (sort keys %$rel_stmts) {
1651         my $src_stmts = $rel_stmts->{$src_class};
1652         foreach my $stmt (@$src_stmts) {
1653             $self->_dbic_stmt($src_class,$stmt->{method},@{$stmt->{args}});
1654         }
1655     }
1656 }
1657
1658 # Overload these in driver class:
1659
1660 # Returns an arrayref of column names
1661 sub _table_columns { croak "ABSTRACT METHOD" }
1662
1663 # Returns arrayref of pk col names
1664 sub _table_pk_info { croak "ABSTRACT METHOD" }
1665
1666 # Returns an arrayref of uniqs [ [ foo => [ col1, col2 ] ], [ bar => [ ... ] ] ]
1667 sub _table_uniq_info { croak "ABSTRACT METHOD" }
1668
1669 # Returns an arrayref of foreign key constraints, each
1670 #   being a hashref with 3 keys:
1671 #   local_columns (arrayref), remote_columns (arrayref), remote_table
1672 sub _table_fk_info { croak "ABSTRACT METHOD" }
1673
1674 # Returns an array of lower case table names
1675 sub _tables_list { croak "ABSTRACT METHOD" }
1676
1677 # Execute a constructive DBIC class method, with debug/dump_to_dir hooks.
1678 sub _dbic_stmt {
1679     my $self   = shift;
1680     my $class  = shift;
1681     my $method = shift;
1682
1683     # generate the pod for this statement, storing it with $self->_pod
1684     $self->_make_pod( $class, $method, @_ ) if $self->generate_pod;
1685
1686     my $args = dump(@_);
1687     $args = '(' . $args . ')' if @_ < 2;
1688     my $stmt = $method . $args . q{;};
1689
1690     warn qq|$class\->$stmt\n| if $self->debug;
1691     $self->_raw_stmt($class, '__PACKAGE__->' . $stmt);
1692     return;
1693 }
1694
1695 # generates the accompanying pod for a DBIC class method statement,
1696 # storing it with $self->_pod
1697 sub _make_pod {
1698     my $self   = shift;
1699     my $class  = shift;
1700     my $method = shift;
1701
1702     if ( $method eq 'table' ) {
1703         my ($table) = @_;
1704         my $pcm = $self->pod_comment_mode;
1705         my ($comment, $comment_overflows, $comment_in_name, $comment_in_desc);
1706         if ( $self->can('_table_comment') ) {
1707             $comment = $self->_table_comment($table);
1708             $comment_overflows = ($comment and length $comment > $self->pod_comment_spillover_length);
1709             $comment_in_name   = ($pcm eq 'name' or ($pcm eq 'auto' and !$comment_overflows));
1710             $comment_in_desc   = ($pcm eq 'description' or ($pcm eq 'auto' and $comment_overflows));
1711         }
1712         $self->_pod( $class, "=head1 NAME" );
1713         my $table_descr = $class;
1714         $table_descr .= " - " . $comment if $comment and $comment_in_name;
1715         $self->{_class2table}{ $class } = $table;
1716         $self->_pod( $class, $table_descr );
1717         if ($comment and $comment_in_desc) {
1718             $self->_pod( $class, "=head1 DESCRIPTION" );
1719             $self->_pod( $class, $comment );
1720         }
1721         $self->_pod_cut( $class );
1722     } elsif ( $method eq 'add_columns' ) {
1723         $self->_pod( $class, "=head1 ACCESSORS" );
1724         my $col_counter = 0;
1725         my @cols = @_;
1726         while( my ($name,$attrs) = splice @cols,0,2 ) {
1727             $col_counter++;
1728             $self->_pod( $class, '=head2 ' . $name  );
1729             $self->_pod( $class,
1730                          join "\n", map {
1731                              my $s = $attrs->{$_};
1732                              $s = !defined $s         ? 'undef'          :
1733                                   length($s) == 0     ? '(empty string)' :
1734                                   ref($s) eq 'SCALAR' ? $$s :
1735                                   ref($s)             ? do {
1736                                                         my $dd = Dumper;
1737                                                         $dd->Indent(0);
1738                                                         $dd->Values([$s]);
1739                                                         $dd->Dump;
1740                                                       } :
1741                                   looks_like_number($s) ? $s :
1742                                                         qq{'$s'}
1743                                   ;
1744
1745                              "  $_: $s"
1746                          } sort keys %$attrs,
1747                        );
1748
1749             if( $self->can('_column_comment')
1750                 and my $comment = $self->_column_comment( $self->{_class2table}{$class}, $col_counter)
1751               ) {
1752                 $self->_pod( $class, $comment );
1753             }
1754         }
1755         $self->_pod_cut( $class );
1756     } elsif ( $method =~ /^(belongs_to|has_many|might_have)$/ ) {
1757         $self->_pod( $class, "=head1 RELATIONS" ) unless $self->{_relations_started} { $class } ;
1758         my ( $accessor, $rel_class ) = @_;
1759         $self->_pod( $class, "=head2 $accessor" );
1760         $self->_pod( $class, 'Type: ' . $method );
1761         $self->_pod( $class, "Related object: L<$rel_class>" );
1762         $self->_pod_cut( $class );
1763         $self->{_relations_started} { $class } = 1;
1764     }
1765 }
1766
1767 # Stores a POD documentation
1768 sub _pod {
1769     my ($self, $class, $stmt) = @_;
1770     $self->_raw_stmt( $class, "\n" . $stmt  );
1771 }
1772
1773 sub _pod_cut {
1774     my ($self, $class ) = @_;
1775     $self->_raw_stmt( $class, "\n=cut\n" );
1776 }
1777
1778 # Store a raw source line for a class (for dumping purposes)
1779 sub _raw_stmt {
1780     my ($self, $class, $stmt) = @_;
1781     push(@{$self->{_dump_storage}->{$class}}, $stmt);
1782 }
1783
1784 # Like above, but separately for the externally loaded stuff
1785 sub _ext_stmt {
1786     my ($self, $class, $stmt) = @_;
1787     push(@{$self->{_ext_storage}->{$class}}, $stmt);
1788 }
1789
1790 sub _quote_table_name {
1791     my ($self, $table) = @_;
1792
1793     my $qt = $self->schema->storage->sql_maker->quote_char;
1794
1795     return $table unless $qt;
1796
1797     if (ref $qt) {
1798         return $qt->[0] . $table . $qt->[1];
1799     }
1800
1801     return $qt . $table . $qt;
1802 }
1803
1804 sub _custom_column_info {
1805     my ( $self, $table_name, $column_name, $column_info ) = @_;
1806
1807     if (my $code = $self->custom_column_info) {
1808         return $code->($table_name, $column_name, $column_info) || {};
1809     }
1810     return {};
1811 }
1812
1813 sub _datetime_column_info {
1814     my ( $self, $table_name, $column_name, $column_info ) = @_;
1815     my $result = {};
1816     my $type = $column_info->{data_type} || '';
1817     if ((grep $_, @{ $column_info }{map "inflate_$_", qw/date datetime timestamp/})
1818             or ($type =~ /date|timestamp/i)) {
1819         $result->{timezone} = $self->datetime_timezone if $self->datetime_timezone;
1820         $result->{locale}   = $self->datetime_locale   if $self->datetime_locale;
1821     }
1822     return $result;
1823 }
1824
1825 sub _lc {
1826     my ($self, $name) = @_;
1827
1828     return $self->preserve_case ? $name : lc($name);
1829 }
1830
1831 sub _uc {
1832     my ($self, $name) = @_;
1833
1834     return $self->preserve_case ? $name : uc($name);
1835 }
1836
1837 sub _unregister_source_for_table {
1838     my ($self, $table) = @_;
1839
1840     eval {
1841         local $@;
1842         my $schema = $self->schema;
1843         # in older DBIC it's a private method
1844         my $unregister = $schema->can('unregister_source') || $schema->can('_unregister_source');
1845         $schema->$unregister($self->_table2moniker($table));
1846         delete $self->monikers->{$table};
1847         delete $self->classes->{$table};
1848         delete $self->_upgrading_classes->{$table};
1849         delete $self->{_tables}{$table};
1850     };
1851 }
1852
1853 # remove the dump dir from @INC on destruction
1854 sub DESTROY {
1855     my $self = shift;
1856
1857     @INC = grep $_ ne $self->dump_directory, @INC;
1858 }
1859
1860 =head2 monikers
1861
1862 Returns a hashref of loaded table to moniker mappings.  There will
1863 be two entries for each table, the original name and the "normalized"
1864 name, in the case that the two are different (such as databases
1865 that like uppercase table names, or preserve your original mixed-case
1866 definitions, or what-have-you).
1867
1868 =head2 classes
1869
1870 Returns a hashref of table to class mappings.  In some cases it will
1871 contain multiple entries per table for the original and normalized table
1872 names, as above in L</monikers>.
1873
1874 =head1 SEE ALSO
1875
1876 L<DBIx::Class::Schema::Loader>
1877
1878 =head1 AUTHOR
1879
1880 See L<DBIx::Class::Schema::Loader/AUTHOR> and L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
1881
1882 =head1 LICENSE
1883
1884 This library is free software; you can redistribute it and/or modify it under
1885 the same terms as Perl itself.
1886
1887 =cut
1888
1889 1;
1890 # vim:et sts=4 sw=4 tw=0: