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