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