load custom content from external un-singularized classes, tested for dynamic schema...
[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
ffc705f3 481 # so that we don't load our own classes, under any circumstances
482 local *INC = [ grep $_ ne $self->dump_directory, @INC ];
483
fb3bb595 484 my $real_inc_path = $self->_find_class_in_inc($class);
f96ef30f 485
ffc705f3 486 my $old_class = $self->_upgrading_classes->{$class}
487 if $self->_upgrading_from;
488
489 my $old_real_inc_path = $self->_find_class_in_inc($old_class)
490 if $old_class && $old_class ne $class;
491
492 return unless $real_inc_path || $old_real_inc_path;
493
494 if ($real_inc_path) {
495 # If we make it to here, we loaded an external definition
496 warn qq/# Loaded external class definition for '$class'\n/
497 if $self->debug;
498
499 open(my $fh, '<', $real_inc_path)
500 or croak "Failed to open '$real_inc_path' for reading: $!";
501 $self->_ext_stmt($class,
502 qq|# These lines were loaded from '$real_inc_path' found in \@INC.\n|
503 .qq|# They are now part of the custom portion of this file\n|
504 .qq|# for you to hand-edit. If you do not either delete\n|
505 .qq|# this section or remove that file from \@INC, this section\n|
506 .qq|# will be repeated redundantly when you re-create this\n|
507 .qq|# file again via Loader!\n|
508 );
509 while(<$fh>) {
510 chomp;
511 $self->_ext_stmt($class, $_);
512 }
513 $self->_ext_stmt($class,
514 qq|# End of lines loaded from '$real_inc_path' |
515 );
516 close($fh)
517 or croak "Failed to close $real_inc_path: $!";
518
519 if ($self->dynamic) { # load the class too
520 # kill redefined warnings
521 local $SIG{__WARN__} = sub {
522 warn @_ unless $_[0] =~ /^Subroutine \S+ redefined/;
523 };
524 do $real_inc_path;
525 die $@ if $@;
526 }
996be9ee 527 }
106a976a 528
ffc705f3 529 if ($old_real_inc_path) {
530 open(my $fh, '<', $old_real_inc_path)
531 or croak "Failed to open '$old_real_inc_path' for reading: $!";
532 $self->_ext_stmt($class, <<"EOF");
533
534# These lines were loaded from '$old_real_inc_path', based on the Result class
535# name that would have been created by an 0.04006 version of the Loader. For a
536# static schema, this happens only once during upgrade.
537EOF
538 if ($self->dynamic) {
539 warn <<"EOF";
540
541Detected external content in '$old_real_inc_path', a class name that would have
542been used by an 0.04006 version of the Loader.
543
544* PLEASE RENAME THIS CLASS: from '$old_class' to '$class', as that is the
545new name of the Result.
546EOF
547 # kill redefined warnings
548 local $SIG{__WARN__} = sub {
549 warn @_ unless $_[0] =~ /^Subroutine \S+ redefined/;
550 };
551 my $code = do {
552 local ($/, @ARGV) = (undef, $old_real_inc_path); <>
553 };
554 $code =~ s/$old_class/$class/g;
555 eval $code;
556 die $@ if $@;
557 }
558
559 while(<$fh>) {
560 chomp;
561 $self->_ext_stmt($class, $_);
562 }
563 $self->_ext_stmt($class,
564 qq|# End of lines loaded from '$old_real_inc_path' |
565 );
566
567 close($fh)
568 or croak "Failed to close $old_real_inc_path: $!";
9e8033c1 569 }
996be9ee 570}
571
572=head2 load
573
574Does the actual schema-construction work.
575
576=cut
577
578sub load {
579 my $self = shift;
580
b97c2c1e 581 $self->_load_tables($self->_tables_list);
582}
583
584=head2 rescan
585
a60b5b8d 586Arguments: schema
587
b97c2c1e 588Rescan the database for newly added tables. Does
a60b5b8d 589not process drops or changes. Returns a list of
590the newly added table monikers.
591
592The schema argument should be the schema class
593or object to be affected. It should probably
594be derived from the original schema_class used
595during L</load>.
b97c2c1e 596
597=cut
598
599sub rescan {
a60b5b8d 600 my ($self, $schema) = @_;
601
602 $self->{schema} = $schema;
7824616e 603 $self->_relbuilder->{schema} = $schema;
b97c2c1e 604
605 my @created;
606 my @current = $self->_tables_list;
607 foreach my $table ($self->_tables_list) {
608 if(!exists $self->{_tables}->{$table}) {
609 push(@created, $table);
610 }
611 }
612
c39e3507 613 my $loaded = $self->_load_tables(@created);
a60b5b8d 614
c39e3507 615 return map { $self->monikers->{$_} } @$loaded;
b97c2c1e 616}
617
7824616e 618sub _relbuilder {
66afce69 619 no warnings 'uninitialized';
7824616e 620 my ($self) = @_;
3fed44ca 621
622 return if $self->{skip_relationships};
623
a8d229ff 624 if ($self->naming->{relationships} eq 'v4') {
625 require DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_040;
626 return $self->{relbuilder} ||=
627 DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_040->new(
628 $self->schema, $self->inflect_plural, $self->inflect_singular
629 );
630 }
631
7824616e 632 $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder->new(
633 $self->schema, $self->inflect_plural, $self->inflect_singular
634 );
635}
636
b97c2c1e 637sub _load_tables {
638 my ($self, @tables) = @_;
639
f96ef30f 640 # First, use _tables_list with constraint and exclude
641 # to get a list of tables to operate on
642
643 my $constraint = $self->constraint;
644 my $exclude = $self->exclude;
f96ef30f 645
b97c2c1e 646 @tables = grep { /$constraint/ } @tables if $constraint;
647 @tables = grep { ! /$exclude/ } @tables if $exclude;
f96ef30f 648
b97c2c1e 649 # Save the new tables to the tables list
a60b5b8d 650 foreach (@tables) {
651 $self->{_tables}->{$_} = 1;
652 }
f96ef30f 653
af31090c 654 $self->_make_src_class($_) for @tables;
f96ef30f 655 $self->_setup_src_meta($_) for @tables;
656
e8ad6491 657 if(!$self->skip_relationships) {
181cc907 658 # The relationship loader needs a working schema
af31090c 659 $self->{quiet} = 1;
79193756 660 local $self->{dump_directory} = $self->{temp_directory};
106a976a 661 $self->_reload_classes(\@tables);
e8ad6491 662 $self->_load_relationships($_) for @tables;
af31090c 663 $self->{quiet} = 0;
79193756 664
665 # Remove that temp dir from INC so it doesn't get reloaded
ffc705f3 666 @INC = grep $_ ne $self->dump_directory, @INC;
e8ad6491 667 }
668
f96ef30f 669 $self->_load_external($_)
75451704 670 for map { $self->classes->{$_} } @tables;
f96ef30f 671
106a976a 672 # Reload without unloading first to preserve any symbols from external
673 # packages.
674 $self->_reload_classes(\@tables, 0);
996be9ee 675
5223f24a 676 # Drop temporary cache
677 delete $self->{_cache};
678
c39e3507 679 return \@tables;
996be9ee 680}
681
af31090c 682sub _reload_classes {
106a976a 683 my ($self, $tables, $unload) = @_;
684
685 my @tables = @$tables;
686 $unload = 1 unless defined $unload;
181cc907 687
4daef04f 688 # so that we don't repeat custom sections
689 @INC = grep $_ ne $self->dump_directory, @INC;
690
181cc907 691 $self->_dump_to_dir(map { $self->classes->{$_} } @tables);
e9b8719e 692
693 unshift @INC, $self->dump_directory;
af31090c 694
706ef173 695 my @to_register;
696 my %have_source = map { $_ => $self->schema->source($_) }
697 $self->schema->sources;
698
181cc907 699 for my $table (@tables) {
700 my $moniker = $self->monikers->{$table};
701 my $class = $self->classes->{$table};
0ae6b65d 702
703 {
704 no warnings 'redefine';
705 local *Class::C3::reinitialize = sub {};
706 use warnings;
707
106a976a 708 Class::Unload->unload($class) if $unload;
706ef173 709 my ($source, $resultset_class);
710 if (
711 ($source = $have_source{$moniker})
712 && ($resultset_class = $source->resultset_class)
713 && ($resultset_class ne 'DBIx::Class::ResultSet')
714 ) {
715 my $has_file = Class::Inspector->loaded_filename($resultset_class);
106a976a 716 Class::Unload->unload($resultset_class) if $unload;
717 $self->_reload_class($resultset_class) if $has_file;
0ae6b65d 718 }
106a976a 719 $self->_reload_class($class);
af31090c 720 }
706ef173 721 push @to_register, [$moniker, $class];
722 }
af31090c 723
706ef173 724 Class::C3->reinitialize;
725 for (@to_register) {
726 $self->schema->register_class(@$_);
af31090c 727 }
728}
729
106a976a 730# We use this instead of ensure_class_loaded when there are package symbols we
731# want to preserve.
732sub _reload_class {
733 my ($self, $class) = @_;
734
735 my $class_path = $self->_class_path($class);
736 delete $INC{ $class_path };
f53dcdf0 737
738# kill redefined warnings
739 local $SIG{__WARN__} = sub {
740 warn @_ unless $_[0] =~ /^Subroutine \S+ redefined/;
741 };
742
106a976a 743 eval "require $class;";
744}
745
996be9ee 746sub _get_dump_filename {
747 my ($self, $class) = (@_);
748
749 $class =~ s{::}{/}g;
750 return $self->dump_directory . q{/} . $class . q{.pm};
751}
752
753sub _ensure_dump_subdirs {
754 my ($self, $class) = (@_);
755
756 my @name_parts = split(/::/, $class);
dd03ee1a 757 pop @name_parts; # we don't care about the very last element,
758 # which is a filename
759
996be9ee 760 my $dir = $self->dump_directory;
7cab3ab7 761 while (1) {
762 if(!-d $dir) {
25328cc4 763 mkdir($dir) or croak "mkdir('$dir') failed: $!";
996be9ee 764 }
7cab3ab7 765 last if !@name_parts;
766 $dir = File::Spec->catdir($dir, shift @name_parts);
996be9ee 767 }
768}
769
770sub _dump_to_dir {
af31090c 771 my ($self, @classes) = @_;
996be9ee 772
fc2b71fd 773 my $schema_class = $self->schema_class;
9c9c2f2b 774 my $schema_base_class = $self->schema_base_class || 'DBIx::Class::Schema';
996be9ee 775
e9b8719e 776 my $target_dir = $self->dump_directory;
af31090c 777 warn "Dumping manual schema for $schema_class to directory $target_dir ...\n"
778 unless $self->{dynamic} or $self->{quiet};
996be9ee 779
7cab3ab7 780 my $schema_text =
781 qq|package $schema_class;\n\n|
b4dcbcc5 782 . qq|# Created by DBIx::Class::Schema::Loader\n|
783 . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
7cab3ab7 784 . qq|use strict;\nuse warnings;\n\n|
9c9c2f2b 785 . qq|use base '$schema_base_class';\n\n|;
f44ecc2f 786
f44ecc2f 787 if ($self->use_namespaces) {
788 $schema_text .= qq|__PACKAGE__->load_namespaces|;
789 my $namespace_options;
790 for my $attr (qw(result_namespace
791 resultset_namespace
792 default_resultset_class)) {
793 if ($self->$attr) {
794 $namespace_options .= qq| $attr => '| . $self->$attr . qq|',\n|
795 }
796 }
797 $schema_text .= qq|(\n$namespace_options)| if $namespace_options;
798 $schema_text .= qq|;\n|;
799 }
800 else {
801 $schema_text .= qq|__PACKAGE__->load_classes;\n|;
f44ecc2f 802 }
996be9ee 803
1c95b304 804 {
805 local $self->{version_to_dump} = $self->schema_version_to_dump;
806 $self->_write_classfile($schema_class, $schema_text);
807 }
996be9ee 808
2229729e 809 my $result_base_class = $self->result_base_class || 'DBIx::Class::Core';
9c9c2f2b 810
af31090c 811 foreach my $src_class (@classes) {
7cab3ab7 812 my $src_text =
813 qq|package $src_class;\n\n|
b4dcbcc5 814 . qq|# Created by DBIx::Class::Schema::Loader\n|
815 . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
7cab3ab7 816 . qq|use strict;\nuse warnings;\n\n|
9c9c2f2b 817 . qq|use base '$result_base_class';\n\n|;
996be9ee 818
7cab3ab7 819 $self->_write_classfile($src_class, $src_text);
02356864 820 }
996be9ee 821
af31090c 822 warn "Schema dump completed.\n" unless $self->{dynamic} or $self->{quiet};
823
7cab3ab7 824}
825
79193756 826sub _sig_comment {
827 my ($self, $version, $ts) = @_;
828 return qq|\n\n# Created by DBIx::Class::Schema::Loader|
829 . qq| v| . $version
830 . q| @ | . $ts
831 . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
832}
833
7cab3ab7 834sub _write_classfile {
835 my ($self, $class, $text) = @_;
836
837 my $filename = $self->_get_dump_filename($class);
838 $self->_ensure_dump_subdirs($class);
839
28b4691d 840 if (-f $filename && $self->really_erase_my_files) {
7cab3ab7 841 warn "Deleting existing file '$filename' due to "
af31090c 842 . "'really_erase_my_files' setting\n" unless $self->{quiet};
7cab3ab7 843 unlink($filename);
844 }
845
79193756 846 my ($custom_content, $old_md5, $old_ver, $old_ts) = $self->_get_custom_content($class, $filename);
17ca645f 847
f53dcdf0 848 if ($self->_upgrading_from) {
849 my $old_class = $self->_upgrading_classes->{$class};
850
851 if ($old_class && ($old_class ne $class)) {
852 my $old_filename = $self->_get_dump_filename($old_class);
853
854 my ($old_custom_content) = $self->_get_custom_content(
ffc705f3 855 $old_class, $old_filename, 0 # do not add default comment
f53dcdf0 856 );
857
ffc705f3 858 $old_custom_content =~ s/\n\n# You can replace.*\n1;\n//;
859
860 if ($old_custom_content) {
861 $custom_content =
862 "\n" . $old_custom_content . "\n" . $custom_content;
863 }
f53dcdf0 864
865 unlink $old_filename;
866 }
867 }
868
7cab3ab7 869 $text .= qq|$_\n|
870 for @{$self->{_dump_storage}->{$class} || []};
871
79193756 872 # Check and see if the dump is infact differnt
873
874 my $compare_to;
875 if ($old_md5) {
876 $compare_to = $text . $self->_sig_comment($old_ver, $old_ts);
877
878
879 if (Digest::MD5::md5_base64($compare_to) eq $old_md5) {
880 return;
881 }
882 }
883
884 $text .= $self->_sig_comment(
01012543 885 $self->version_to_dump,
79193756 886 POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
887 );
7cab3ab7 888
889 open(my $fh, '>', $filename)
890 or croak "Cannot open '$filename' for writing: $!";
891
892 # Write the top half and its MD5 sum
a4476f41 893 print $fh $text . Digest::MD5::md5_base64($text) . "\n";
7cab3ab7 894
895 # Write out anything loaded via external partial class file in @INC
896 print $fh qq|$_\n|
897 for @{$self->{_ext_storage}->{$class} || []};
898
1eea4fb1 899 # Write out any custom content the user has added
7cab3ab7 900 print $fh $custom_content;
901
902 close($fh)
e9b8719e 903 or croak "Error closing '$filename': $!";
7cab3ab7 904}
905
79193756 906sub _default_custom_content {
907 return qq|\n\n# You can replace this text with custom|
908 . qq| content, and it will be preserved on regeneration|
909 . qq|\n1;\n|;
910}
911
7cab3ab7 912sub _get_custom_content {
ffc705f3 913 my ($self, $class, $filename, $add_default) = @_;
914
915 $add_default = 1 unless defined $add_default;
7cab3ab7 916
79193756 917 return ($self->_default_custom_content) if ! -f $filename;
918
7cab3ab7 919 open(my $fh, '<', $filename)
920 or croak "Cannot open '$filename' for reading: $!";
921
922 my $mark_re =
419a2eeb 923 qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
7cab3ab7 924
7cab3ab7 925 my $buffer = '';
79193756 926 my ($md5, $ts, $ver);
7cab3ab7 927 while(<$fh>) {
79193756 928 if(!$md5 && /$mark_re/) {
929 $md5 = $2;
930 my $line = $1;
931
932 # Pull out the previous version and timestamp
933 ($ver, $ts) = $buffer =~ m/# Created by DBIx::Class::Schema::Loader v(.*?) @ (.*?)$/s;
934
935 $buffer .= $line;
7cab3ab7 936 croak "Checksum mismatch in '$filename'"
79193756 937 if Digest::MD5::md5_base64($buffer) ne $md5;
7cab3ab7 938
939 $buffer = '';
940 }
941 else {
942 $buffer .= $_;
943 }
996be9ee 944 }
945
28b4691d 946 croak "Cannot not overwrite '$filename' without 'really_erase_my_files',"
419a2eeb 947 . " it does not appear to have been generated by Loader"
79193756 948 if !$md5;
5ef3c771 949
79193756 950 # Default custom content:
ffc705f3 951 $buffer ||= $self->_default_custom_content if $add_default;
5ef3c771 952
79193756 953 return ($buffer, $md5, $ver, $ts);
996be9ee 954}
955
956sub _use {
957 my $self = shift;
958 my $target = shift;
959
960 foreach (@_) {
cb54990b 961 warn "$target: use $_;" if $self->debug;
996be9ee 962 $self->_raw_stmt($target, "use $_;");
996be9ee 963 }
964}
965
966sub _inject {
967 my $self = shift;
968 my $target = shift;
969 my $schema_class = $self->schema_class;
970
af31090c 971 my $blist = join(q{ }, @_);
972 warn "$target: use base qw/ $blist /;" if $self->debug && @_;
973 $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
996be9ee 974}
975
f96ef30f 976# Create class with applicable bases, setup monikers, etc
977sub _make_src_class {
978 my ($self, $table) = @_;
996be9ee 979
a13b2803 980 my $schema = $self->schema;
981 my $schema_class = $self->schema_class;
996be9ee 982
f96ef30f 983 my $table_moniker = $self->_table2moniker($table);
f44ecc2f 984 my @result_namespace = ($schema_class);
985 if ($self->use_namespaces) {
986 my $result_namespace = $self->result_namespace || 'Result';
987 if ($result_namespace =~ /^\+(.*)/) {
988 # Fully qualified namespace
989 @result_namespace = ($1)
990 }
991 else {
992 # Relative namespace
993 push @result_namespace, $result_namespace;
994 }
995 }
996 my $table_class = join(q{::}, @result_namespace, $table_moniker);
996be9ee 997
f53dcdf0 998 if (my $upgrading_v = $self->_upgrading_from) {
999 local $self->naming->{monikers} = $upgrading_v;
1000
1001 my $old_class = join(q{::}, @result_namespace,
1002 $self->_table2moniker($table));
1003
1004 $self->_upgrading_classes->{$table_class} = $old_class;
1005 }
1006
f96ef30f 1007 my $table_normalized = lc $table;
1008 $self->classes->{$table} = $table_class;
1009 $self->classes->{$table_normalized} = $table_class;
1010 $self->monikers->{$table} = $table_moniker;
1011 $self->monikers->{$table_normalized} = $table_moniker;
996be9ee 1012
f96ef30f 1013 $self->_use ($table_class, @{$self->additional_classes});
af31090c 1014 $self->_inject($table_class, @{$self->left_base_classes});
996be9ee 1015
2229729e 1016 if (my @components = @{ $self->components }) {
1017 $self->_dbic_stmt($table_class, 'load_components', @components);
1018 }
996be9ee 1019
f96ef30f 1020 $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
1021 if @{$self->resultset_components};
af31090c 1022 $self->_inject($table_class, @{$self->additional_base_classes});
f96ef30f 1023}
996be9ee 1024
af31090c 1025# Set up metadata (cols, pks, etc)
f96ef30f 1026sub _setup_src_meta {
1027 my ($self, $table) = @_;
996be9ee 1028
f96ef30f 1029 my $schema = $self->schema;
1030 my $schema_class = $self->schema_class;
a13b2803 1031
f96ef30f 1032 my $table_class = $self->classes->{$table};
1033 my $table_moniker = $self->monikers->{$table};
996be9ee 1034
ff30991a 1035 my $table_name = $table;
1036 my $name_sep = $self->schema->storage->sql_maker->name_sep;
1037
c177d483 1038 if ($name_sep && $table_name =~ /\Q$name_sep\E/) {
ff30991a 1039 $table_name = \ $self->_quote_table_name($table_name);
1040 }
1041
1042 $self->_dbic_stmt($table_class,'table',$table_name);
996be9ee 1043
f96ef30f 1044 my $cols = $self->_table_columns($table);
1045 my $col_info;
1046 eval { $col_info = $self->_columns_info_for($table) };
1047 if($@) {
1048 $self->_dbic_stmt($table_class,'add_columns',@$cols);
1049 }
1050 else {
0906d55b 1051 if ($self->_is_case_sensitive) {
1052 for my $col (keys %$col_info) {
1053 $col_info->{$col}{accessor} = lc $col
1054 if $col ne lc($col);
1055 }
1056 } else {
1057 $col_info = { map { lc($_), $col_info->{$_} } keys %$col_info };
c9373b79 1058 }
1059
e7213f4f 1060 my $fks = $self->_table_fk_info($table);
565335e6 1061
e7213f4f 1062 for my $fkdef (@$fks) {
1063 for my $col (@{ $fkdef->{local_columns} }) {
565335e6 1064 $col_info->{$col}{is_foreign_key} = 1;
e7213f4f 1065 }
1066 }
f96ef30f 1067 $self->_dbic_stmt(
1068 $table_class,
1069 'add_columns',
565335e6 1070 map { $_, ($col_info->{$_}||{}) } @$cols
f96ef30f 1071 );
996be9ee 1072 }
1073
d70c335f 1074 my %uniq_tag; # used to eliminate duplicate uniqs
1075
f96ef30f 1076 my $pks = $self->_table_pk_info($table) || [];
1077 @$pks ? $self->_dbic_stmt($table_class,'set_primary_key',@$pks)
1078 : carp("$table has no primary key");
d70c335f 1079 $uniq_tag{ join("\0", @$pks) }++ if @$pks; # pk is a uniq
996be9ee 1080
f96ef30f 1081 my $uniqs = $self->_table_uniq_info($table) || [];
d70c335f 1082 for (@$uniqs) {
1083 my ($name, $cols) = @$_;
1084 next if $uniq_tag{ join("\0", @$cols) }++; # skip duplicates
1085 $self->_dbic_stmt($table_class,'add_unique_constraint', $name, $cols);
1086 }
1087
996be9ee 1088}
1089
1090=head2 tables
1091
1092Returns a sorted list of loaded tables, using the original database table
1093names.
1094
1095=cut
1096
1097sub tables {
1098 my $self = shift;
1099
b97c2c1e 1100 return keys %{$self->_tables};
996be9ee 1101}
1102
1103# Make a moniker from a table
c39e403e 1104sub _default_table2moniker {
66afce69 1105 no warnings 'uninitialized';
c39e403e 1106 my ($self, $table) = @_;
1107
a8d229ff 1108 if ($self->naming->{monikers} eq 'v4') {
1109 return join '', map ucfirst, split /[\W_]+/, lc $table;
1110 }
1111
c39e403e 1112 return join '', map ucfirst, split /[\W_]+/,
1113 Lingua::EN::Inflect::Number::to_S(lc $table);
1114}
1115
996be9ee 1116sub _table2moniker {
1117 my ( $self, $table ) = @_;
1118
1119 my $moniker;
1120
1121 if( ref $self->moniker_map eq 'HASH' ) {
1122 $moniker = $self->moniker_map->{$table};
1123 }
1124 elsif( ref $self->moniker_map eq 'CODE' ) {
1125 $moniker = $self->moniker_map->($table);
1126 }
1127
c39e403e 1128 $moniker ||= $self->_default_table2moniker($table);
996be9ee 1129
1130 return $moniker;
1131}
1132
1133sub _load_relationships {
e8ad6491 1134 my ($self, $table) = @_;
996be9ee 1135
e8ad6491 1136 my $tbl_fk_info = $self->_table_fk_info($table);
1137 foreach my $fkdef (@$tbl_fk_info) {
1138 $fkdef->{remote_source} =
1139 $self->monikers->{delete $fkdef->{remote_table}};
996be9ee 1140 }
26f1c8c9 1141 my $tbl_uniq_info = $self->_table_uniq_info($table);
996be9ee 1142
e8ad6491 1143 my $local_moniker = $self->monikers->{$table};
7824616e 1144 my $rel_stmts = $self->_relbuilder->generate_code($local_moniker, $tbl_fk_info, $tbl_uniq_info);
996be9ee 1145
996be9ee 1146 foreach my $src_class (sort keys %$rel_stmts) {
1147 my $src_stmts = $rel_stmts->{$src_class};
1148 foreach my $stmt (@$src_stmts) {
1149 $self->_dbic_stmt($src_class,$stmt->{method},@{$stmt->{args}});
1150 }
1151 }
1152}
1153
1154# Overload these in driver class:
1155
1156# Returns an arrayref of column names
1157sub _table_columns { croak "ABSTRACT METHOD" }
1158
1159# Returns arrayref of pk col names
1160sub _table_pk_info { croak "ABSTRACT METHOD" }
1161
1162# Returns an arrayref of uniqs [ [ foo => [ col1, col2 ] ], [ bar => [ ... ] ] ]
1163sub _table_uniq_info { croak "ABSTRACT METHOD" }
1164
1165# Returns an arrayref of foreign key constraints, each
1166# being a hashref with 3 keys:
1167# local_columns (arrayref), remote_columns (arrayref), remote_table
1168sub _table_fk_info { croak "ABSTRACT METHOD" }
1169
1170# Returns an array of lower case table names
1171sub _tables_list { croak "ABSTRACT METHOD" }
1172
1173# Execute a constructive DBIC class method, with debug/dump_to_dir hooks.
1174sub _dbic_stmt {
1175 my $self = shift;
1176 my $class = shift;
1177 my $method = shift;
fbcfebdd 1178 if ( $method eq 'table' ) {
1179 my ($table) = @_;
1180 $self->_pod( $class, "=head1 NAME" );
1181 my $table_descr = $class;
1182 if ( $self->can('_table_comment') ) {
1183 my $comment = $self->_table_comment($table);
1184 $table_descr .= " - " . $comment if $comment;
1185 }
1186 $self->{_class2table}{ $class } = $table;
1187 $self->_pod( $class, $table_descr );
1188 $self->_pod_cut( $class );
1189 } elsif ( $method eq 'add_columns' ) {
1190 $self->_pod( $class, "=head1 ACCESSORS" );
1191 my $i = 0;
1192 foreach ( @_ ) {
1193 $i++;
1194 next unless $i % 2;
1195 $self->_pod( $class, '=head2 ' . $_ );
1196 my $comment;
1197 $comment = $self->_column_comment( $self->{_class2table}{$class}, ($i - 1) / 2 + 1 ) if $self->can('_column_comment');
1198 $self->_pod( $class, $comment ) if $comment;
1199 }
1200 $self->_pod_cut( $class );
1201 } elsif ( $method =~ /^(belongs_to|has_many|might_have)$/ ) {
1202 $self->_pod( $class, "=head1 RELATIONS" ) unless $self->{_relations_started} { $class } ;
1203 my ( $accessor, $rel_class ) = @_;
1204 $self->_pod( $class, "=head2 $accessor" );
1205 $self->_pod( $class, 'Type: ' . $method );
1206 $self->_pod( $class, "Related object: L<$rel_class>" );
1207 $self->_pod_cut( $class );
1208 $self->{_relations_started} { $class } = 1;
1209 }
996be9ee 1210 my $args = dump(@_);
1211 $args = '(' . $args . ')' if @_ < 2;
1212 my $stmt = $method . $args . q{;};
1213
1214 warn qq|$class\->$stmt\n| if $self->debug;
996be9ee 1215 $self->_raw_stmt($class, '__PACKAGE__->' . $stmt);
fbcfebdd 1216 return;
996be9ee 1217}
1218
fbcfebdd 1219# Stores a POD documentation
1220sub _pod {
1221 my ($self, $class, $stmt) = @_;
1222 $self->_raw_stmt( $class, "\n" . $stmt );
1223}
1224
1225sub _pod_cut {
1226 my ($self, $class ) = @_;
1227 $self->_raw_stmt( $class, "\n=cut\n" );
1228}
1229
1230
996be9ee 1231# Store a raw source line for a class (for dumping purposes)
1232sub _raw_stmt {
1233 my ($self, $class, $stmt) = @_;
af31090c 1234 push(@{$self->{_dump_storage}->{$class}}, $stmt);
996be9ee 1235}
1236
7cab3ab7 1237# Like above, but separately for the externally loaded stuff
1238sub _ext_stmt {
1239 my ($self, $class, $stmt) = @_;
af31090c 1240 push(@{$self->{_ext_storage}->{$class}}, $stmt);
7cab3ab7 1241}
1242
565335e6 1243sub _quote_table_name {
1244 my ($self, $table) = @_;
1245
1246 my $qt = $self->schema->storage->sql_maker->quote_char;
1247
c177d483 1248 return $table unless $qt;
1249
565335e6 1250 if (ref $qt) {
1251 return $qt->[0] . $table . $qt->[1];
1252 }
1253
1254 return $qt . $table . $qt;
1255}
1256
1257sub _is_case_sensitive { 0 }
1258
ffc705f3 1259# remove the dump dir from @INC on destruction
1260sub DESTROY {
1261 my $self = shift;
1262
1263 @INC = grep $_ ne $self->dump_directory, @INC;
1264}
1265
996be9ee 1266=head2 monikers
1267
8f9d7ce5 1268Returns a hashref of loaded table to moniker mappings. There will
996be9ee 1269be two entries for each table, the original name and the "normalized"
1270name, in the case that the two are different (such as databases
1271that like uppercase table names, or preserve your original mixed-case
1272definitions, or what-have-you).
1273
1274=head2 classes
1275
8f9d7ce5 1276Returns a hashref of table to class mappings. In some cases it will
996be9ee 1277contain multiple entries per table for the original and normalized table
1278names, as above in L</monikers>.
1279
1280=head1 SEE ALSO
1281
1282L<DBIx::Class::Schema::Loader>
1283
be80bba7 1284=head1 AUTHOR
1285
9cc8e7e1 1286See L<DBIx::Class::Schema::Loader/AUTHOR> and L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
be80bba7 1287
1288=head1 LICENSE
1289
1290This library is free software; you can redistribute it and/or modify it under
1291the same terms as Perl itself.
1292
996be9ee 1293=cut
1294
12951;