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