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