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