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