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