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