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