column_info POD generation improvements
[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;
65e705c3 5use base qw/Class::Accessor::Grouped Class::C3::Componentised/;
f170d55b 6use namespace::autoclean;
996be9ee 7use Class::C3;
fa994d3c 8use Carp::Clan qw/^DBIx::Class/;
996be9ee 9use DBIx::Class::Schema::Loader::RelBuilder;
10use Data::Dump qw/ dump /;
11use POSIX qw//;
dd03ee1a 12use File::Spec qw//;
419a2eeb 13use Cwd qw//;
7cab3ab7 14use Digest::MD5 qw//;
22270947 15use Lingua::EN::Inflect::Number qw//;
af31090c 16use File::Temp qw//;
17use Class::Unload;
8048320c 18use Class::Inspector ();
f170d55b 19use Data::Dumper::Concise;
20use Scalar::Util 'looks_like_number';
996be9ee 21require DBIx::Class;
22
e42ec4ef 23our $VERSION = '0.05003';
32f784fc 24
3d95f9ff 25__PACKAGE__->mk_group_ro_accessors('simple', qw/
996be9ee 26 schema
27 schema_class
28
29 exclude
30 constraint
31 additional_classes
32 additional_base_classes
33 left_base_classes
34 components
35 resultset_components
59cfa251 36 skip_relationships
0ca61324 37 skip_load_external
996be9ee 38 moniker_map
b639d969 39 custom_column_info
996be9ee 40 inflect_singular
41 inflect_plural
42 debug
43 dump_directory
d65cda9e 44 dump_overwrite
28b4691d 45 really_erase_my_files
f44ecc2f 46 resultset_namespace
47 default_resultset_class
9c9c2f2b 48 schema_base_class
49 result_base_class
639a1367 50 overwrite_modifications
996be9ee 51
c8c27020 52 relationship_attrs
53
996be9ee 54 db_schema
55 _tables
56 classes
f53dcdf0 57 _upgrading_classes
996be9ee 58 monikers
106a976a 59 dynamic
a8d229ff 60 naming
42e785fa 61 datetime_timezone
62 datetime_locale
65e705c3 63/);
64
996be9ee 65
3d95f9ff 66__PACKAGE__->mk_group_accessors('simple', qw/
01012543 67 version_to_dump
1c95b304 68 schema_version_to_dump
f53dcdf0 69 _upgrading_from
f22644d7 70 _upgrading_from_load_classes
a4b94090 71 _downgrading_to_load_classes
540a8149 72 _rewriting_result_namespace
f22644d7 73 use_namespaces
540a8149 74 result_namespace
492dce8d 75 generate_pod
43b982ea 76 pod_comment_mode
77 pod_comment_spillover_length
01012543 78/);
79
996be9ee 80=head1 NAME
81
82DBIx::Class::Schema::Loader::Base - Base DBIx::Class::Schema::Loader Implementation.
83
84=head1 SYNOPSIS
85
86See L<DBIx::Class::Schema::Loader>
87
88=head1 DESCRIPTION
89
90This is the base class for the storage-specific C<DBIx::Class::Schema::*>
91classes, and implements the common functionality between them.
92
93=head1 CONSTRUCTOR OPTIONS
94
95These constructor options are the base options for
29ddb54c 96L<DBIx::Class::Schema::Loader/loader_options>. Available constructor options are:
3953cbee 97
59cfa251 98=head2 skip_relationships
996be9ee 99
59cfa251 100Skip setting up relationships. The default is to attempt the loading
101of relationships.
996be9ee 102
0ca61324 103=head2 skip_load_external
104
105Skip loading of other classes in @INC. The default is to merge all other classes
106with the same name found in @INC into the schema file we are creating.
107
9a95164d 108=head2 naming
109
110Static schemas (ones dumped to disk) will, by default, use the new-style 0.05XXX
111relationship names and singularized Results, unless you're overwriting an
112existing dump made by a 0.04XXX version of L<DBIx::Class::Schema::Loader>, in
113which case the backward compatible RelBuilder will be activated, and
114singularization will be turned off.
115
116Specifying
117
118 naming => 'v5'
119
120will disable the backward-compatible RelBuilder and use
121the new-style relationship names along with singularized Results, even when
122overwriting a dump made with an earlier version.
123
124The option also takes a hashref:
125
a8d229ff 126 naming => { relationships => 'v5', monikers => 'v4' }
127
128The keys are:
129
130=over 4
131
132=item relationships
133
134How to name relationship accessors.
135
136=item monikers
137
138How to name Result classes.
139
140=back
9a95164d 141
142The values can be:
143
144=over 4
145
146=item current
147
148Latest default style, whatever that happens to be.
149
150=item v5
151
152Version 0.05XXX style.
153
154=item v4
155
156Version 0.04XXX style.
157
158=back
159
160Dynamic schemas will always default to the 0.04XXX relationship names and won't
161singularize Results for backward compatibility, to activate the new RelBuilder
162and singularization put this in your C<Schema.pm> file:
163
164 __PACKAGE__->naming('current');
165
166Or if you prefer to use 0.05XXX features but insure that nothing breaks in the
167next major version upgrade:
168
169 __PACKAGE__->naming('v5');
170
492dce8d 171=head2 generate_pod
172
173By default POD will be generated for columns and relationships, using database
7f2de014 174metadata for the text if available and supported.
175
176Reading database metadata (e.g. C<COMMENT ON TABLE some_table ...>) is only
177supported for Postgres right now.
492dce8d 178
179Set this to C<0> to turn off all POD generation.
180
43b982ea 181=head2 pod_comment_mode
182
f7976fea 183Controls where table comments appear in the generated POD. Smaller table
184comments are appended to the C<NAME> section of the documentation, and larger
185ones are inserted into C<DESCRIPTION> instead. You can force a C<DESCRIPTION>
186section to be generated with the comment always, only use C<NAME>, or choose
187the length threshold at which the comment is forced into the description.
43b982ea 188
34896b5e 189=over 4
190
191=item name
192
193Use C<NAME> section only.
194
195=item description
196
197Force C<DESCRIPTION> always.
198
199=item auto
200
201Use C<DESCRIPTION> if length > L</pod_comment_spillover_length>, this is the
202default.
203
204=back
43b982ea 205
206=head2 pod_comment_spillover_length
207
208When pod_comment_mode is set to C<auto>, this is the length of the comment at
209which it will be forced into a separate description section.
210
211The default is C<60>
212
c8c27020 213=head2 relationship_attrs
214
215Hashref of attributes to pass to each generated relationship, listed
216by type. Also supports relationship type 'all', containing options to
217pass to all generated relationships. Attributes set for more specific
218relationship types override those set in 'all'.
219
220For example:
221
222 relationship_attrs => {
223 all => { cascade_delete => 0 },
224 has_many => { cascade_delete => 1 },
225 },
226
227will set the C<cascade_delete> option to 0 for all generated relationships,
228except for C<has_many>, which will have cascade_delete as 1.
229
230NOTE: this option is not supported if v4 backward-compatible naming is
231set either globally (naming => 'v4') or just for relationships.
232
996be9ee 233=head2 debug
234
235If set to true, each constructive L<DBIx::Class> statement the loader
236decides to execute will be C<warn>-ed before execution.
237
d65cda9e 238=head2 db_schema
239
240Set the name of the schema to load (schema in the sense that your database
241vendor means it). Does not currently support loading more than one schema
242name.
243
996be9ee 244=head2 constraint
245
246Only load tables matching regex. Best specified as a qr// regex.
247
248=head2 exclude
249
250Exclude tables matching regex. Best specified as a qr// regex.
251
252=head2 moniker_map
253
8f9d7ce5 254Overrides the default table name to moniker translation. Can be either
255a hashref of table keys and moniker values, or a coderef for a translator
996be9ee 256function taking a single scalar table name argument and returning
257a scalar moniker. If the hash entry does not exist, or the function
258returns a false value, the code falls back to default behavior
259for that table name.
260
9cc8e7e1 261The default behavior is to singularize the table name, and: C<join '', map
262ucfirst, split /[\W_]+/, lc $table>, which is to say: lowercase everything,
263split up the table name into chunks anywhere a non-alpha-numeric character
264occurs, change the case of first letter of each chunk to upper case, and put
265the chunks back together. Examples:
996be9ee 266
267 Table Name | Moniker Name
268 ---------------------------
269 luser | Luser
270 luser_group | LuserGroup
48b8d687 271 luser-opts | LuserOpt
996be9ee 272
273=head2 inflect_plural
274
275Just like L</moniker_map> above (can be hash/code-ref, falls back to default
276if hash key does not exist or coderef returns false), but acts as a map
277for pluralizing relationship names. The default behavior is to utilize
278L<Lingua::EN::Inflect::Number/to_PL>.
279
280=head2 inflect_singular
281
282As L</inflect_plural> above, but for singularizing relationship names.
283Default behavior is to utilize L<Lingua::EN::Inflect::Number/to_S>.
284
9c9c2f2b 285=head2 schema_base_class
286
287Base class for your schema classes. Defaults to 'DBIx::Class::Schema'.
288
289=head2 result_base_class
290
2229729e 291Base class for your table classes (aka result classes). Defaults to
292'DBIx::Class::Core'.
9c9c2f2b 293
996be9ee 294=head2 additional_base_classes
295
296List of additional base classes all of your table classes will use.
297
298=head2 left_base_classes
299
300List of additional base classes all of your table classes will use
301that need to be leftmost.
302
303=head2 additional_classes
304
305List of additional classes which all of your table classes will use.
306
307=head2 components
308
309List of additional components to be loaded into all of your table
310classes. A good example would be C<ResultSetManager>.
311
312=head2 resultset_components
313
8f9d7ce5 314List of additional ResultSet components to be loaded into your table
996be9ee 315classes. A good example would be C<AlwaysRS>. Component
316C<ResultSetManager> will be automatically added to the above
317C<components> list if this option is set.
318
f44ecc2f 319=head2 use_namespaces
320
f22644d7 321This is now the default, to go back to L<DBIx::Class::Schema/load_classes> pass
322a C<0>.
323
f44ecc2f 324Generate result class names suitable for
325L<DBIx::Class::Schema/load_namespaces> and call that instead of
326L<DBIx::Class::Schema/load_classes>. When using this option you can also
327specify any of the options for C<load_namespaces> (i.e. C<result_namespace>,
328C<resultset_namespace>, C<default_resultset_class>), and they will be added
329to the call (and the generated result class names adjusted appropriately).
330
996be9ee 331=head2 dump_directory
332
333This option is designed to be a tool to help you transition from this
334loader to a manually-defined schema when you decide it's time to do so.
335
336The value of this option is a perl libdir pathname. Within
337that directory this module will create a baseline manual
338L<DBIx::Class::Schema> module set, based on what it creates at runtime
339in memory.
340
341The created schema class will have the same classname as the one on
342which you are setting this option (and the ResultSource classes will be
7cab3ab7 343based on this name as well).
996be9ee 344
8f9d7ce5 345Normally you wouldn't hard-code this setting in your schema class, as it
996be9ee 346is meant for one-time manual usage.
347
348See L<DBIx::Class::Schema::Loader/dump_to_dir> for examples of the
349recommended way to access this functionality.
350
d65cda9e 351=head2 dump_overwrite
352
28b4691d 353Deprecated. See L</really_erase_my_files> below, which does *not* mean
354the same thing as the old C<dump_overwrite> setting from previous releases.
355
356=head2 really_erase_my_files
357
7cab3ab7 358Default false. If true, Loader will unconditionally delete any existing
359files before creating the new ones from scratch when dumping a schema to disk.
360
361The default behavior is instead to only replace the top portion of the
362file, up to and including the final stanza which contains
363C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!>
364leaving any customizations you placed after that as they were.
365
28b4691d 366When C<really_erase_my_files> is not set, if the output file already exists,
7cab3ab7 367but the aforementioned final stanza is not found, or the checksum
368contained there does not match the generated contents, Loader will
369croak and not touch the file.
d65cda9e 370
28b4691d 371You should really be using version control on your schema classes (and all
372of the rest of your code for that matter). Don't blame me if a bug in this
373code wipes something out when it shouldn't have, you've been warned.
374
639a1367 375=head2 overwrite_modifications
376
377Default false. If false, when updating existing files, Loader will
378refuse to modify any Loader-generated code that has been modified
379since its last run (as determined by the checksum Loader put in its
380comment lines).
381
382If true, Loader will discard any manual modifications that have been
383made to Loader-generated code.
384
385Again, you should be using version control on your schema classes. Be
386careful with this option.
387
3a368709 388=head2 custom_column_info
389
d67d058e 390Hook for adding extra attributes to the
391L<column_info|DBIx::Class::ResultSource/column_info> for a column.
392
393Must be a coderef that returns a hashref with the extra attributes.
394
395Receives the table name, column name and column_info.
396
397For example:
398
399 custom_column_info => sub {
400 my ($table_name, $column_name, $column_info) = @_;
401
402 if ($column_name eq 'dog' && $column_info->{default_value} eq 'snoopy') {
403 return { is_snoopy => 1 };
404 }
405 },
3a368709 406
d67d058e 407This attribute can also be used to set C<inflate_datetime> on a non-datetime
408column so it also receives the L</datetime_timezone> and/or L</datetime_locale>.
3a368709 409
42e785fa 410=head2 datetime_timezone
411
d67d058e 412Sets the timezone attribute for L<DBIx::Class::InflateColumn::DateTime> for all
413columns with the DATE/DATETIME/TIMESTAMP data_types.
42e785fa 414
415=head2 datetime_locale
416
d67d058e 417Sets the locale attribute for L<DBIx::Class::InflateColumn::DateTime> for all
418columns with the DATE/DATETIME/TIMESTAMP data_types.
42e785fa 419
996be9ee 420=head1 METHODS
421
422None of these methods are intended for direct invocation by regular
d67d058e 423users of L<DBIx::Class::Schema::Loader>. Some are proxied via
424L<DBIx::Class::Schema::Loader>.
996be9ee 425
426=cut
427
8048320c 428use constant CURRENT_V => 'v5';
429
430use constant CLASS_ARGS => qw(
431 schema_base_class result_base_class additional_base_classes
432 left_base_classes additional_classes components resultset_components
433);
66afce69 434
996be9ee 435# ensure that a peice of object data is a valid arrayref, creating
436# an empty one or encapsulating whatever's there.
437sub _ensure_arrayref {
438 my $self = shift;
439
440 foreach (@_) {
441 $self->{$_} ||= [];
442 $self->{$_} = [ $self->{$_} ]
443 unless ref $self->{$_} eq 'ARRAY';
444 }
445}
446
447=head2 new
448
449Constructor for L<DBIx::Class::Schema::Loader::Base>, used internally
450by L<DBIx::Class::Schema::Loader>.
451
452=cut
453
454sub new {
455 my ( $class, %args ) = @_;
456
457 my $self = { %args };
458
459 bless $self => $class;
460
996be9ee 461 $self->_ensure_arrayref(qw/additional_classes
462 additional_base_classes
463 left_base_classes
464 components
465 resultset_components
466 /);
467
8048320c 468 $self->_validate_class_args;
469
996be9ee 470 push(@{$self->{components}}, 'ResultSetManager')
471 if @{$self->{resultset_components}};
472
473 $self->{monikers} = {};
474 $self->{classes} = {};
f53dcdf0 475 $self->{_upgrading_classes} = {};
996be9ee 476
996be9ee 477 $self->{schema_class} ||= ( ref $self->{schema} || $self->{schema} );
478 $self->{schema} ||= $self->{schema_class};
479
28b4691d 480 croak "dump_overwrite is deprecated. Please read the"
481 . " DBIx::Class::Schema::Loader::Base documentation"
482 if $self->{dump_overwrite};
483
af31090c 484 $self->{dynamic} = ! $self->{dump_directory};
79193756 485 $self->{temp_directory} ||= File::Temp::tempdir( 'dbicXXXX',
af31090c 486 TMPDIR => 1,
487 CLEANUP => 1,
488 );
489
79193756 490 $self->{dump_directory} ||= $self->{temp_directory};
491
01012543 492 $self->version_to_dump($DBIx::Class::Schema::Loader::VERSION);
1c95b304 493 $self->schema_version_to_dump($DBIx::Class::Schema::Loader::VERSION);
01012543 494
66afce69 495 if ((not ref $self->naming) && defined $self->naming) {
9cc8e7e1 496 my $naming_ver = $self->naming;
a8d229ff 497 $self->{naming} = {
498 relationships => $naming_ver,
499 monikers => $naming_ver,
500 };
501 }
502
66afce69 503 if ($self->naming) {
504 for (values %{ $self->naming }) {
505 $_ = CURRENT_V if $_ eq 'current';
506 }
507 }
508 $self->{naming} ||= {};
509
d67d058e 510 if ($self->custom_column_info && ref $self->custom_column_info ne 'CODE') {
511 croak 'custom_column_info must be a CODE ref';
512 }
513
7824616e 514 $self->_check_back_compat;
9c465d2c 515
f22644d7 516 $self->use_namespaces(1) unless defined $self->use_namespaces;
492dce8d 517 $self->generate_pod(1) unless defined $self->generate_pod;
43b982ea 518 $self->pod_comment_mode('auto') unless defined $self->pod_comment_mode;
519 $self->pod_comment_spillover_length(60) unless defined $self->pod_comment_spillover_length;
f22644d7 520
7824616e 521 $self;
522}
af31090c 523
7824616e 524sub _check_back_compat {
525 my ($self) = @_;
e8ad6491 526
a8d229ff 527# dynamic schemas will always be in 0.04006 mode, unless overridden
106a976a 528 if ($self->dynamic) {
fb3bb595 529# just in case, though no one is likely to dump a dynamic schema
1c95b304 530 $self->schema_version_to_dump('0.04006');
a8d229ff 531
66afce69 532 if (not %{ $self->naming }) {
533 warn <<EOF unless $ENV{SCHEMA_LOADER_BACKCOMPAT};
534
535Dynamic schema detected, will run in 0.04006 mode.
536
537Set the 'naming' attribute or the SCHEMA_LOADER_BACKCOMPAT environment variable
538to disable this warning.
a0e0a56a 539
805dbe0a 540Also consider setting 'use_namespaces => 1' if/when upgrading.
541
a0e0a56a 542See perldoc DBIx::Class::Schema::Loader::Manual::UpgradingFromV4 for more
543details.
66afce69 544EOF
545 }
f53dcdf0 546 else {
547 $self->_upgrading_from('v4');
548 }
66afce69 549
a8d229ff 550 $self->naming->{relationships} ||= 'v4';
551 $self->naming->{monikers} ||= 'v4';
552
805dbe0a 553 if ($self->use_namespaces) {
554 $self->_upgrading_from_load_classes(1);
555 }
556 else {
557 $self->use_namespaces(0);
558 }
f22644d7 559
01012543 560 return;
561 }
562
563# otherwise check if we need backcompat mode for a static schema
7824616e 564 my $filename = $self->_get_dump_filename($self->schema_class);
565 return unless -e $filename;
566
567 open(my $fh, '<', $filename)
568 or croak "Cannot open '$filename' for reading: $!";
569
540a8149 570 my $load_classes = 0;
571 my $result_namespace = '';
f22644d7 572
7824616e 573 while (<$fh>) {
f22644d7 574 if (/^__PACKAGE__->load_classes;/) {
575 $load_classes = 1;
540a8149 576 } elsif (/result_namespace => '([^']+)'/) {
577 $result_namespace = $1;
805dbe0a 578 } elsif (my ($real_ver) =
579 /^# Created by DBIx::Class::Schema::Loader v(\d+\.\d+)/) {
580
581 if ($load_classes && (not defined $self->use_namespaces)) {
582 warn <<"EOF" unless $ENV{SCHEMA_LOADER_BACKCOMPAT};
583
a1a91c42 584'load_classes;' static schema detected, turning off 'use_namespaces'.
805dbe0a 585
586Set the 'use_namespaces' attribute or the SCHEMA_LOADER_BACKCOMPAT environment
587variable to disable this warning.
588
589See perldoc DBIx::Class::Schema::Loader::Manual::UpgradingFromV4 for more
590details.
591EOF
592 $self->use_namespaces(0);
593 }
594 elsif ($load_classes && $self->use_namespaces) {
805dbe0a 595 $self->_upgrading_from_load_classes(1);
596 }
540a8149 597 elsif ((not $load_classes) && defined $self->use_namespaces
598 && (not $self->use_namespaces)) {
a4b94090 599 $self->_downgrading_to_load_classes(
600 $result_namespace || 'Result'
601 );
602 }
540a8149 603 elsif ((not defined $self->use_namespaces)
604 || $self->use_namespaces) {
605 if (not $self->result_namespace) {
606 $self->result_namespace($result_namespace || 'Result');
607 }
608 elsif ($result_namespace ne $self->result_namespace) {
609 $self->_rewriting_result_namespace(
610 $result_namespace || 'Result'
611 );
612 }
613 }
a8d229ff 614
a8d229ff 615 # XXX when we go past .0 this will need fixing
616 my ($v) = $real_ver =~ /([1-9])/;
617 $v = "v$v";
618
b24cb177 619 last if $v eq CURRENT_V || $real_ver =~ /^0\.\d\d999/;
a0e0a56a 620
621 if (not %{ $self->naming }) {
622 warn <<"EOF" unless $ENV{SCHEMA_LOADER_BACKCOMPAT};
623
624Version $real_ver static schema detected, turning on backcompat mode.
625
626Set the 'naming' attribute or the SCHEMA_LOADER_BACKCOMPAT environment variable
627to disable this warning.
628
629See perldoc DBIx::Class::Schema::Loader::Manual::UpgradingFromV4 for more
630details.
631EOF
632 }
f53dcdf0 633 else {
634 $self->_upgrading_from($v);
68d49e50 635 last;
f53dcdf0 636 }
a0e0a56a 637
a8d229ff 638 $self->naming->{relationships} ||= $v;
639 $self->naming->{monikers} ||= $v;
640
a0e0a56a 641 $self->schema_version_to_dump($real_ver);
642
7824616e 643 last;
644 }
645 }
646 close $fh;
996be9ee 647}
648
8048320c 649sub _validate_class_args {
650 my $self = shift;
651 my $args = shift;
652
653 foreach my $k (CLASS_ARGS) {
654 next unless $self->$k;
655
656 my @classes = ref $self->$k eq 'ARRAY' ? @{ $self->$k } : $self->$k;
657 foreach my $c (@classes) {
658 # components default to being under the DBIx::Class namespace unless they
659 # are preceeded with a '+'
660 if ( $k =~ m/components$/ && $c !~ s/^\+// ) {
661 $c = 'DBIx::Class::' . $c;
662 }
663
664 # 1 == installed, 0 == not installed, undef == invalid classname
665 my $installed = Class::Inspector->installed($c);
666 if ( defined($installed) ) {
667 if ( $installed == 0 ) {
668 croak qq/$c, as specified in the loader option "$k", is not installed/;
669 }
670 } else {
671 croak qq/$c, as specified in the loader option "$k", is an invalid class name/;
672 }
673 }
674 }
675}
676
419a2eeb 677sub _find_file_in_inc {
678 my ($self, $file) = @_;
679
680 foreach my $prefix (@INC) {
af31090c 681 my $fullpath = File::Spec->catfile($prefix, $file);
682 return $fullpath if -f $fullpath
281d0f3e 683 # abs_path throws on Windows for nonexistant files
684 and eval { Cwd::abs_path($fullpath) } ne
685 (eval { Cwd::abs_path(File::Spec->catfile($self->dump_directory, $file)) } || '');
419a2eeb 686 }
687
688 return;
689}
690
fb3bb595 691sub _class_path {
f96ef30f 692 my ($self, $class) = @_;
693
694 my $class_path = $class;
695 $class_path =~ s{::}{/}g;
696 $class_path .= '.pm';
697
fb3bb595 698 return $class_path;
699}
700
701sub _find_class_in_inc {
702 my ($self, $class) = @_;
703
704 return $self->_find_file_in_inc($self->_class_path($class));
705}
706
a4b94090 707sub _rewriting {
708 my $self = shift;
709
710 return $self->_upgrading_from
711 || $self->_upgrading_from_load_classes
540a8149 712 || $self->_downgrading_to_load_classes
713 || $self->_rewriting_result_namespace
714 ;
a4b94090 715}
716
b24cb177 717sub _rewrite_old_classnames {
718 my ($self, $code) = @_;
719
a4b94090 720 return $code unless $self->_rewriting;
b24cb177 721
722 my %old_classes = reverse %{ $self->_upgrading_classes };
723
724 my $re = join '|', keys %old_classes;
725 $re = qr/\b($re)\b/;
726
68d49e50 727 $code =~ s/$re/$old_classes{$1} || $1/eg;
b24cb177 728
729 return $code;
730}
731
fb3bb595 732sub _load_external {
733 my ($self, $class) = @_;
734
0ca61324 735 return if $self->{skip_load_external};
736
ffc705f3 737 # so that we don't load our own classes, under any circumstances
738 local *INC = [ grep $_ ne $self->dump_directory, @INC ];
739
fb3bb595 740 my $real_inc_path = $self->_find_class_in_inc($class);
f96ef30f 741
ffc705f3 742 my $old_class = $self->_upgrading_classes->{$class}
a4b94090 743 if $self->_rewriting;
ffc705f3 744
745 my $old_real_inc_path = $self->_find_class_in_inc($old_class)
746 if $old_class && $old_class ne $class;
747
748 return unless $real_inc_path || $old_real_inc_path;
749
750 if ($real_inc_path) {
751 # If we make it to here, we loaded an external definition
752 warn qq/# Loaded external class definition for '$class'\n/
753 if $self->debug;
754
755 open(my $fh, '<', $real_inc_path)
756 or croak "Failed to open '$real_inc_path' for reading: $!";
b24cb177 757 my $code = do { local $/; <$fh> };
ffc705f3 758 close($fh)
759 or croak "Failed to close $real_inc_path: $!";
b24cb177 760 $code = $self->_rewrite_old_classnames($code);
ffc705f3 761
762 if ($self->dynamic) { # load the class too
763 # kill redefined warnings
502b65d4 764 my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
ffc705f3 765 local $SIG{__WARN__} = sub {
502b65d4 766 $warn_handler->(@_)
767 unless $_[0] =~ /^Subroutine \S+ redefined/;
ffc705f3 768 };
b24cb177 769 eval $code;
ffc705f3 770 die $@ if $@;
771 }
b24cb177 772
773 $self->_ext_stmt($class,
774 qq|# These lines were loaded from '$real_inc_path' found in \@INC.\n|
775 .qq|# They are now part of the custom portion of this file\n|
776 .qq|# for you to hand-edit. If you do not either delete\n|
777 .qq|# this section or remove that file from \@INC, this section\n|
778 .qq|# will be repeated redundantly when you re-create this\n|
e770e9ce 779 .qq|# file again via Loader! See skip_load_external to disable\n|
780 .qq|# this feature.\n|
b24cb177 781 );
782 chomp $code;
783 $self->_ext_stmt($class, $code);
784 $self->_ext_stmt($class,
785 qq|# End of lines loaded from '$real_inc_path' |
786 );
996be9ee 787 }
106a976a 788
ffc705f3 789 if ($old_real_inc_path) {
790 open(my $fh, '<', $old_real_inc_path)
791 or croak "Failed to open '$old_real_inc_path' for reading: $!";
792 $self->_ext_stmt($class, <<"EOF");
793
30a4c064 794# These lines were loaded from '$old_real_inc_path',
795# based on the Result class name that would have been created by an 0.04006
796# version of the Loader. For a static schema, this happens only once during
e770e9ce 797# upgrade. See skip_load_external to disable this feature.
ffc705f3 798EOF
b24cb177 799
800 my $code = do {
801 local ($/, @ARGV) = (undef, $old_real_inc_path); <>
802 };
803 $code = $self->_rewrite_old_classnames($code);
804
ffc705f3 805 if ($self->dynamic) {
806 warn <<"EOF";
807
808Detected external content in '$old_real_inc_path', a class name that would have
809been used by an 0.04006 version of the Loader.
810
811* PLEASE RENAME THIS CLASS: from '$old_class' to '$class', as that is the
812new name of the Result.
813EOF
814 # kill redefined warnings
502b65d4 815 my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
ffc705f3 816 local $SIG{__WARN__} = sub {
502b65d4 817 $warn_handler->(@_)
818 unless $_[0] =~ /^Subroutine \S+ redefined/;
ffc705f3 819 };
ffc705f3 820 eval $code;
821 die $@ if $@;
822 }
823
b24cb177 824 chomp $code;
825 $self->_ext_stmt($class, $code);
ffc705f3 826 $self->_ext_stmt($class,
827 qq|# End of lines loaded from '$old_real_inc_path' |
828 );
9e8033c1 829 }
996be9ee 830}
831
832=head2 load
833
834Does the actual schema-construction work.
835
836=cut
837
838sub load {
839 my $self = shift;
840
b97c2c1e 841 $self->_load_tables($self->_tables_list);
842}
843
844=head2 rescan
845
a60b5b8d 846Arguments: schema
847
b97c2c1e 848Rescan the database for newly added tables. Does
a60b5b8d 849not process drops or changes. Returns a list of
850the newly added table monikers.
851
852The schema argument should be the schema class
853or object to be affected. It should probably
854be derived from the original schema_class used
855during L</load>.
b97c2c1e 856
857=cut
858
859sub rescan {
a60b5b8d 860 my ($self, $schema) = @_;
861
862 $self->{schema} = $schema;
7824616e 863 $self->_relbuilder->{schema} = $schema;
b97c2c1e 864
865 my @created;
866 my @current = $self->_tables_list;
867 foreach my $table ($self->_tables_list) {
868 if(!exists $self->{_tables}->{$table}) {
869 push(@created, $table);
870 }
871 }
872
c39e3507 873 my $loaded = $self->_load_tables(@created);
a60b5b8d 874
c39e3507 875 return map { $self->monikers->{$_} } @$loaded;
b97c2c1e 876}
877
7824616e 878sub _relbuilder {
66afce69 879 no warnings 'uninitialized';
7824616e 880 my ($self) = @_;
3fed44ca 881
882 return if $self->{skip_relationships};
883
a8d229ff 884 if ($self->naming->{relationships} eq 'v4') {
885 require DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_040;
886 return $self->{relbuilder} ||=
887 DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_040->new(
888 $self->schema, $self->inflect_plural, $self->inflect_singular
889 );
890 }
891
c8c27020 892 $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder->new (
893 $self->schema,
894 $self->inflect_plural,
895 $self->inflect_singular,
896 $self->relationship_attrs,
7824616e 897 );
898}
899
b97c2c1e 900sub _load_tables {
901 my ($self, @tables) = @_;
902
f96ef30f 903 # First, use _tables_list with constraint and exclude
904 # to get a list of tables to operate on
905
906 my $constraint = $self->constraint;
907 my $exclude = $self->exclude;
f96ef30f 908
b97c2c1e 909 @tables = grep { /$constraint/ } @tables if $constraint;
910 @tables = grep { ! /$exclude/ } @tables if $exclude;
f96ef30f 911
b97c2c1e 912 # Save the new tables to the tables list
a60b5b8d 913 foreach (@tables) {
914 $self->{_tables}->{$_} = 1;
915 }
f96ef30f 916
af31090c 917 $self->_make_src_class($_) for @tables;
f96ef30f 918 $self->_setup_src_meta($_) for @tables;
919
e8ad6491 920 if(!$self->skip_relationships) {
181cc907 921 # The relationship loader needs a working schema
af31090c 922 $self->{quiet} = 1;
79193756 923 local $self->{dump_directory} = $self->{temp_directory};
106a976a 924 $self->_reload_classes(\@tables);
e8ad6491 925 $self->_load_relationships($_) for @tables;
af31090c 926 $self->{quiet} = 0;
79193756 927
928 # Remove that temp dir from INC so it doesn't get reloaded
ffc705f3 929 @INC = grep $_ ne $self->dump_directory, @INC;
e8ad6491 930 }
931
f96ef30f 932 $self->_load_external($_)
75451704 933 for map { $self->classes->{$_} } @tables;
f96ef30f 934
106a976a 935 # Reload without unloading first to preserve any symbols from external
936 # packages.
937 $self->_reload_classes(\@tables, 0);
996be9ee 938
5223f24a 939 # Drop temporary cache
940 delete $self->{_cache};
941
c39e3507 942 return \@tables;
996be9ee 943}
944
af31090c 945sub _reload_classes {
106a976a 946 my ($self, $tables, $unload) = @_;
947
948 my @tables = @$tables;
949 $unload = 1 unless defined $unload;
181cc907 950
4daef04f 951 # so that we don't repeat custom sections
952 @INC = grep $_ ne $self->dump_directory, @INC;
953
181cc907 954 $self->_dump_to_dir(map { $self->classes->{$_} } @tables);
e9b8719e 955
956 unshift @INC, $self->dump_directory;
af31090c 957
706ef173 958 my @to_register;
959 my %have_source = map { $_ => $self->schema->source($_) }
960 $self->schema->sources;
961
181cc907 962 for my $table (@tables) {
963 my $moniker = $self->monikers->{$table};
964 my $class = $self->classes->{$table};
0ae6b65d 965
966 {
967 no warnings 'redefine';
968 local *Class::C3::reinitialize = sub {};
969 use warnings;
970
106a976a 971 Class::Unload->unload($class) if $unload;
706ef173 972 my ($source, $resultset_class);
973 if (
974 ($source = $have_source{$moniker})
975 && ($resultset_class = $source->resultset_class)
976 && ($resultset_class ne 'DBIx::Class::ResultSet')
977 ) {
978 my $has_file = Class::Inspector->loaded_filename($resultset_class);
106a976a 979 Class::Unload->unload($resultset_class) if $unload;
980 $self->_reload_class($resultset_class) if $has_file;
0ae6b65d 981 }
106a976a 982 $self->_reload_class($class);
af31090c 983 }
706ef173 984 push @to_register, [$moniker, $class];
985 }
af31090c 986
706ef173 987 Class::C3->reinitialize;
988 for (@to_register) {
989 $self->schema->register_class(@$_);
af31090c 990 }
991}
992
106a976a 993# We use this instead of ensure_class_loaded when there are package symbols we
994# want to preserve.
995sub _reload_class {
996 my ($self, $class) = @_;
997
998 my $class_path = $self->_class_path($class);
999 delete $INC{ $class_path };
f53dcdf0 1000
1001# kill redefined warnings
502b65d4 1002 my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
f53dcdf0 1003 local $SIG{__WARN__} = sub {
502b65d4 1004 $warn_handler->(@_)
1005 unless $_[0] =~ /^Subroutine \S+ redefined/;
f53dcdf0 1006 };
106a976a 1007 eval "require $class;";
1008}
1009
996be9ee 1010sub _get_dump_filename {
1011 my ($self, $class) = (@_);
1012
1013 $class =~ s{::}{/}g;
1014 return $self->dump_directory . q{/} . $class . q{.pm};
1015}
1016
1017sub _ensure_dump_subdirs {
1018 my ($self, $class) = (@_);
1019
1020 my @name_parts = split(/::/, $class);
dd03ee1a 1021 pop @name_parts; # we don't care about the very last element,
1022 # which is a filename
1023
996be9ee 1024 my $dir = $self->dump_directory;
7cab3ab7 1025 while (1) {
1026 if(!-d $dir) {
25328cc4 1027 mkdir($dir) or croak "mkdir('$dir') failed: $!";
996be9ee 1028 }
7cab3ab7 1029 last if !@name_parts;
1030 $dir = File::Spec->catdir($dir, shift @name_parts);
996be9ee 1031 }
1032}
1033
1034sub _dump_to_dir {
af31090c 1035 my ($self, @classes) = @_;
996be9ee 1036
fc2b71fd 1037 my $schema_class = $self->schema_class;
9c9c2f2b 1038 my $schema_base_class = $self->schema_base_class || 'DBIx::Class::Schema';
996be9ee 1039
e9b8719e 1040 my $target_dir = $self->dump_directory;
af31090c 1041 warn "Dumping manual schema for $schema_class to directory $target_dir ...\n"
1042 unless $self->{dynamic} or $self->{quiet};
996be9ee 1043
7cab3ab7 1044 my $schema_text =
1045 qq|package $schema_class;\n\n|
b4dcbcc5 1046 . qq|# Created by DBIx::Class::Schema::Loader\n|
1047 . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
7cab3ab7 1048 . qq|use strict;\nuse warnings;\n\n|
9c9c2f2b 1049 . qq|use base '$schema_base_class';\n\n|;
f44ecc2f 1050
f44ecc2f 1051 if ($self->use_namespaces) {
1052 $schema_text .= qq|__PACKAGE__->load_namespaces|;
1053 my $namespace_options;
1054 for my $attr (qw(result_namespace
1055 resultset_namespace
1056 default_resultset_class)) {
1057 if ($self->$attr) {
1058 $namespace_options .= qq| $attr => '| . $self->$attr . qq|',\n|
1059 }
1060 }
1061 $schema_text .= qq|(\n$namespace_options)| if $namespace_options;
1062 $schema_text .= qq|;\n|;
1063 }
1064 else {
1065 $schema_text .= qq|__PACKAGE__->load_classes;\n|;
f44ecc2f 1066 }
996be9ee 1067
1c95b304 1068 {
1069 local $self->{version_to_dump} = $self->schema_version_to_dump;
68d49e50 1070 $self->_write_classfile($schema_class, $schema_text, 1);
1c95b304 1071 }
996be9ee 1072
2229729e 1073 my $result_base_class = $self->result_base_class || 'DBIx::Class::Core';
9c9c2f2b 1074
af31090c 1075 foreach my $src_class (@classes) {
7cab3ab7 1076 my $src_text =
1077 qq|package $src_class;\n\n|
b4dcbcc5 1078 . qq|# Created by DBIx::Class::Schema::Loader\n|
1079 . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
7cab3ab7 1080 . qq|use strict;\nuse warnings;\n\n|
9c9c2f2b 1081 . qq|use base '$result_base_class';\n\n|;
996be9ee 1082
7cab3ab7 1083 $self->_write_classfile($src_class, $src_text);
02356864 1084 }
996be9ee 1085
a4b94090 1086 # remove Result dir if downgrading from use_namespaces, and there are no
1087 # files left.
b5f1b43c 1088 if (my $result_ns = $self->_downgrading_to_load_classes
1089 || $self->_rewriting_result_namespace) {
540a8149 1090 my $result_namespace = $self->_result_namespace(
1091 $schema_class,
1092 $result_ns,
1093 );
a4b94090 1094
540a8149 1095 (my $result_dir = $result_namespace) =~ s{::}{/}g;
a4b94090 1096 $result_dir = $self->dump_directory . '/' . $result_dir;
1097
1098 unless (my @files = glob "$result_dir/*") {
1099 rmdir $result_dir;
1100 }
1101 }
1102
af31090c 1103 warn "Schema dump completed.\n" unless $self->{dynamic} or $self->{quiet};
1104
7cab3ab7 1105}
1106
79193756 1107sub _sig_comment {
1108 my ($self, $version, $ts) = @_;
1109 return qq|\n\n# Created by DBIx::Class::Schema::Loader|
1110 . qq| v| . $version
1111 . q| @ | . $ts
1112 . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
1113}
1114
7cab3ab7 1115sub _write_classfile {
68d49e50 1116 my ($self, $class, $text, $is_schema) = @_;
7cab3ab7 1117
1118 my $filename = $self->_get_dump_filename($class);
1119 $self->_ensure_dump_subdirs($class);
1120
28b4691d 1121 if (-f $filename && $self->really_erase_my_files) {
7cab3ab7 1122 warn "Deleting existing file '$filename' due to "
af31090c 1123 . "'really_erase_my_files' setting\n" unless $self->{quiet};
7cab3ab7 1124 unlink($filename);
1125 }
1126
79193756 1127 my ($custom_content, $old_md5, $old_ver, $old_ts) = $self->_get_custom_content($class, $filename);
17ca645f 1128
e1373c52 1129 if (my $old_class = $self->_upgrading_classes->{$class}) {
1130 my $old_filename = $self->_get_dump_filename($old_class);
f53dcdf0 1131
e1373c52 1132 my ($old_custom_content) = $self->_get_custom_content(
1133 $old_class, $old_filename, 0 # do not add default comment
1134 );
ffc705f3 1135
e1373c52 1136 $old_custom_content =~ s/\n\n# You can replace.*\n1;\n//;
f53dcdf0 1137
e1373c52 1138 if ($old_custom_content) {
1139 $custom_content =
1140 "\n" . $old_custom_content . "\n" . $custom_content;
f53dcdf0 1141 }
e1373c52 1142
1143 unlink $old_filename;
f53dcdf0 1144 }
1145
b24cb177 1146 $custom_content = $self->_rewrite_old_classnames($custom_content);
1147
7cab3ab7 1148 $text .= qq|$_\n|
1149 for @{$self->{_dump_storage}->{$class} || []};
1150
79193756 1151 # Check and see if the dump is infact differnt
1152
1153 my $compare_to;
1154 if ($old_md5) {
1155 $compare_to = $text . $self->_sig_comment($old_ver, $old_ts);
1156
1157
1158 if (Digest::MD5::md5_base64($compare_to) eq $old_md5) {
68d49e50 1159 return unless $self->_upgrading_from && $is_schema;
79193756 1160 }
1161 }
1162
1163 $text .= $self->_sig_comment(
01012543 1164 $self->version_to_dump,
79193756 1165 POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
1166 );
7cab3ab7 1167
1168 open(my $fh, '>', $filename)
1169 or croak "Cannot open '$filename' for writing: $!";
1170
1171 # Write the top half and its MD5 sum
a4476f41 1172 print $fh $text . Digest::MD5::md5_base64($text) . "\n";
7cab3ab7 1173
1174 # Write out anything loaded via external partial class file in @INC
1175 print $fh qq|$_\n|
1176 for @{$self->{_ext_storage}->{$class} || []};
1177
1eea4fb1 1178 # Write out any custom content the user has added
7cab3ab7 1179 print $fh $custom_content;
1180
1181 close($fh)
e9b8719e 1182 or croak "Error closing '$filename': $!";
7cab3ab7 1183}
1184
79193756 1185sub _default_custom_content {
1186 return qq|\n\n# You can replace this text with custom|
1187 . qq| content, and it will be preserved on regeneration|
1188 . qq|\n1;\n|;
1189}
1190
7cab3ab7 1191sub _get_custom_content {
ffc705f3 1192 my ($self, $class, $filename, $add_default) = @_;
1193
1194 $add_default = 1 unless defined $add_default;
7cab3ab7 1195
79193756 1196 return ($self->_default_custom_content) if ! -f $filename;
1197
7cab3ab7 1198 open(my $fh, '<', $filename)
1199 or croak "Cannot open '$filename' for reading: $!";
1200
1201 my $mark_re =
419a2eeb 1202 qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
7cab3ab7 1203
7cab3ab7 1204 my $buffer = '';
79193756 1205 my ($md5, $ts, $ver);
7cab3ab7 1206 while(<$fh>) {
79193756 1207 if(!$md5 && /$mark_re/) {
1208 $md5 = $2;
1209 my $line = $1;
1210
1211 # Pull out the previous version and timestamp
1212 ($ver, $ts) = $buffer =~ m/# Created by DBIx::Class::Schema::Loader v(.*?) @ (.*?)$/s;
1213
1214 $buffer .= $line;
b4cc5793 1215 croak "Checksum mismatch in '$filename', the auto-generated part of the file has been modified outside of this loader. Aborting.\nIf you want to overwrite these modifications, set the 'overwrite_modifications' loader option.\n"
639a1367 1216 if !$self->overwrite_modifications && Digest::MD5::md5_base64($buffer) ne $md5;
7cab3ab7 1217
1218 $buffer = '';
1219 }
1220 else {
1221 $buffer .= $_;
1222 }
996be9ee 1223 }
1224
28b4691d 1225 croak "Cannot not overwrite '$filename' without 'really_erase_my_files',"
419a2eeb 1226 . " it does not appear to have been generated by Loader"
79193756 1227 if !$md5;
5ef3c771 1228
79193756 1229 # Default custom content:
ffc705f3 1230 $buffer ||= $self->_default_custom_content if $add_default;
5ef3c771 1231
79193756 1232 return ($buffer, $md5, $ver, $ts);
996be9ee 1233}
1234
1235sub _use {
1236 my $self = shift;
1237 my $target = shift;
1238
1239 foreach (@_) {
cb54990b 1240 warn "$target: use $_;" if $self->debug;
996be9ee 1241 $self->_raw_stmt($target, "use $_;");
996be9ee 1242 }
1243}
1244
1245sub _inject {
1246 my $self = shift;
1247 my $target = shift;
1248 my $schema_class = $self->schema_class;
1249
af31090c 1250 my $blist = join(q{ }, @_);
1251 warn "$target: use base qw/ $blist /;" if $self->debug && @_;
1252 $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
996be9ee 1253}
1254
540a8149 1255sub _result_namespace {
1256 my ($self, $schema_class, $ns) = @_;
1257 my @result_namespace;
1258
1259 if ($ns =~ /^\+(.*)/) {
1260 # Fully qualified namespace
1261 @result_namespace = ($1)
1262 }
1263 else {
1264 # Relative namespace
1265 @result_namespace = ($schema_class, $ns);
1266 }
1267
1268 return wantarray ? @result_namespace : join '::', @result_namespace;
1269}
1270
f96ef30f 1271# Create class with applicable bases, setup monikers, etc
1272sub _make_src_class {
1273 my ($self, $table) = @_;
996be9ee 1274
a13b2803 1275 my $schema = $self->schema;
1276 my $schema_class = $self->schema_class;
996be9ee 1277
f96ef30f 1278 my $table_moniker = $self->_table2moniker($table);
f44ecc2f 1279 my @result_namespace = ($schema_class);
1280 if ($self->use_namespaces) {
1281 my $result_namespace = $self->result_namespace || 'Result';
540a8149 1282 @result_namespace = $self->_result_namespace(
1283 $schema_class,
1284 $result_namespace,
1285 );
f44ecc2f 1286 }
1287 my $table_class = join(q{::}, @result_namespace, $table_moniker);
996be9ee 1288
805dbe0a 1289 if ((my $upgrading_v = $self->_upgrading_from)
a4b94090 1290 || $self->_rewriting) {
805dbe0a 1291 local $self->naming->{monikers} = $upgrading_v
1292 if $upgrading_v;
1293
1294 my @result_namespace = @result_namespace;
a4b94090 1295 if ($self->_upgrading_from_load_classes) {
1296 @result_namespace = ($schema_class);
1297 }
1298 elsif (my $ns = $self->_downgrading_to_load_classes) {
540a8149 1299 @result_namespace = $self->_result_namespace(
1300 $schema_class,
1301 $ns,
1302 );
1303 }
1304 elsif ($ns = $self->_rewriting_result_namespace) {
1305 @result_namespace = $self->_result_namespace(
1306 $schema_class,
1307 $ns,
1308 );
a4b94090 1309 }
f53dcdf0 1310
1311 my $old_class = join(q{::}, @result_namespace,
1312 $self->_table2moniker($table));
1313
68d49e50 1314 $self->_upgrading_classes->{$table_class} = $old_class
1315 unless $table_class eq $old_class;
f53dcdf0 1316 }
1317
f96ef30f 1318 my $table_normalized = lc $table;
1319 $self->classes->{$table} = $table_class;
1320 $self->classes->{$table_normalized} = $table_class;
1321 $self->monikers->{$table} = $table_moniker;
1322 $self->monikers->{$table_normalized} = $table_moniker;
996be9ee 1323
f96ef30f 1324 $self->_use ($table_class, @{$self->additional_classes});
af31090c 1325 $self->_inject($table_class, @{$self->left_base_classes});
996be9ee 1326
2229729e 1327 if (my @components = @{ $self->components }) {
1328 $self->_dbic_stmt($table_class, 'load_components', @components);
1329 }
996be9ee 1330
f96ef30f 1331 $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
1332 if @{$self->resultset_components};
af31090c 1333 $self->_inject($table_class, @{$self->additional_base_classes});
f96ef30f 1334}
996be9ee 1335
af31090c 1336# Set up metadata (cols, pks, etc)
f96ef30f 1337sub _setup_src_meta {
1338 my ($self, $table) = @_;
996be9ee 1339
f96ef30f 1340 my $schema = $self->schema;
1341 my $schema_class = $self->schema_class;
a13b2803 1342
f96ef30f 1343 my $table_class = $self->classes->{$table};
1344 my $table_moniker = $self->monikers->{$table};
996be9ee 1345
ff30991a 1346 my $table_name = $table;
1347 my $name_sep = $self->schema->storage->sql_maker->name_sep;
1348
c177d483 1349 if ($name_sep && $table_name =~ /\Q$name_sep\E/) {
ff30991a 1350 $table_name = \ $self->_quote_table_name($table_name);
1351 }
1352
1353 $self->_dbic_stmt($table_class,'table',$table_name);
996be9ee 1354
f96ef30f 1355 my $cols = $self->_table_columns($table);
1356 my $col_info;
d67d058e 1357 eval { $col_info = $self->__columns_info_for($table) };
f96ef30f 1358 if($@) {
1359 $self->_dbic_stmt($table_class,'add_columns',@$cols);
1360 }
1361 else {
0906d55b 1362 if ($self->_is_case_sensitive) {
1363 for my $col (keys %$col_info) {
1364 $col_info->{$col}{accessor} = lc $col
1365 if $col ne lc($col);
1366 }
1367 } else {
1368 $col_info = { map { lc($_), $col_info->{$_} } keys %$col_info };
c9373b79 1369 }
1370
e7213f4f 1371 my $fks = $self->_table_fk_info($table);
565335e6 1372
e7213f4f 1373 for my $fkdef (@$fks) {
1374 for my $col (@{ $fkdef->{local_columns} }) {
565335e6 1375 $col_info->{$col}{is_foreign_key} = 1;
e7213f4f 1376 }
1377 }
f96ef30f 1378 $self->_dbic_stmt(
1379 $table_class,
1380 'add_columns',
565335e6 1381 map { $_, ($col_info->{$_}||{}) } @$cols
f96ef30f 1382 );
996be9ee 1383 }
1384
d70c335f 1385 my %uniq_tag; # used to eliminate duplicate uniqs
1386
f96ef30f 1387 my $pks = $self->_table_pk_info($table) || [];
1388 @$pks ? $self->_dbic_stmt($table_class,'set_primary_key',@$pks)
1389 : carp("$table has no primary key");
d70c335f 1390 $uniq_tag{ join("\0", @$pks) }++ if @$pks; # pk is a uniq
996be9ee 1391
f96ef30f 1392 my $uniqs = $self->_table_uniq_info($table) || [];
d70c335f 1393 for (@$uniqs) {
1394 my ($name, $cols) = @$_;
1395 next if $uniq_tag{ join("\0", @$cols) }++; # skip duplicates
1396 $self->_dbic_stmt($table_class,'add_unique_constraint', $name, $cols);
1397 }
1398
996be9ee 1399}
1400
d67d058e 1401sub __columns_info_for {
1402 my ($self, $table) = @_;
1403
1404 my $result = $self->_columns_info_for($table);
1405
1406 while (my ($col, $info) = each %$result) {
1407 $info = { %$info, %{ $self->_custom_column_info ($table, $col, $info) } };
1408 $info = { %$info, %{ $self->_datetime_column_info($table, $col, $info) } };
1409
1410 $result->{$col} = $info;
1411 }
1412
1413 return $result;
1414}
1415
996be9ee 1416=head2 tables
1417
1418Returns a sorted list of loaded tables, using the original database table
1419names.
1420
1421=cut
1422
1423sub tables {
1424 my $self = shift;
1425
b97c2c1e 1426 return keys %{$self->_tables};
996be9ee 1427}
1428
1429# Make a moniker from a table
c39e403e 1430sub _default_table2moniker {
66afce69 1431 no warnings 'uninitialized';
c39e403e 1432 my ($self, $table) = @_;
1433
a8d229ff 1434 if ($self->naming->{monikers} eq 'v4') {
1435 return join '', map ucfirst, split /[\W_]+/, lc $table;
1436 }
1437
c39e403e 1438 return join '', map ucfirst, split /[\W_]+/,
1439 Lingua::EN::Inflect::Number::to_S(lc $table);
1440}
1441
996be9ee 1442sub _table2moniker {
1443 my ( $self, $table ) = @_;
1444
1445 my $moniker;
1446
1447 if( ref $self->moniker_map eq 'HASH' ) {
1448 $moniker = $self->moniker_map->{$table};
1449 }
1450 elsif( ref $self->moniker_map eq 'CODE' ) {
1451 $moniker = $self->moniker_map->($table);
1452 }
1453
c39e403e 1454 $moniker ||= $self->_default_table2moniker($table);
996be9ee 1455
1456 return $moniker;
1457}
1458
1459sub _load_relationships {
e8ad6491 1460 my ($self, $table) = @_;
996be9ee 1461
e8ad6491 1462 my $tbl_fk_info = $self->_table_fk_info($table);
1463 foreach my $fkdef (@$tbl_fk_info) {
1464 $fkdef->{remote_source} =
1465 $self->monikers->{delete $fkdef->{remote_table}};
996be9ee 1466 }
26f1c8c9 1467 my $tbl_uniq_info = $self->_table_uniq_info($table);
996be9ee 1468
e8ad6491 1469 my $local_moniker = $self->monikers->{$table};
7824616e 1470 my $rel_stmts = $self->_relbuilder->generate_code($local_moniker, $tbl_fk_info, $tbl_uniq_info);
996be9ee 1471
996be9ee 1472 foreach my $src_class (sort keys %$rel_stmts) {
1473 my $src_stmts = $rel_stmts->{$src_class};
1474 foreach my $stmt (@$src_stmts) {
1475 $self->_dbic_stmt($src_class,$stmt->{method},@{$stmt->{args}});
1476 }
1477 }
1478}
1479
1480# Overload these in driver class:
1481
1482# Returns an arrayref of column names
1483sub _table_columns { croak "ABSTRACT METHOD" }
1484
1485# Returns arrayref of pk col names
1486sub _table_pk_info { croak "ABSTRACT METHOD" }
1487
1488# Returns an arrayref of uniqs [ [ foo => [ col1, col2 ] ], [ bar => [ ... ] ] ]
1489sub _table_uniq_info { croak "ABSTRACT METHOD" }
1490
1491# Returns an arrayref of foreign key constraints, each
1492# being a hashref with 3 keys:
1493# local_columns (arrayref), remote_columns (arrayref), remote_table
1494sub _table_fk_info { croak "ABSTRACT METHOD" }
1495
1496# Returns an array of lower case table names
1497sub _tables_list { croak "ABSTRACT METHOD" }
1498
1499# Execute a constructive DBIC class method, with debug/dump_to_dir hooks.
1500sub _dbic_stmt {
bf654ab9 1501 my $self = shift;
1502 my $class = shift;
996be9ee 1503 my $method = shift;
bf654ab9 1504
1505 # generate the pod for this statement, storing it with $self->_pod
43b982ea 1506 $self->_make_pod( $class, $method, @_ ) if $self->generate_pod;
bf654ab9 1507
1508 my $args = dump(@_);
1509 $args = '(' . $args . ')' if @_ < 2;
1510 my $stmt = $method . $args . q{;};
1511
1512 warn qq|$class\->$stmt\n| if $self->debug;
1513 $self->_raw_stmt($class, '__PACKAGE__->' . $stmt);
1514 return;
1515}
1516
1517# generates the accompanying pod for a DBIC class method statement,
1518# storing it with $self->_pod
1519sub _make_pod {
1520 my $self = shift;
1521 my $class = shift;
1522 my $method = shift;
1523
fbcfebdd 1524 if ( $method eq 'table' ) {
1525 my ($table) = @_;
43b982ea 1526 my $pcm = $self->pod_comment_mode;
1527 my ($comment, $comment_overflows, $comment_in_name, $comment_in_desc);
fbcfebdd 1528 if ( $self->can('_table_comment') ) {
43b982ea 1529 $comment = $self->_table_comment($table);
1530 $comment_overflows = ($comment and length $comment > $self->pod_comment_spillover_length);
1531 $comment_in_name = ($pcm eq 'name' or ($pcm eq 'auto' and !$comment_overflows));
1532 $comment_in_desc = ($pcm eq 'description' or ($pcm eq 'auto' and $comment_overflows));
fbcfebdd 1533 }
43b982ea 1534 $self->_pod( $class, "=head1 NAME" );
1535 my $table_descr = $class;
1536 $table_descr .= " - " . $comment if $comment and $comment_in_name;
fbcfebdd 1537 $self->{_class2table}{ $class } = $table;
1538 $self->_pod( $class, $table_descr );
43b982ea 1539 if ($comment and $comment_in_desc) {
1540 $self->_pod( $class, "=head1 DESCRIPTION" );
1541 $self->_pod( $class, $comment );
1542 }
fbcfebdd 1543 $self->_pod_cut( $class );
1544 } elsif ( $method eq 'add_columns' ) {
1545 $self->_pod( $class, "=head1 ACCESSORS" );
79a00530 1546 my $col_counter = 0;
1547 my @cols = @_;
1548 while( my ($name,$attrs) = splice @cols,0,2 ) {
1549 $col_counter++;
1550 $self->_pod( $class, '=head2 ' . $name );
1551 $self->_pod( $class,
1552 join "\n", map {
1553 my $s = $attrs->{$_};
fca5431b 1554 $s = !defined $s ? 'undef' :
1555 length($s) == 0 ? '(empty string)' :
f170d55b 1556 ref($s) eq 'SCALAR' ? $$s :
1557 ref($s) ? do {
1558 my $dd = Dumper;
1559 $dd->Indent(0);
1560 $dd->Values([$s]);
1561 $dd->Dump;
1562 } :
1563 looks_like_number($s) ? $s :
1564 qq{'$s'}
fca5431b 1565 ;
79a00530 1566
1567 " $_: $s"
1568 } sort keys %$attrs,
1569 );
1570
1571 if( $self->can('_column_comment')
1572 and my $comment = $self->_column_comment( $self->{_class2table}{$class}, $col_counter)
1573 ) {
1574 $self->_pod( $class, $comment );
1575 }
fbcfebdd 1576 }
1577 $self->_pod_cut( $class );
1578 } elsif ( $method =~ /^(belongs_to|has_many|might_have)$/ ) {
1579 $self->_pod( $class, "=head1 RELATIONS" ) unless $self->{_relations_started} { $class } ;
1580 my ( $accessor, $rel_class ) = @_;
1581 $self->_pod( $class, "=head2 $accessor" );
1582 $self->_pod( $class, 'Type: ' . $method );
1583 $self->_pod( $class, "Related object: L<$rel_class>" );
1584 $self->_pod_cut( $class );
1585 $self->{_relations_started} { $class } = 1;
1586 }
996be9ee 1587}
1588
fbcfebdd 1589# Stores a POD documentation
1590sub _pod {
1591 my ($self, $class, $stmt) = @_;
1592 $self->_raw_stmt( $class, "\n" . $stmt );
1593}
1594
1595sub _pod_cut {
1596 my ($self, $class ) = @_;
1597 $self->_raw_stmt( $class, "\n=cut\n" );
1598}
1599
996be9ee 1600# Store a raw source line for a class (for dumping purposes)
1601sub _raw_stmt {
1602 my ($self, $class, $stmt) = @_;
af31090c 1603 push(@{$self->{_dump_storage}->{$class}}, $stmt);
996be9ee 1604}
1605
7cab3ab7 1606# Like above, but separately for the externally loaded stuff
1607sub _ext_stmt {
1608 my ($self, $class, $stmt) = @_;
af31090c 1609 push(@{$self->{_ext_storage}->{$class}}, $stmt);
7cab3ab7 1610}
1611
565335e6 1612sub _quote_table_name {
1613 my ($self, $table) = @_;
1614
1615 my $qt = $self->schema->storage->sql_maker->quote_char;
1616
c177d483 1617 return $table unless $qt;
1618
565335e6 1619 if (ref $qt) {
1620 return $qt->[0] . $table . $qt->[1];
1621 }
1622
1623 return $qt . $table . $qt;
1624}
1625
1626sub _is_case_sensitive { 0 }
1627
b639d969 1628sub _custom_column_info {
23d1f36b 1629 my ( $self, $table_name, $column_name, $column_info ) = @_;
b639d969 1630
d67d058e 1631 if (my $code = $self->custom_column_info) {
1632 return $code->($table_name, $column_name, $column_info) || {};
b639d969 1633 }
3a368709 1634 return {};
b639d969 1635}
1636
42e785fa 1637sub _datetime_column_info {
23d1f36b 1638 my ( $self, $table_name, $column_name, $column_info ) = @_;
d67d058e 1639 my $result = {};
1640 my $type = $column_info->{data_type} || '';
1641 if ((grep $_, @{ $column_info }{map "inflate_$_", qw/date datetime timestamp/})
1642 or ($type =~ /date|timestamp/i)) {
1643 $result->{timezone} = $self->datetime_timezone if $self->datetime_timezone;
1644 $result->{locale} = $self->datetime_locale if $self->datetime_locale;
42e785fa 1645 }
d67d058e 1646 return $result;
42e785fa 1647}
1648
ffc705f3 1649# remove the dump dir from @INC on destruction
1650sub DESTROY {
1651 my $self = shift;
1652
1653 @INC = grep $_ ne $self->dump_directory, @INC;
1654}
1655
996be9ee 1656=head2 monikers
1657
8f9d7ce5 1658Returns a hashref of loaded table to moniker mappings. There will
996be9ee 1659be two entries for each table, the original name and the "normalized"
1660name, in the case that the two are different (such as databases
1661that like uppercase table names, or preserve your original mixed-case
1662definitions, or what-have-you).
1663
1664=head2 classes
1665
8f9d7ce5 1666Returns a hashref of table to class mappings. In some cases it will
996be9ee 1667contain multiple entries per table for the original and normalized table
1668names, as above in L</monikers>.
1669
1670=head1 SEE ALSO
1671
1672L<DBIx::Class::Schema::Loader>
1673
be80bba7 1674=head1 AUTHOR
1675
9cc8e7e1 1676See L<DBIx::Class::Schema::Loader/AUTHOR> and L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
be80bba7 1677
1678=head1 LICENSE
1679
1680This library is free software; you can redistribute it and/or modify it under
1681the same terms as Perl itself.
1682
996be9ee 1683=cut
1684
16851;