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