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