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