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