Suppress 2nd warning below which makes the test fail.
[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::Fast Class::C3::Componentised/;
6 use Class::C3;
7 use Carp::Clan qw/^DBIx::Class/;
8 use DBIx::Class::Schema::Loader::RelBuilder;
9 use Data::Dump qw/ dump /;
10 use POSIX qw//;
11 use File::Spec qw//;
12 use Cwd qw//;
13 use Digest::MD5 qw//;
14 use Lingua::EN::Inflect::Number qw//;
15 use File::Temp qw//;
16 use Class::Unload;
17 require DBIx::Class;
18
19 our $VERSION = '0.04999_12';
20
21 __PACKAGE__->mk_ro_accessors(qw/
22                                 schema
23                                 schema_class
24
25                                 exclude
26                                 constraint
27                                 additional_classes
28                                 additional_base_classes
29                                 left_base_classes
30                                 components
31                                 resultset_components
32                                 skip_relationships
33                                 moniker_map
34                                 inflect_singular
35                                 inflect_plural
36                                 debug
37                                 dump_directory
38                                 dump_overwrite
39                                 really_erase_my_files
40                                 use_namespaces
41                                 result_namespace
42                                 resultset_namespace
43                                 default_resultset_class
44                                 schema_base_class
45                                 result_base_class
46
47                                 db_schema
48                                 _tables
49                                 classes
50                                 _upgrading_classes
51                                 monikers
52                                 dynamic
53                                 naming
54                              /);
55
56 __PACKAGE__->mk_accessors(qw/
57                                 version_to_dump
58                                 schema_version_to_dump
59                                 _upgrading_from
60 /);
61
62 =head1 NAME
63
64 DBIx::Class::Schema::Loader::Base - Base DBIx::Class::Schema::Loader Implementation.
65
66 =head1 SYNOPSIS
67
68 See L<DBIx::Class::Schema::Loader>
69
70 =head1 DESCRIPTION
71
72 This is the base class for the storage-specific C<DBIx::Class::Schema::*>
73 classes, and implements the common functionality between them.
74
75 =head1 CONSTRUCTOR OPTIONS
76
77 These constructor options are the base options for
78 L<DBIx::Class::Schema::Loader/loader_options>.  Available constructor options are:
79
80 =head2 skip_relationships
81
82 Skip setting up relationships.  The default is to attempt the loading
83 of relationships.
84
85 =head2 naming
86
87 Static schemas (ones dumped to disk) will, by default, use the new-style 0.05XXX
88 relationship names and singularized Results, unless you're overwriting an
89 existing dump made by a 0.04XXX version of L<DBIx::Class::Schema::Loader>, in
90 which case the backward compatible RelBuilder will be activated, and
91 singularization will be turned off.
92
93 Specifying
94
95     naming => 'v5'
96
97 will disable the backward-compatible RelBuilder and use
98 the new-style relationship names along with singularized Results, even when
99 overwriting a dump made with an earlier version.
100
101 The option also takes a hashref:
102
103     naming => { relationships => 'v5', monikers => 'v4' }
104
105 The keys are:
106
107 =over 4
108
109 =item relationships
110
111 How to name relationship accessors.
112
113 =item monikers
114
115 How to name Result classes.
116
117 =back
118
119 The values can be:
120
121 =over 4
122
123 =item current
124
125 Latest default style, whatever that happens to be.
126
127 =item v5
128
129 Version 0.05XXX style.
130
131 =item v4
132
133 Version 0.04XXX style.
134
135 =back
136
137 Dynamic schemas will always default to the 0.04XXX relationship names and won't
138 singularize Results for backward compatibility, to activate the new RelBuilder
139 and singularization put this in your C<Schema.pm> file:
140
141     __PACKAGE__->naming('current');
142
143 Or if you prefer to use 0.05XXX features but insure that nothing breaks in the
144 next major version upgrade:
145
146     __PACKAGE__->naming('v5');
147
148 =head2 debug
149
150 If set to true, each constructive L<DBIx::Class> statement the loader
151 decides to execute will be C<warn>-ed before execution.
152
153 =head2 db_schema
154
155 Set the name of the schema to load (schema in the sense that your database
156 vendor means it).  Does not currently support loading more than one schema
157 name.
158
159 =head2 constraint
160
161 Only load tables matching regex.  Best specified as a qr// regex.
162
163 =head2 exclude
164
165 Exclude tables matching regex.  Best specified as a qr// regex.
166
167 =head2 moniker_map
168
169 Overrides the default table name to moniker translation.  Can be either
170 a hashref of table keys and moniker values, or a coderef for a translator
171 function taking a single scalar table name argument and returning
172 a scalar moniker.  If the hash entry does not exist, or the function
173 returns a false value, the code falls back to default behavior
174 for that table name.
175
176 The default behavior is to singularize the table name, and: C<join '', map
177 ucfirst, split /[\W_]+/, lc $table>, which is to say: lowercase everything,
178 split up the table name into chunks anywhere a non-alpha-numeric character
179 occurs, change the case of first letter of each chunk to upper case, and put
180 the chunks back together.  Examples:
181
182     Table Name  | Moniker Name
183     ---------------------------
184     luser       | Luser
185     luser_group | LuserGroup
186     luser-opts  | LuserOpts
187
188 =head2 inflect_plural
189
190 Just like L</moniker_map> above (can be hash/code-ref, falls back to default
191 if hash key does not exist or coderef returns false), but acts as a map
192 for pluralizing relationship names.  The default behavior is to utilize
193 L<Lingua::EN::Inflect::Number/to_PL>.
194
195 =head2 inflect_singular
196
197 As L</inflect_plural> above, but for singularizing relationship names.
198 Default behavior is to utilize L<Lingua::EN::Inflect::Number/to_S>.
199
200 =head2 schema_base_class
201
202 Base class for your schema classes. Defaults to 'DBIx::Class::Schema'.
203
204 =head2 result_base_class
205
206 Base class for your table classes (aka result classes). Defaults to
207 'DBIx::Class::Core'.
208
209 =head2 additional_base_classes
210
211 List of additional base classes all of your table classes will use.
212
213 =head2 left_base_classes
214
215 List of additional base classes all of your table classes will use
216 that need to be leftmost.
217
218 =head2 additional_classes
219
220 List of additional classes which all of your table classes will use.
221
222 =head2 components
223
224 List of additional components to be loaded into all of your table
225 classes.  A good example would be C<ResultSetManager>.
226
227 =head2 resultset_components
228
229 List of additional ResultSet components to be loaded into your table
230 classes.  A good example would be C<AlwaysRS>.  Component
231 C<ResultSetManager> will be automatically added to the above
232 C<components> list if this option is set.
233
234 =head2 use_namespaces
235
236 Generate result class names suitable for
237 L<DBIx::Class::Schema/load_namespaces> and call that instead of
238 L<DBIx::Class::Schema/load_classes>. When using this option you can also
239 specify any of the options for C<load_namespaces> (i.e. C<result_namespace>,
240 C<resultset_namespace>, C<default_resultset_class>), and they will be added
241 to the call (and the generated result class names adjusted appropriately).
242
243 =head2 dump_directory
244
245 This option is designed to be a tool to help you transition from this
246 loader to a manually-defined schema when you decide it's time to do so.
247
248 The value of this option is a perl libdir pathname.  Within
249 that directory this module will create a baseline manual
250 L<DBIx::Class::Schema> module set, based on what it creates at runtime
251 in memory.
252
253 The created schema class will have the same classname as the one on
254 which you are setting this option (and the ResultSource classes will be
255 based on this name as well).
256
257 Normally you wouldn't hard-code this setting in your schema class, as it
258 is meant for one-time manual usage.
259
260 See L<DBIx::Class::Schema::Loader/dump_to_dir> for examples of the
261 recommended way to access this functionality.
262
263 =head2 dump_overwrite
264
265 Deprecated.  See L</really_erase_my_files> below, which does *not* mean
266 the same thing as the old C<dump_overwrite> setting from previous releases.
267
268 =head2 really_erase_my_files
269
270 Default false.  If true, Loader will unconditionally delete any existing
271 files before creating the new ones from scratch when dumping a schema to disk.
272
273 The default behavior is instead to only replace the top portion of the
274 file, up to and including the final stanza which contains
275 C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!>
276 leaving any customizations you placed after that as they were.
277
278 When C<really_erase_my_files> is not set, if the output file already exists,
279 but the aforementioned final stanza is not found, or the checksum
280 contained there does not match the generated contents, Loader will
281 croak and not touch the file.
282
283 You should really be using version control on your schema classes (and all
284 of the rest of your code for that matter).  Don't blame me if a bug in this
285 code wipes something out when it shouldn't have, you've been warned.
286
287 =head1 METHODS
288
289 None of these methods are intended for direct invocation by regular
290 users of L<DBIx::Class::Schema::Loader>.  Anything you can find here
291 can also be found via standard L<DBIx::Class::Schema> methods somehow.
292
293 =cut
294
295 use constant CURRENT_V => 'v5';
296
297 # ensure that a peice of object data is a valid arrayref, creating
298 # an empty one or encapsulating whatever's there.
299 sub _ensure_arrayref {
300     my $self = shift;
301
302     foreach (@_) {
303         $self->{$_} ||= [];
304         $self->{$_} = [ $self->{$_} ]
305             unless ref $self->{$_} eq 'ARRAY';
306     }
307 }
308
309 =head2 new
310
311 Constructor for L<DBIx::Class::Schema::Loader::Base>, used internally
312 by L<DBIx::Class::Schema::Loader>.
313
314 =cut
315
316 sub new {
317     my ( $class, %args ) = @_;
318
319     my $self = { %args };
320
321     bless $self => $class;
322
323     $self->_ensure_arrayref(qw/additional_classes
324                                additional_base_classes
325                                left_base_classes
326                                components
327                                resultset_components
328                               /);
329
330     push(@{$self->{components}}, 'ResultSetManager')
331         if @{$self->{resultset_components}};
332
333     $self->{monikers} = {};
334     $self->{classes} = {};
335     $self->{_upgrading_classes} = {};
336
337     $self->{schema_class} ||= ( ref $self->{schema} || $self->{schema} );
338     $self->{schema} ||= $self->{schema_class};
339
340     croak "dump_overwrite is deprecated.  Please read the"
341         . " DBIx::Class::Schema::Loader::Base documentation"
342             if $self->{dump_overwrite};
343
344     $self->{dynamic} = ! $self->{dump_directory};
345     $self->{temp_directory} ||= File::Temp::tempdir( 'dbicXXXX',
346                                                      TMPDIR  => 1,
347                                                      CLEANUP => 1,
348                                                    );
349
350     $self->{dump_directory} ||= $self->{temp_directory};
351
352     $self->version_to_dump($DBIx::Class::Schema::Loader::VERSION);
353     $self->schema_version_to_dump($DBIx::Class::Schema::Loader::VERSION);
354
355     if ((not ref $self->naming) && defined $self->naming) {
356         my $naming_ver = $self->naming;
357         $self->{naming} = {
358             relationships => $naming_ver,
359             monikers => $naming_ver,
360         };
361     }
362
363     if ($self->naming) {
364         for (values %{ $self->naming }) {
365             $_ = CURRENT_V if $_ eq 'current';
366         }
367     }
368     $self->{naming} ||= {};
369
370     $self->_check_back_compat;
371
372     $self;
373 }
374
375 sub _check_back_compat {
376     my ($self) = @_;
377
378 # dynamic schemas will always be in 0.04006 mode, unless overridden
379     if ($self->dynamic) {
380 # just in case, though no one is likely to dump a dynamic schema
381         $self->schema_version_to_dump('0.04006');
382
383         if (not %{ $self->naming }) {
384             warn <<EOF unless $ENV{SCHEMA_LOADER_BACKCOMPAT};
385
386 Dynamic schema detected, will run in 0.04006 mode.
387
388 Set the 'naming' attribute or the SCHEMA_LOADER_BACKCOMPAT environment variable
389 to disable this warning.
390
391 See perldoc DBIx::Class::Schema::Loader::Manual::UpgradingFromV4 for more
392 details.
393 EOF
394         }
395         else {
396             $self->_upgrading_from('v4');
397         }
398
399         $self->naming->{relationships} ||= 'v4';
400         $self->naming->{monikers}      ||= 'v4';
401
402         return;
403     }
404
405 # otherwise check if we need backcompat mode for a static schema
406     my $filename = $self->_get_dump_filename($self->schema_class);
407     return unless -e $filename;
408
409     open(my $fh, '<', $filename)
410         or croak "Cannot open '$filename' for reading: $!";
411
412     while (<$fh>) {
413         if (/^# Created by DBIx::Class::Schema::Loader v((\d+)\.(\d+))/) {
414             my $real_ver = $1;
415
416             # XXX when we go past .0 this will need fixing
417             my ($v) = $real_ver =~ /([1-9])/;
418             $v = "v$v";
419
420             last if $v eq CURRENT_V;
421
422             if (not %{ $self->naming }) {
423                 warn <<"EOF" unless $ENV{SCHEMA_LOADER_BACKCOMPAT};
424
425 Version $real_ver static schema detected, turning on backcompat mode.
426
427 Set the 'naming' attribute or the SCHEMA_LOADER_BACKCOMPAT environment variable
428 to disable this warning.
429
430 See perldoc DBIx::Class::Schema::Loader::Manual::UpgradingFromV4 for more
431 details.
432 EOF
433             }
434             else {
435                 $self->_upgrading_from($v);
436             }
437
438             $self->naming->{relationships} ||= $v;
439             $self->naming->{monikers}      ||= $v;
440
441             $self->schema_version_to_dump($real_ver);
442
443             last;
444         }
445     }
446     close $fh;
447 }
448
449 sub _find_file_in_inc {
450     my ($self, $file) = @_;
451
452     foreach my $prefix (@INC) {
453         my $fullpath = File::Spec->catfile($prefix, $file);
454         return $fullpath if -f $fullpath
455             and Cwd::abs_path($fullpath) ne
456                (Cwd::abs_path(File::Spec->catfile($self->dump_directory, $file)) || '');
457     }
458
459     return;
460 }
461
462 sub _class_path {
463     my ($self, $class) = @_;
464
465     my $class_path = $class;
466     $class_path =~ s{::}{/}g;
467     $class_path .= '.pm';
468
469     return $class_path;
470 }
471
472 sub _find_class_in_inc {
473     my ($self, $class) = @_;
474
475     return $self->_find_file_in_inc($self->_class_path($class));
476 }
477
478 sub _load_external {
479     my ($self, $class) = @_;
480
481     # so that we don't load our own classes, under any circumstances
482     local *INC = [ grep $_ ne $self->dump_directory, @INC ];
483
484     my $real_inc_path = $self->_find_class_in_inc($class);
485
486     my $old_class = $self->_upgrading_classes->{$class}
487         if $self->_upgrading_from;
488
489     my $old_real_inc_path = $self->_find_class_in_inc($old_class)
490         if $old_class && $old_class ne $class;
491
492     return unless $real_inc_path || $old_real_inc_path;
493
494     if ($real_inc_path) {
495         # If we make it to here, we loaded an external definition
496         warn qq/# Loaded external class definition for '$class'\n/
497             if $self->debug;
498
499         open(my $fh, '<', $real_inc_path)
500             or croak "Failed to open '$real_inc_path' for reading: $!";
501         $self->_ext_stmt($class,
502           qq|# These lines were loaded from '$real_inc_path' found in \@INC.\n|
503          .qq|# They are now part of the custom portion of this file\n|
504          .qq|# for you to hand-edit.  If you do not either delete\n|
505          .qq|# this section or remove that file from \@INC, this section\n|
506          .qq|# will be repeated redundantly when you re-create this\n|
507          .qq|# file again via Loader!\n|
508         );
509         while(<$fh>) {
510             chomp;
511             $self->_ext_stmt($class, $_);
512         }
513         $self->_ext_stmt($class,
514             qq|# End of lines loaded from '$real_inc_path' |
515         );
516         close($fh)
517             or croak "Failed to close $real_inc_path: $!";
518
519         if ($self->dynamic) { # load the class too
520             # kill redefined warnings
521             local $SIG{__WARN__} = sub {
522                 warn @_ unless $_[0] =~ /^Subroutine \S+ redefined/;
523             };
524             do $real_inc_path;
525             die $@ if $@;
526         }
527     }
528
529     if ($old_real_inc_path) {
530         open(my $fh, '<', $old_real_inc_path)
531             or croak "Failed to open '$old_real_inc_path' for reading: $!";
532         $self->_ext_stmt($class, <<"EOF");
533
534 # These lines were loaded from '$old_real_inc_path', based on the Result class
535 # name that would have been created by an 0.04006 version of the Loader. For a
536 # static schema, this happens only once during upgrade.
537 EOF
538         if ($self->dynamic) {
539             warn <<"EOF";
540
541 Detected external content in '$old_real_inc_path', a class name that would have
542 been used by an 0.04006 version of the Loader.
543
544 * PLEASE RENAME THIS CLASS: from '$old_class' to '$class', as that is the
545 new name of the Result.
546 EOF
547             # kill redefined warnings
548             local $SIG{__WARN__} = sub {
549                 warn @_ unless $_[0] =~ /^Subroutine \S+ redefined/;
550             };
551             my $code = do {
552                 local ($/, @ARGV) = (undef, $old_real_inc_path); <>
553             };
554             $code =~ s/$old_class/$class/g;
555             eval $code;
556             die $@ if $@;
557         }
558
559         while(<$fh>) {
560             chomp;
561             $self->_ext_stmt($class, $_);
562         }
563         $self->_ext_stmt($class,
564             qq|# End of lines loaded from '$old_real_inc_path' |
565         );
566
567         close($fh)
568             or croak "Failed to close $old_real_inc_path: $!";
569     }
570 }
571
572 =head2 load
573
574 Does the actual schema-construction work.
575
576 =cut
577
578 sub load {
579     my $self = shift;
580
581     $self->_load_tables($self->_tables_list);
582 }
583
584 =head2 rescan
585
586 Arguments: schema
587
588 Rescan the database for newly added tables.  Does
589 not process drops or changes.  Returns a list of
590 the newly added table monikers.
591
592 The schema argument should be the schema class
593 or object to be affected.  It should probably
594 be derived from the original schema_class used
595 during L</load>.
596
597 =cut
598
599 sub rescan {
600     my ($self, $schema) = @_;
601
602     $self->{schema} = $schema;
603     $self->_relbuilder->{schema} = $schema;
604
605     my @created;
606     my @current = $self->_tables_list;
607     foreach my $table ($self->_tables_list) {
608         if(!exists $self->{_tables}->{$table}) {
609             push(@created, $table);
610         }
611     }
612
613     my $loaded = $self->_load_tables(@created);
614
615     return map { $self->monikers->{$_} } @$loaded;
616 }
617
618 sub _relbuilder {
619     no warnings 'uninitialized';
620     my ($self) = @_;
621
622     return if $self->{skip_relationships};
623
624     if ($self->naming->{relationships} eq 'v4') {
625         require DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_040;
626         return $self->{relbuilder} ||=
627             DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_040->new(
628                 $self->schema, $self->inflect_plural, $self->inflect_singular
629             );
630     }
631
632     $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder->new(
633         $self->schema, $self->inflect_plural, $self->inflect_singular
634     );
635 }
636
637 sub _load_tables {
638     my ($self, @tables) = @_;
639
640     # First, use _tables_list with constraint and exclude
641     #  to get a list of tables to operate on
642
643     my $constraint   = $self->constraint;
644     my $exclude      = $self->exclude;
645
646     @tables = grep { /$constraint/ } @tables if $constraint;
647     @tables = grep { ! /$exclude/ } @tables if $exclude;
648
649     # Save the new tables to the tables list
650     foreach (@tables) {
651         $self->{_tables}->{$_} = 1;
652     }
653
654     $self->_make_src_class($_) for @tables;
655     $self->_setup_src_meta($_) for @tables;
656
657     if(!$self->skip_relationships) {
658         # The relationship loader needs a working schema
659         $self->{quiet} = 1;
660         local $self->{dump_directory} = $self->{temp_directory};
661         $self->_reload_classes(\@tables);
662         $self->_load_relationships($_) for @tables;
663         $self->{quiet} = 0;
664
665         # Remove that temp dir from INC so it doesn't get reloaded
666         @INC = grep $_ ne $self->dump_directory, @INC;
667     }
668
669     $self->_load_external($_)
670         for map { $self->classes->{$_} } @tables;
671
672     # Reload without unloading first to preserve any symbols from external
673     # packages.
674     $self->_reload_classes(\@tables, 0);
675
676     # Drop temporary cache
677     delete $self->{_cache};
678
679     return \@tables;
680 }
681
682 sub _reload_classes {
683     my ($self, $tables, $unload) = @_;
684
685     my @tables = @$tables;
686     $unload = 1 unless defined $unload;
687
688     # so that we don't repeat custom sections
689     @INC = grep $_ ne $self->dump_directory, @INC;
690
691     $self->_dump_to_dir(map { $self->classes->{$_} } @tables);
692
693     unshift @INC, $self->dump_directory;
694     
695     my @to_register;
696     my %have_source = map { $_ => $self->schema->source($_) }
697         $self->schema->sources;
698
699     for my $table (@tables) {
700         my $moniker = $self->monikers->{$table};
701         my $class = $self->classes->{$table};
702         
703         {
704             no warnings 'redefine';
705             local *Class::C3::reinitialize = sub {};
706             use warnings;
707
708             Class::Unload->unload($class) if $unload;
709             my ($source, $resultset_class);
710             if (
711                 ($source = $have_source{$moniker})
712                 && ($resultset_class = $source->resultset_class)
713                 && ($resultset_class ne 'DBIx::Class::ResultSet')
714             ) {
715                 my $has_file = Class::Inspector->loaded_filename($resultset_class);
716                 Class::Unload->unload($resultset_class) if $unload;
717                 $self->_reload_class($resultset_class) if $has_file;
718             }
719             $self->_reload_class($class);
720         }
721         push @to_register, [$moniker, $class];
722     }
723
724     Class::C3->reinitialize;
725     for (@to_register) {
726         $self->schema->register_class(@$_);
727     }
728 }
729
730 # We use this instead of ensure_class_loaded when there are package symbols we
731 # want to preserve.
732 sub _reload_class {
733     my ($self, $class) = @_;
734
735     my $class_path = $self->_class_path($class);
736     delete $INC{ $class_path };
737
738 # kill redefined warnings
739     local $SIG{__WARN__} = sub {
740         warn @_ unless $_[0] =~ /^Subroutine \S+ redefined/;
741     };
742
743     eval "require $class;";
744 }
745
746 sub _get_dump_filename {
747     my ($self, $class) = (@_);
748
749     $class =~ s{::}{/}g;
750     return $self->dump_directory . q{/} . $class . q{.pm};
751 }
752
753 sub _ensure_dump_subdirs {
754     my ($self, $class) = (@_);
755
756     my @name_parts = split(/::/, $class);
757     pop @name_parts; # we don't care about the very last element,
758                      # which is a filename
759
760     my $dir = $self->dump_directory;
761     while (1) {
762         if(!-d $dir) {
763             mkdir($dir) or croak "mkdir('$dir') failed: $!";
764         }
765         last if !@name_parts;
766         $dir = File::Spec->catdir($dir, shift @name_parts);
767     }
768 }
769
770 sub _dump_to_dir {
771     my ($self, @classes) = @_;
772
773     my $schema_class = $self->schema_class;
774     my $schema_base_class = $self->schema_base_class || 'DBIx::Class::Schema';
775
776     my $target_dir = $self->dump_directory;
777     warn "Dumping manual schema for $schema_class to directory $target_dir ...\n"
778         unless $self->{dynamic} or $self->{quiet};
779
780     my $schema_text =
781           qq|package $schema_class;\n\n|
782         . qq|# Created by DBIx::Class::Schema::Loader\n|
783         . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
784         . qq|use strict;\nuse warnings;\n\n|
785         . qq|use base '$schema_base_class';\n\n|;
786
787     if ($self->use_namespaces) {
788         $schema_text .= qq|__PACKAGE__->load_namespaces|;
789         my $namespace_options;
790         for my $attr (qw(result_namespace
791                          resultset_namespace
792                          default_resultset_class)) {
793             if ($self->$attr) {
794                 $namespace_options .= qq|    $attr => '| . $self->$attr . qq|',\n|
795             }
796         }
797         $schema_text .= qq|(\n$namespace_options)| if $namespace_options;
798         $schema_text .= qq|;\n|;
799     }
800     else {
801         $schema_text .= qq|__PACKAGE__->load_classes;\n|;
802     }
803
804     {
805         local $self->{version_to_dump} = $self->schema_version_to_dump;
806         $self->_write_classfile($schema_class, $schema_text);
807     }
808
809     my $result_base_class = $self->result_base_class || 'DBIx::Class::Core';
810
811     foreach my $src_class (@classes) {
812         my $src_text = 
813               qq|package $src_class;\n\n|
814             . qq|# Created by DBIx::Class::Schema::Loader\n|
815             . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
816             . qq|use strict;\nuse warnings;\n\n|
817             . qq|use base '$result_base_class';\n\n|;
818
819         $self->_write_classfile($src_class, $src_text);
820     }
821
822     warn "Schema dump completed.\n" unless $self->{dynamic} or $self->{quiet};
823
824 }
825
826 sub _sig_comment {
827     my ($self, $version, $ts) = @_;
828     return qq|\n\n# Created by DBIx::Class::Schema::Loader|
829          . qq| v| . $version
830          . q| @ | . $ts 
831          . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
832 }
833
834 sub _write_classfile {
835     my ($self, $class, $text) = @_;
836
837     my $filename = $self->_get_dump_filename($class);
838     $self->_ensure_dump_subdirs($class);
839
840     if (-f $filename && $self->really_erase_my_files) {
841         warn "Deleting existing file '$filename' due to "
842             . "'really_erase_my_files' setting\n" unless $self->{quiet};
843         unlink($filename);
844     }    
845
846     my ($custom_content, $old_md5, $old_ver, $old_ts) = $self->_get_custom_content($class, $filename);
847
848     if ($self->_upgrading_from) {
849         my $old_class = $self->_upgrading_classes->{$class};
850
851         if ($old_class && ($old_class ne $class)) {
852             my $old_filename = $self->_get_dump_filename($old_class);
853
854             my ($old_custom_content) = $self->_get_custom_content(
855                 $old_class, $old_filename, 0 # do not add default comment
856             );
857
858             $old_custom_content =~ s/\n\n# You can replace.*\n1;\n//;
859
860             if ($old_custom_content) {
861                 $custom_content =
862                     "\n" . $old_custom_content . "\n" . $custom_content;
863             }
864
865             unlink $old_filename;
866         }
867     }
868
869     $text .= qq|$_\n|
870         for @{$self->{_dump_storage}->{$class} || []};
871
872     # Check and see if the dump is infact differnt
873
874     my $compare_to;
875     if ($old_md5) {
876       $compare_to = $text . $self->_sig_comment($old_ver, $old_ts);
877       
878
879       if (Digest::MD5::md5_base64($compare_to) eq $old_md5) {
880         return;
881       }
882     }
883
884     $text .= $self->_sig_comment(
885       $self->version_to_dump,
886       POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
887     );
888
889     open(my $fh, '>', $filename)
890         or croak "Cannot open '$filename' for writing: $!";
891
892     # Write the top half and its MD5 sum
893     print $fh $text . Digest::MD5::md5_base64($text) . "\n";
894
895     # Write out anything loaded via external partial class file in @INC
896     print $fh qq|$_\n|
897         for @{$self->{_ext_storage}->{$class} || []};
898
899     # Write out any custom content the user has added
900     print $fh $custom_content;
901
902     close($fh)
903         or croak "Error closing '$filename': $!";
904 }
905
906 sub _default_custom_content {
907     return qq|\n\n# You can replace this text with custom|
908          . qq| content, and it will be preserved on regeneration|
909          . qq|\n1;\n|;
910 }
911
912 sub _get_custom_content {
913     my ($self, $class, $filename, $add_default) = @_;
914
915     $add_default = 1 unless defined $add_default;
916
917     return ($self->_default_custom_content) if ! -f $filename;
918
919     open(my $fh, '<', $filename)
920         or croak "Cannot open '$filename' for reading: $!";
921
922     my $mark_re = 
923         qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
924
925     my $buffer = '';
926     my ($md5, $ts, $ver);
927     while(<$fh>) {
928         if(!$md5 && /$mark_re/) {
929             $md5 = $2;
930             my $line = $1;
931
932             # Pull out the previous version and timestamp
933             ($ver, $ts) = $buffer =~ m/# Created by DBIx::Class::Schema::Loader v(.*?) @ (.*?)$/s;
934
935             $buffer .= $line;
936             croak "Checksum mismatch in '$filename'"
937                 if Digest::MD5::md5_base64($buffer) ne $md5;
938
939             $buffer = '';
940         }
941         else {
942             $buffer .= $_;
943         }
944     }
945
946     croak "Cannot not overwrite '$filename' without 'really_erase_my_files',"
947         . " it does not appear to have been generated by Loader"
948             if !$md5;
949
950     # Default custom content:
951     $buffer ||= $self->_default_custom_content if $add_default;
952
953     return ($buffer, $md5, $ver, $ts);
954 }
955
956 sub _use {
957     my $self = shift;
958     my $target = shift;
959
960     foreach (@_) {
961         warn "$target: use $_;" if $self->debug;
962         $self->_raw_stmt($target, "use $_;");
963     }
964 }
965
966 sub _inject {
967     my $self = shift;
968     my $target = shift;
969     my $schema_class = $self->schema_class;
970
971     my $blist = join(q{ }, @_);
972     warn "$target: use base qw/ $blist /;" if $self->debug && @_;
973     $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
974 }
975
976 # Create class with applicable bases, setup monikers, etc
977 sub _make_src_class {
978     my ($self, $table) = @_;
979
980     my $schema       = $self->schema;
981     my $schema_class = $self->schema_class;
982
983     my $table_moniker = $self->_table2moniker($table);
984     my @result_namespace = ($schema_class);
985     if ($self->use_namespaces) {
986         my $result_namespace = $self->result_namespace || 'Result';
987         if ($result_namespace =~ /^\+(.*)/) {
988             # Fully qualified namespace
989             @result_namespace =  ($1)
990         }
991         else {
992             # Relative namespace
993             push @result_namespace, $result_namespace;
994         }
995     }
996     my $table_class = join(q{::}, @result_namespace, $table_moniker);
997
998     if (my $upgrading_v = $self->_upgrading_from) {
999         local $self->naming->{monikers} = $upgrading_v;
1000
1001         my $old_class = join(q{::}, @result_namespace,
1002             $self->_table2moniker($table));
1003
1004         $self->_upgrading_classes->{$table_class} = $old_class;
1005     }
1006
1007     my $table_normalized = lc $table;
1008     $self->classes->{$table} = $table_class;
1009     $self->classes->{$table_normalized} = $table_class;
1010     $self->monikers->{$table} = $table_moniker;
1011     $self->monikers->{$table_normalized} = $table_moniker;
1012
1013     $self->_use   ($table_class, @{$self->additional_classes});
1014     $self->_inject($table_class, @{$self->left_base_classes});
1015
1016     if (my @components = @{ $self->components }) {
1017         $self->_dbic_stmt($table_class, 'load_components', @components);
1018     }
1019
1020     $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
1021         if @{$self->resultset_components};
1022     $self->_inject($table_class, @{$self->additional_base_classes});
1023 }
1024
1025 # Set up metadata (cols, pks, etc)
1026 sub _setup_src_meta {
1027     my ($self, $table) = @_;
1028
1029     my $schema       = $self->schema;
1030     my $schema_class = $self->schema_class;
1031
1032     my $table_class = $self->classes->{$table};
1033     my $table_moniker = $self->monikers->{$table};
1034
1035     my $table_name = $table;
1036     my $name_sep   = $self->schema->storage->sql_maker->name_sep;
1037
1038     if ($name_sep && $table_name =~ /\Q$name_sep\E/) {
1039         $table_name = \ $self->_quote_table_name($table_name);
1040     }
1041
1042     $self->_dbic_stmt($table_class,'table',$table_name);
1043
1044     my $cols = $self->_table_columns($table);
1045     my $col_info;
1046     eval { $col_info = $self->_columns_info_for($table) };
1047     if($@) {
1048         $self->_dbic_stmt($table_class,'add_columns',@$cols);
1049     }
1050     else {
1051         if ($self->_is_case_sensitive) {
1052             for my $col (keys %$col_info) {
1053                 $col_info->{$col}{accessor} = lc $col
1054                     if $col ne lc($col);
1055             }
1056         } else {
1057             $col_info = { map { lc($_), $col_info->{$_} } keys %$col_info };
1058         }
1059
1060         my $fks = $self->_table_fk_info($table);
1061
1062         for my $fkdef (@$fks) {
1063             for my $col (@{ $fkdef->{local_columns} }) {
1064                 $col_info->{$col}{is_foreign_key} = 1;
1065             }
1066         }
1067         $self->_dbic_stmt(
1068             $table_class,
1069             'add_columns',
1070             map { $_, ($col_info->{$_}||{}) } @$cols
1071         );
1072     }
1073
1074     my %uniq_tag; # used to eliminate duplicate uniqs
1075
1076     my $pks = $self->_table_pk_info($table) || [];
1077     @$pks ? $self->_dbic_stmt($table_class,'set_primary_key',@$pks)
1078           : carp("$table has no primary key");
1079     $uniq_tag{ join("\0", @$pks) }++ if @$pks; # pk is a uniq
1080
1081     my $uniqs = $self->_table_uniq_info($table) || [];
1082     for (@$uniqs) {
1083         my ($name, $cols) = @$_;
1084         next if $uniq_tag{ join("\0", @$cols) }++; # skip duplicates
1085         $self->_dbic_stmt($table_class,'add_unique_constraint', $name, $cols);
1086     }
1087
1088 }
1089
1090 =head2 tables
1091
1092 Returns a sorted list of loaded tables, using the original database table
1093 names.
1094
1095 =cut
1096
1097 sub tables {
1098     my $self = shift;
1099
1100     return keys %{$self->_tables};
1101 }
1102
1103 # Make a moniker from a table
1104 sub _default_table2moniker {
1105     no warnings 'uninitialized';
1106     my ($self, $table) = @_;
1107
1108     if ($self->naming->{monikers} eq 'v4') {
1109         return join '', map ucfirst, split /[\W_]+/, lc $table;
1110     }
1111
1112     return join '', map ucfirst, split /[\W_]+/,
1113         Lingua::EN::Inflect::Number::to_S(lc $table);
1114 }
1115
1116 sub _table2moniker {
1117     my ( $self, $table ) = @_;
1118
1119     my $moniker;
1120
1121     if( ref $self->moniker_map eq 'HASH' ) {
1122         $moniker = $self->moniker_map->{$table};
1123     }
1124     elsif( ref $self->moniker_map eq 'CODE' ) {
1125         $moniker = $self->moniker_map->($table);
1126     }
1127
1128     $moniker ||= $self->_default_table2moniker($table);
1129
1130     return $moniker;
1131 }
1132
1133 sub _load_relationships {
1134     my ($self, $table) = @_;
1135
1136     my $tbl_fk_info = $self->_table_fk_info($table);
1137     foreach my $fkdef (@$tbl_fk_info) {
1138         $fkdef->{remote_source} =
1139             $self->monikers->{delete $fkdef->{remote_table}};
1140     }
1141     my $tbl_uniq_info = $self->_table_uniq_info($table);
1142
1143     my $local_moniker = $self->monikers->{$table};
1144     my $rel_stmts = $self->_relbuilder->generate_code($local_moniker, $tbl_fk_info, $tbl_uniq_info);
1145
1146     foreach my $src_class (sort keys %$rel_stmts) {
1147         my $src_stmts = $rel_stmts->{$src_class};
1148         foreach my $stmt (@$src_stmts) {
1149             $self->_dbic_stmt($src_class,$stmt->{method},@{$stmt->{args}});
1150         }
1151     }
1152 }
1153
1154 # Overload these in driver class:
1155
1156 # Returns an arrayref of column names
1157 sub _table_columns { croak "ABSTRACT METHOD" }
1158
1159 # Returns arrayref of pk col names
1160 sub _table_pk_info { croak "ABSTRACT METHOD" }
1161
1162 # Returns an arrayref of uniqs [ [ foo => [ col1, col2 ] ], [ bar => [ ... ] ] ]
1163 sub _table_uniq_info { croak "ABSTRACT METHOD" }
1164
1165 # Returns an arrayref of foreign key constraints, each
1166 #   being a hashref with 3 keys:
1167 #   local_columns (arrayref), remote_columns (arrayref), remote_table
1168 sub _table_fk_info { croak "ABSTRACT METHOD" }
1169
1170 # Returns an array of lower case table names
1171 sub _tables_list { croak "ABSTRACT METHOD" }
1172
1173 # Execute a constructive DBIC class method, with debug/dump_to_dir hooks.
1174 sub _dbic_stmt {
1175     my $self = shift;
1176     my $class = shift;
1177     my $method = shift;
1178     if ( $method eq 'table' ) {
1179         my ($table) = @_;
1180         $self->_pod( $class, "=head1 NAME" );
1181         my $table_descr = $class;
1182         if ( $self->can('_table_comment') ) {
1183             my $comment = $self->_table_comment($table);
1184             $table_descr .= " - " . $comment if $comment;
1185         }
1186         $self->{_class2table}{ $class } = $table;
1187         $self->_pod( $class, $table_descr );
1188         $self->_pod_cut( $class );
1189     } elsif ( $method eq 'add_columns' ) {
1190         $self->_pod( $class, "=head1 ACCESSORS" );
1191         my $i = 0;
1192         foreach ( @_ ) {
1193             $i++;
1194             next unless $i % 2;
1195             $self->_pod( $class, '=head2 ' . $_  );
1196             my $comment;
1197             $comment = $self->_column_comment( $self->{_class2table}{$class}, ($i - 1) / 2 + 1  ) if $self->can('_column_comment');
1198             $self->_pod( $class, $comment ) if $comment;
1199         }
1200         $self->_pod_cut( $class );
1201     } elsif ( $method =~ /^(belongs_to|has_many|might_have)$/ ) {
1202         $self->_pod( $class, "=head1 RELATIONS" ) unless $self->{_relations_started} { $class } ;
1203         my ( $accessor, $rel_class ) = @_;
1204         $self->_pod( $class, "=head2 $accessor" );
1205         $self->_pod( $class, 'Type: ' . $method );
1206         $self->_pod( $class, "Related object: L<$rel_class>" );
1207         $self->_pod_cut( $class );
1208         $self->{_relations_started} { $class } = 1;
1209     }
1210     my $args = dump(@_);
1211     $args = '(' . $args . ')' if @_ < 2;
1212     my $stmt = $method . $args . q{;};
1213
1214     warn qq|$class\->$stmt\n| if $self->debug;
1215     $self->_raw_stmt($class, '__PACKAGE__->' . $stmt);
1216     return;
1217 }
1218
1219 # Stores a POD documentation
1220 sub _pod {
1221     my ($self, $class, $stmt) = @_;
1222     $self->_raw_stmt( $class, "\n" . $stmt  );
1223 }
1224
1225 sub _pod_cut {
1226     my ($self, $class ) = @_;
1227     $self->_raw_stmt( $class, "\n=cut\n" );
1228 }
1229
1230
1231 # Store a raw source line for a class (for dumping purposes)
1232 sub _raw_stmt {
1233     my ($self, $class, $stmt) = @_;
1234     push(@{$self->{_dump_storage}->{$class}}, $stmt);
1235 }
1236
1237 # Like above, but separately for the externally loaded stuff
1238 sub _ext_stmt {
1239     my ($self, $class, $stmt) = @_;
1240     push(@{$self->{_ext_storage}->{$class}}, $stmt);
1241 }
1242
1243 sub _quote_table_name {
1244     my ($self, $table) = @_;
1245
1246     my $qt = $self->schema->storage->sql_maker->quote_char;
1247
1248     return $table unless $qt;
1249
1250     if (ref $qt) {
1251         return $qt->[0] . $table . $qt->[1];
1252     }
1253
1254     return $qt . $table . $qt;
1255 }
1256
1257 sub _is_case_sensitive { 0 }
1258
1259 # remove the dump dir from @INC on destruction
1260 sub DESTROY {
1261     my $self = shift;
1262
1263     @INC = grep $_ ne $self->dump_directory, @INC;
1264 }
1265
1266 =head2 monikers
1267
1268 Returns a hashref of loaded table to moniker mappings.  There will
1269 be two entries for each table, the original name and the "normalized"
1270 name, in the case that the two are different (such as databases
1271 that like uppercase table names, or preserve your original mixed-case
1272 definitions, or what-have-you).
1273
1274 =head2 classes
1275
1276 Returns a hashref of table to class mappings.  In some cases it will
1277 contain multiple entries per table for the original and normalized table
1278 names, as above in L</monikers>.
1279
1280 =head1 SEE ALSO
1281
1282 L<DBIx::Class::Schema::Loader>
1283
1284 =head1 AUTHOR
1285
1286 See L<DBIx::Class::Schema::Loader/AUTHOR> and L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
1287
1288 =head1 LICENSE
1289
1290 This library is free software; you can redistribute it and/or modify it under
1291 the same terms as Perl itself.
1292
1293 =cut
1294
1295 1;