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