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