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