minor changes
[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;
fb3bb595 346# just in case, though no one is likely to dump a dynamic schema
347 $self->version_to_dump('0.04006');
01012543 348 return;
349 }
350
351# otherwise check if we need backcompat mode for a static schema
7824616e 352 my $filename = $self->_get_dump_filename($self->schema_class);
353 return unless -e $filename;
354
355 open(my $fh, '<', $filename)
356 or croak "Cannot open '$filename' for reading: $!";
357
358 while (<$fh>) {
01012543 359 if (/^# Created by DBIx::Class::Schema::Loader v((\d+)\.(\d+))/) {
360 my $real_ver = $1;
361 my $ver = "v${2}_${3}";
7824616e 362 while (1) {
363 my $compat_class = "DBIx::Class::Schema::Loader::Compat::${ver}";
364 if ($self->load_optional_class($compat_class)) {
365 no strict 'refs';
366 my $class = ref $self || $self;
367 unshift @{"${class}::ISA"}, $compat_class;
01012543 368 Class::C3::reinitialize;
369 $self->version_to_dump($real_ver);
7824616e 370 last;
371 }
372 $ver =~ s/\d\z// or last;
373 }
374 last;
375 }
376 }
377 close $fh;
996be9ee 378}
379
419a2eeb 380sub _find_file_in_inc {
381 my ($self, $file) = @_;
382
383 foreach my $prefix (@INC) {
af31090c 384 my $fullpath = File::Spec->catfile($prefix, $file);
385 return $fullpath if -f $fullpath
386 and Cwd::abs_path($fullpath) ne
387 Cwd::abs_path(File::Spec->catfile($self->dump_directory, $file)) || '';
419a2eeb 388 }
389
390 return;
391}
392
fb3bb595 393sub _class_path {
f96ef30f 394 my ($self, $class) = @_;
395
396 my $class_path = $class;
397 $class_path =~ s{::}{/}g;
398 $class_path .= '.pm';
399
fb3bb595 400 return $class_path;
401}
402
403sub _find_class_in_inc {
404 my ($self, $class) = @_;
405
406 return $self->_find_file_in_inc($self->_class_path($class));
407}
408
409sub _load_external {
410 my ($self, $class) = @_;
411
412 my $real_inc_path = $self->_find_class_in_inc($class);
f96ef30f 413
af31090c 414 return if !$real_inc_path;
f96ef30f 415
416 # If we make it to here, we loaded an external definition
417 warn qq/# Loaded external class definition for '$class'\n/
418 if $self->debug;
419
f96ef30f 420 open(my $fh, '<', $real_inc_path)
421 or croak "Failed to open '$real_inc_path' for reading: $!";
422 $self->_ext_stmt($class,
565ca24d 423 qq|# These lines were loaded from '$real_inc_path' found in \@INC.\n|
424 .qq|# They are now part of the custom portion of this file\n|
425 .qq|# for you to hand-edit. If you do not either delete\n|
426 .qq|# this section or remove that file from \@INC, this section\n|
427 .qq|# will be repeated redundantly when you re-create this\n|
428 .qq|# file again via Loader!\n|
f96ef30f 429 );
430 while(<$fh>) {
431 chomp;
432 $self->_ext_stmt($class, $_);
996be9ee 433 }
f96ef30f 434 $self->_ext_stmt($class,
70b72fab 435 qq|# End of lines loaded from '$real_inc_path' |
f96ef30f 436 );
437 close($fh)
438 or croak "Failed to close $real_inc_path: $!";
996be9ee 439}
440
441=head2 load
442
443Does the actual schema-construction work.
444
445=cut
446
447sub load {
448 my $self = shift;
449
b97c2c1e 450 $self->_load_tables($self->_tables_list);
451}
452
453=head2 rescan
454
a60b5b8d 455Arguments: schema
456
b97c2c1e 457Rescan the database for newly added tables. Does
a60b5b8d 458not process drops or changes. Returns a list of
459the newly added table monikers.
460
461The schema argument should be the schema class
462or object to be affected. It should probably
463be derived from the original schema_class used
464during L</load>.
b97c2c1e 465
466=cut
467
468sub rescan {
a60b5b8d 469 my ($self, $schema) = @_;
470
471 $self->{schema} = $schema;
7824616e 472 $self->_relbuilder->{schema} = $schema;
b97c2c1e 473
474 my @created;
475 my @current = $self->_tables_list;
476 foreach my $table ($self->_tables_list) {
477 if(!exists $self->{_tables}->{$table}) {
478 push(@created, $table);
479 }
480 }
481
c39e3507 482 my $loaded = $self->_load_tables(@created);
a60b5b8d 483
c39e3507 484 return map { $self->monikers->{$_} } @$loaded;
b97c2c1e 485}
486
7824616e 487sub _relbuilder {
488 my ($self) = @_;
3fed44ca 489
490 return if $self->{skip_relationships};
491
7824616e 492 $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder->new(
493 $self->schema, $self->inflect_plural, $self->inflect_singular
494 );
495}
496
b97c2c1e 497sub _load_tables {
498 my ($self, @tables) = @_;
499
f96ef30f 500 # First, use _tables_list with constraint and exclude
501 # to get a list of tables to operate on
502
503 my $constraint = $self->constraint;
504 my $exclude = $self->exclude;
f96ef30f 505
b97c2c1e 506 @tables = grep { /$constraint/ } @tables if $constraint;
507 @tables = grep { ! /$exclude/ } @tables if $exclude;
f96ef30f 508
b97c2c1e 509 # Save the new tables to the tables list
a60b5b8d 510 foreach (@tables) {
511 $self->{_tables}->{$_} = 1;
512 }
f96ef30f 513
af31090c 514 $self->_make_src_class($_) for @tables;
f96ef30f 515 $self->_setup_src_meta($_) for @tables;
516
e8ad6491 517 if(!$self->skip_relationships) {
181cc907 518 # The relationship loader needs a working schema
af31090c 519 $self->{quiet} = 1;
79193756 520 local $self->{dump_directory} = $self->{temp_directory};
181cc907 521 $self->_reload_classes(@tables);
e8ad6491 522 $self->_load_relationships($_) for @tables;
af31090c 523 $self->{quiet} = 0;
79193756 524
525 # Remove that temp dir from INC so it doesn't get reloaded
526 @INC = grep { $_ ne $self->{dump_directory} } @INC;
e8ad6491 527 }
528
f96ef30f 529 $self->_load_external($_)
75451704 530 for map { $self->classes->{$_} } @tables;
f96ef30f 531
181cc907 532 $self->_reload_classes(@tables);
996be9ee 533
5223f24a 534 # Drop temporary cache
535 delete $self->{_cache};
536
c39e3507 537 return \@tables;
996be9ee 538}
539
af31090c 540sub _reload_classes {
181cc907 541 my ($self, @tables) = @_;
542
4daef04f 543 # so that we don't repeat custom sections
544 @INC = grep $_ ne $self->dump_directory, @INC;
545
181cc907 546 $self->_dump_to_dir(map { $self->classes->{$_} } @tables);
e9b8719e 547
548 unshift @INC, $self->dump_directory;
af31090c 549
706ef173 550 my @to_register;
551 my %have_source = map { $_ => $self->schema->source($_) }
552 $self->schema->sources;
553
181cc907 554 for my $table (@tables) {
555 my $moniker = $self->monikers->{$table};
556 my $class = $self->classes->{$table};
0ae6b65d 557
558 {
559 no warnings 'redefine';
560 local *Class::C3::reinitialize = sub {};
561 use warnings;
562
706ef173 563 Class::Unload->unload($class);
564 my ($source, $resultset_class);
565 if (
566 ($source = $have_source{$moniker})
567 && ($resultset_class = $source->resultset_class)
568 && ($resultset_class ne 'DBIx::Class::ResultSet')
569 ) {
570 my $has_file = Class::Inspector->loaded_filename($resultset_class);
571 Class::Unload->unload($resultset_class);
6ae3f335 572 $self->ensure_class_loaded($resultset_class) if $has_file;
0ae6b65d 573 }
6ae3f335 574 $self->ensure_class_loaded($class);
af31090c 575 }
706ef173 576 push @to_register, [$moniker, $class];
577 }
af31090c 578
706ef173 579 Class::C3->reinitialize;
580 for (@to_register) {
581 $self->schema->register_class(@$_);
af31090c 582 }
583}
584
996be9ee 585sub _get_dump_filename {
586 my ($self, $class) = (@_);
587
588 $class =~ s{::}{/}g;
589 return $self->dump_directory . q{/} . $class . q{.pm};
590}
591
592sub _ensure_dump_subdirs {
593 my ($self, $class) = (@_);
594
595 my @name_parts = split(/::/, $class);
dd03ee1a 596 pop @name_parts; # we don't care about the very last element,
597 # which is a filename
598
996be9ee 599 my $dir = $self->dump_directory;
7cab3ab7 600 while (1) {
601 if(!-d $dir) {
25328cc4 602 mkdir($dir) or croak "mkdir('$dir') failed: $!";
996be9ee 603 }
7cab3ab7 604 last if !@name_parts;
605 $dir = File::Spec->catdir($dir, shift @name_parts);
996be9ee 606 }
607}
608
609sub _dump_to_dir {
af31090c 610 my ($self, @classes) = @_;
996be9ee 611
fc2b71fd 612 my $schema_class = $self->schema_class;
9c9c2f2b 613 my $schema_base_class = $self->schema_base_class || 'DBIx::Class::Schema';
996be9ee 614
e9b8719e 615 my $target_dir = $self->dump_directory;
af31090c 616 warn "Dumping manual schema for $schema_class to directory $target_dir ...\n"
617 unless $self->{dynamic} or $self->{quiet};
996be9ee 618
7cab3ab7 619 my $schema_text =
620 qq|package $schema_class;\n\n|
b4dcbcc5 621 . qq|# Created by DBIx::Class::Schema::Loader\n|
622 . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
7cab3ab7 623 . qq|use strict;\nuse warnings;\n\n|
9c9c2f2b 624 . qq|use base '$schema_base_class';\n\n|;
f44ecc2f 625
f44ecc2f 626 if ($self->use_namespaces) {
627 $schema_text .= qq|__PACKAGE__->load_namespaces|;
628 my $namespace_options;
629 for my $attr (qw(result_namespace
630 resultset_namespace
631 default_resultset_class)) {
632 if ($self->$attr) {
633 $namespace_options .= qq| $attr => '| . $self->$attr . qq|',\n|
634 }
635 }
636 $schema_text .= qq|(\n$namespace_options)| if $namespace_options;
637 $schema_text .= qq|;\n|;
638 }
639 else {
640 $schema_text .= qq|__PACKAGE__->load_classes;\n|;
f44ecc2f 641 }
996be9ee 642
7cab3ab7 643 $self->_write_classfile($schema_class, $schema_text);
996be9ee 644
9c9c2f2b 645 my $result_base_class = $self->result_base_class || 'DBIx::Class';
646
af31090c 647 foreach my $src_class (@classes) {
7cab3ab7 648 my $src_text =
649 qq|package $src_class;\n\n|
b4dcbcc5 650 . qq|# Created by DBIx::Class::Schema::Loader\n|
651 . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
7cab3ab7 652 . qq|use strict;\nuse warnings;\n\n|
9c9c2f2b 653 . qq|use base '$result_base_class';\n\n|;
996be9ee 654
7cab3ab7 655 $self->_write_classfile($src_class, $src_text);
02356864 656 }
996be9ee 657
af31090c 658 warn "Schema dump completed.\n" unless $self->{dynamic} or $self->{quiet};
659
7cab3ab7 660}
661
79193756 662sub _sig_comment {
663 my ($self, $version, $ts) = @_;
664 return qq|\n\n# Created by DBIx::Class::Schema::Loader|
665 . qq| v| . $version
666 . q| @ | . $ts
667 . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
668}
669
7cab3ab7 670sub _write_classfile {
671 my ($self, $class, $text) = @_;
672
673 my $filename = $self->_get_dump_filename($class);
674 $self->_ensure_dump_subdirs($class);
675
28b4691d 676 if (-f $filename && $self->really_erase_my_files) {
7cab3ab7 677 warn "Deleting existing file '$filename' due to "
af31090c 678 . "'really_erase_my_files' setting\n" unless $self->{quiet};
7cab3ab7 679 unlink($filename);
680 }
681
79193756 682 my ($custom_content, $old_md5, $old_ver, $old_ts) = $self->_get_custom_content($class, $filename);
17ca645f 683
7cab3ab7 684 $text .= qq|$_\n|
685 for @{$self->{_dump_storage}->{$class} || []};
686
79193756 687 # Check and see if the dump is infact differnt
688
689 my $compare_to;
690 if ($old_md5) {
691 $compare_to = $text . $self->_sig_comment($old_ver, $old_ts);
692
693
694 if (Digest::MD5::md5_base64($compare_to) eq $old_md5) {
695 return;
696 }
697 }
698
699 $text .= $self->_sig_comment(
01012543 700 $self->version_to_dump,
79193756 701 POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
702 );
7cab3ab7 703
704 open(my $fh, '>', $filename)
705 or croak "Cannot open '$filename' for writing: $!";
706
707 # Write the top half and its MD5 sum
a4476f41 708 print $fh $text . Digest::MD5::md5_base64($text) . "\n";
7cab3ab7 709
710 # Write out anything loaded via external partial class file in @INC
711 print $fh qq|$_\n|
712 for @{$self->{_ext_storage}->{$class} || []};
713
1eea4fb1 714 # Write out any custom content the user has added
7cab3ab7 715 print $fh $custom_content;
716
717 close($fh)
e9b8719e 718 or croak "Error closing '$filename': $!";
7cab3ab7 719}
720
79193756 721sub _default_custom_content {
722 return qq|\n\n# You can replace this text with custom|
723 . qq| content, and it will be preserved on regeneration|
724 . qq|\n1;\n|;
725}
726
7cab3ab7 727sub _get_custom_content {
728 my ($self, $class, $filename) = @_;
729
79193756 730 return ($self->_default_custom_content) if ! -f $filename;
731
7cab3ab7 732 open(my $fh, '<', $filename)
733 or croak "Cannot open '$filename' for reading: $!";
734
735 my $mark_re =
419a2eeb 736 qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
7cab3ab7 737
7cab3ab7 738 my $buffer = '';
79193756 739 my ($md5, $ts, $ver);
7cab3ab7 740 while(<$fh>) {
79193756 741 if(!$md5 && /$mark_re/) {
742 $md5 = $2;
743 my $line = $1;
744
745 # Pull out the previous version and timestamp
746 ($ver, $ts) = $buffer =~ m/# Created by DBIx::Class::Schema::Loader v(.*?) @ (.*?)$/s;
747
748 $buffer .= $line;
7cab3ab7 749 croak "Checksum mismatch in '$filename'"
79193756 750 if Digest::MD5::md5_base64($buffer) ne $md5;
7cab3ab7 751
752 $buffer = '';
753 }
754 else {
755 $buffer .= $_;
756 }
996be9ee 757 }
758
28b4691d 759 croak "Cannot not overwrite '$filename' without 'really_erase_my_files',"
419a2eeb 760 . " it does not appear to have been generated by Loader"
79193756 761 if !$md5;
5ef3c771 762
79193756 763 # Default custom content:
764 $buffer ||= $self->_default_custom_content;
5ef3c771 765
79193756 766 return ($buffer, $md5, $ver, $ts);
996be9ee 767}
768
769sub _use {
770 my $self = shift;
771 my $target = shift;
772
773 foreach (@_) {
cb54990b 774 warn "$target: use $_;" if $self->debug;
996be9ee 775 $self->_raw_stmt($target, "use $_;");
996be9ee 776 }
777}
778
779sub _inject {
780 my $self = shift;
781 my $target = shift;
782 my $schema_class = $self->schema_class;
783
af31090c 784 my $blist = join(q{ }, @_);
785 warn "$target: use base qw/ $blist /;" if $self->debug && @_;
786 $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
996be9ee 787}
788
f96ef30f 789# Create class with applicable bases, setup monikers, etc
790sub _make_src_class {
791 my ($self, $table) = @_;
996be9ee 792
a13b2803 793 my $schema = $self->schema;
794 my $schema_class = $self->schema_class;
996be9ee 795
f96ef30f 796 my $table_moniker = $self->_table2moniker($table);
f44ecc2f 797 my @result_namespace = ($schema_class);
798 if ($self->use_namespaces) {
799 my $result_namespace = $self->result_namespace || 'Result';
800 if ($result_namespace =~ /^\+(.*)/) {
801 # Fully qualified namespace
802 @result_namespace = ($1)
803 }
804 else {
805 # Relative namespace
806 push @result_namespace, $result_namespace;
807 }
808 }
809 my $table_class = join(q{::}, @result_namespace, $table_moniker);
996be9ee 810
f96ef30f 811 my $table_normalized = lc $table;
812 $self->classes->{$table} = $table_class;
813 $self->classes->{$table_normalized} = $table_class;
814 $self->monikers->{$table} = $table_moniker;
815 $self->monikers->{$table_normalized} = $table_moniker;
996be9ee 816
f96ef30f 817 $self->_use ($table_class, @{$self->additional_classes});
af31090c 818 $self->_inject($table_class, @{$self->left_base_classes});
996be9ee 819
605fcea8 820 $self->_dbic_stmt($table_class, 'load_components', @{$self->components}, 'Core');
996be9ee 821
f96ef30f 822 $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
823 if @{$self->resultset_components};
af31090c 824 $self->_inject($table_class, @{$self->additional_base_classes});
f96ef30f 825}
996be9ee 826
af31090c 827# Set up metadata (cols, pks, etc)
f96ef30f 828sub _setup_src_meta {
829 my ($self, $table) = @_;
996be9ee 830
f96ef30f 831 my $schema = $self->schema;
832 my $schema_class = $self->schema_class;
a13b2803 833
f96ef30f 834 my $table_class = $self->classes->{$table};
835 my $table_moniker = $self->monikers->{$table};
996be9ee 836
ff30991a 837 my $table_name = $table;
838 my $name_sep = $self->schema->storage->sql_maker->name_sep;
839
c177d483 840 if ($name_sep && $table_name =~ /\Q$name_sep\E/) {
ff30991a 841 $table_name = \ $self->_quote_table_name($table_name);
842 }
843
844 $self->_dbic_stmt($table_class,'table',$table_name);
996be9ee 845
f96ef30f 846 my $cols = $self->_table_columns($table);
847 my $col_info;
848 eval { $col_info = $self->_columns_info_for($table) };
849 if($@) {
850 $self->_dbic_stmt($table_class,'add_columns',@$cols);
851 }
852 else {
0906d55b 853 if ($self->_is_case_sensitive) {
854 for my $col (keys %$col_info) {
855 $col_info->{$col}{accessor} = lc $col
856 if $col ne lc($col);
857 }
858 } else {
859 $col_info = { map { lc($_), $col_info->{$_} } keys %$col_info };
c9373b79 860 }
861
e7213f4f 862 my $fks = $self->_table_fk_info($table);
565335e6 863
e7213f4f 864 for my $fkdef (@$fks) {
865 for my $col (@{ $fkdef->{local_columns} }) {
565335e6 866 $col_info->{$col}{is_foreign_key} = 1;
e7213f4f 867 }
868 }
f96ef30f 869 $self->_dbic_stmt(
870 $table_class,
871 'add_columns',
565335e6 872 map { $_, ($col_info->{$_}||{}) } @$cols
f96ef30f 873 );
996be9ee 874 }
875
d70c335f 876 my %uniq_tag; # used to eliminate duplicate uniqs
877
f96ef30f 878 my $pks = $self->_table_pk_info($table) || [];
879 @$pks ? $self->_dbic_stmt($table_class,'set_primary_key',@$pks)
880 : carp("$table has no primary key");
d70c335f 881 $uniq_tag{ join("\0", @$pks) }++ if @$pks; # pk is a uniq
996be9ee 882
f96ef30f 883 my $uniqs = $self->_table_uniq_info($table) || [];
d70c335f 884 for (@$uniqs) {
885 my ($name, $cols) = @$_;
886 next if $uniq_tag{ join("\0", @$cols) }++; # skip duplicates
887 $self->_dbic_stmt($table_class,'add_unique_constraint', $name, $cols);
888 }
889
996be9ee 890}
891
892=head2 tables
893
894Returns a sorted list of loaded tables, using the original database table
895names.
896
897=cut
898
899sub tables {
900 my $self = shift;
901
b97c2c1e 902 return keys %{$self->_tables};
996be9ee 903}
904
905# Make a moniker from a table
c39e403e 906sub _default_table2moniker {
907 my ($self, $table) = @_;
908
909 return join '', map ucfirst, split /[\W_]+/,
910 Lingua::EN::Inflect::Number::to_S(lc $table);
911}
912
996be9ee 913sub _table2moniker {
914 my ( $self, $table ) = @_;
915
916 my $moniker;
917
918 if( ref $self->moniker_map eq 'HASH' ) {
919 $moniker = $self->moniker_map->{$table};
920 }
921 elsif( ref $self->moniker_map eq 'CODE' ) {
922 $moniker = $self->moniker_map->($table);
923 }
924
c39e403e 925 $moniker ||= $self->_default_table2moniker($table);
996be9ee 926
927 return $moniker;
928}
929
930sub _load_relationships {
e8ad6491 931 my ($self, $table) = @_;
996be9ee 932
e8ad6491 933 my $tbl_fk_info = $self->_table_fk_info($table);
934 foreach my $fkdef (@$tbl_fk_info) {
935 $fkdef->{remote_source} =
936 $self->monikers->{delete $fkdef->{remote_table}};
996be9ee 937 }
26f1c8c9 938 my $tbl_uniq_info = $self->_table_uniq_info($table);
996be9ee 939
e8ad6491 940 my $local_moniker = $self->monikers->{$table};
7824616e 941 my $rel_stmts = $self->_relbuilder->generate_code($local_moniker, $tbl_fk_info, $tbl_uniq_info);
996be9ee 942
996be9ee 943 foreach my $src_class (sort keys %$rel_stmts) {
944 my $src_stmts = $rel_stmts->{$src_class};
945 foreach my $stmt (@$src_stmts) {
946 $self->_dbic_stmt($src_class,$stmt->{method},@{$stmt->{args}});
947 }
948 }
949}
950
951# Overload these in driver class:
952
953# Returns an arrayref of column names
954sub _table_columns { croak "ABSTRACT METHOD" }
955
956# Returns arrayref of pk col names
957sub _table_pk_info { croak "ABSTRACT METHOD" }
958
959# Returns an arrayref of uniqs [ [ foo => [ col1, col2 ] ], [ bar => [ ... ] ] ]
960sub _table_uniq_info { croak "ABSTRACT METHOD" }
961
962# Returns an arrayref of foreign key constraints, each
963# being a hashref with 3 keys:
964# local_columns (arrayref), remote_columns (arrayref), remote_table
965sub _table_fk_info { croak "ABSTRACT METHOD" }
966
967# Returns an array of lower case table names
968sub _tables_list { croak "ABSTRACT METHOD" }
969
970# Execute a constructive DBIC class method, with debug/dump_to_dir hooks.
971sub _dbic_stmt {
972 my $self = shift;
973 my $class = shift;
974 my $method = shift;
975
996be9ee 976 my $args = dump(@_);
977 $args = '(' . $args . ')' if @_ < 2;
978 my $stmt = $method . $args . q{;};
979
980 warn qq|$class\->$stmt\n| if $self->debug;
996be9ee 981 $self->_raw_stmt($class, '__PACKAGE__->' . $stmt);
982}
983
984# Store a raw source line for a class (for dumping purposes)
985sub _raw_stmt {
986 my ($self, $class, $stmt) = @_;
af31090c 987 push(@{$self->{_dump_storage}->{$class}}, $stmt);
996be9ee 988}
989
7cab3ab7 990# Like above, but separately for the externally loaded stuff
991sub _ext_stmt {
992 my ($self, $class, $stmt) = @_;
af31090c 993 push(@{$self->{_ext_storage}->{$class}}, $stmt);
7cab3ab7 994}
995
565335e6 996sub _quote_table_name {
997 my ($self, $table) = @_;
998
999 my $qt = $self->schema->storage->sql_maker->quote_char;
1000
c177d483 1001 return $table unless $qt;
1002
565335e6 1003 if (ref $qt) {
1004 return $qt->[0] . $table . $qt->[1];
1005 }
1006
1007 return $qt . $table . $qt;
1008}
1009
1010sub _is_case_sensitive { 0 }
1011
996be9ee 1012=head2 monikers
1013
8f9d7ce5 1014Returns a hashref of loaded table to moniker mappings. There will
996be9ee 1015be two entries for each table, the original name and the "normalized"
1016name, in the case that the two are different (such as databases
1017that like uppercase table names, or preserve your original mixed-case
1018definitions, or what-have-you).
1019
1020=head2 classes
1021
8f9d7ce5 1022Returns a hashref of table to class mappings. In some cases it will
996be9ee 1023contain multiple entries per table for the original and normalized table
1024names, as above in L</monikers>.
1025
1026=head1 SEE ALSO
1027
1028L<DBIx::Class::Schema::Loader>
1029
be80bba7 1030=head1 AUTHOR
1031
1032See L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
1033
1034=head1 LICENSE
1035
1036This library is free software; you can redistribute it and/or modify it under
1037the same terms as Perl itself.
1038
996be9ee 1039=cut
1040
10411;