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