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