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