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