1 package DBIx::Class::Schema::Loader::Base;
5 use base qw/Class::Accessor::Grouped Class::C3::Componentised/;
6 use namespace::autoclean;
8 use Carp::Clan qw/^DBIx::Class/;
9 use DBIx::Class::Schema::Loader::RelBuilder;
10 use Data::Dump qw/ dump /;
15 use Lingua::EN::Inflect::Number qw//;
16 use Lingua::EN::Inflect::Phrase qw//;
19 use Class::Inspector ();
20 use Data::Dumper::Concise;
21 use Scalar::Util 'looks_like_number';
22 use File::Slurp 'slurp';
25 our $VERSION = '0.07000';
27 __PACKAGE__->mk_group_ro_accessors('simple', qw/
34 additional_base_classes
49 default_resultset_class
52 overwrite_modifications
70 __PACKAGE__->mk_group_accessors('simple', qw/
72 schema_version_to_dump
74 _upgrading_from_load_classes
75 _downgrading_to_load_classes
76 _rewriting_result_namespace
81 pod_comment_spillover_length
86 DBIx::Class::Schema::Loader::Base - Base DBIx::Class::Schema::Loader Implementation.
90 See L<DBIx::Class::Schema::Loader>
94 This is the base class for the storage-specific C<DBIx::Class::Schema::*>
95 classes, and implements the common functionality between them.
97 =head1 CONSTRUCTOR OPTIONS
99 These constructor options are the base options for
100 L<DBIx::Class::Schema::Loader/loader_options>. Available constructor options are:
102 =head2 skip_relationships
104 Skip setting up relationships. The default is to attempt the loading
107 =head2 skip_load_external
109 Skip loading of other classes in @INC. The default is to merge all other classes
110 with the same name found in @INC into the schema file we are creating.
114 Static schemas (ones dumped to disk) will, by default, use the new-style
115 relationship names and singularized Results, unless you're overwriting an
116 existing dump made by an older version of L<DBIx::Class::Schema::Loader>, in
117 which case the backward compatible RelBuilder will be activated, and the
118 appropriate monikerization used.
124 will disable the backward-compatible RelBuilder and use
125 the new-style relationship names along with singularized Results, even when
126 overwriting a dump made with an earlier version.
128 The option also takes a hashref:
130 naming => { relationships => 'v6', monikers => 'v6' }
138 How to name relationship accessors.
142 How to name Result classes.
152 Latest style, whatever that happens to be.
156 Unsingularlized monikers, C<has_many> only relationships with no _id stripping.
160 Monikers singularized as whole words, C<might_have> relationships for FKs on
161 C<UNIQUE> constraints, C<_id> stripping for belongs_to relationships.
163 Some of the C<_id> stripping edge cases in C<0.05003> have been reverted for
168 All monikers and relationships inflected using L<Lingua::EN::Inflect::Phrase>,
169 more aggressive C<_id> stripping from relationships.
171 In general, there is very little difference between v5 and v6 schemas.
175 This mode is identical to C<v6> mode, except that monikerization of CamelCase
176 table names is also done correctly.
178 If you don't have any CamelCase table names, you can upgrade without breaking
183 Dynamic schemas will always default to the 0.04XXX relationship names and won't
184 singularize Results for backward compatibility, to activate the new RelBuilder
185 and singularization put this in your C<Schema.pm> file:
187 __PACKAGE__->naming('current');
189 Or if you prefer to use 0.05XXX features but insure that nothing breaks in the
190 next major version upgrade:
192 __PACKAGE__->naming('v5');
196 By default POD will be generated for columns and relationships, using database
197 metadata for the text if available and supported.
199 Reading database metadata (e.g. C<COMMENT ON TABLE some_table ...>) is only
200 supported for Postgres right now.
202 Set this to C<0> to turn off all POD generation.
204 =head2 pod_comment_mode
206 Controls where table comments appear in the generated POD. Smaller table
207 comments are appended to the C<NAME> section of the documentation, and larger
208 ones are inserted into C<DESCRIPTION> instead. You can force a C<DESCRIPTION>
209 section to be generated with the comment always, only use C<NAME>, or choose
210 the length threshold at which the comment is forced into the description.
216 Use C<NAME> section only.
220 Force C<DESCRIPTION> always.
224 Use C<DESCRIPTION> if length > L</pod_comment_spillover_length>, this is the
229 =head2 pod_comment_spillover_length
231 When pod_comment_mode is set to C<auto>, this is the length of the comment at
232 which it will be forced into a separate description section.
236 =head2 relationship_attrs
238 Hashref of attributes to pass to each generated relationship, listed
239 by type. Also supports relationship type 'all', containing options to
240 pass to all generated relationships. Attributes set for more specific
241 relationship types override those set in 'all'.
245 relationship_attrs => {
246 all => { cascade_delete => 0 },
247 has_many => { cascade_delete => 1 },
250 will set the C<cascade_delete> option to 0 for all generated relationships,
251 except for C<has_many>, which will have cascade_delete as 1.
253 NOTE: this option is not supported if v4 backward-compatible naming is
254 set either globally (naming => 'v4') or just for relationships.
258 If set to true, each constructive L<DBIx::Class> statement the loader
259 decides to execute will be C<warn>-ed before execution.
263 Set the name of the schema to load (schema in the sense that your database
264 vendor means it). Does not currently support loading more than one schema
269 Only load tables matching regex. Best specified as a qr// regex.
273 Exclude tables matching regex. Best specified as a qr// regex.
277 Overrides the default table name to moniker translation. Can be either
278 a hashref of table keys and moniker values, or a coderef for a translator
279 function taking a single scalar table name argument and returning
280 a scalar moniker. If the hash entry does not exist, or the function
281 returns a false value, the code falls back to default behavior
284 The default behavior is to split on case transition and non-alphanumeric
285 boundaries, singularize the resulting phrase, then join the titlecased words
288 Table Name | Moniker Name
289 ---------------------------------
291 luser_group | LuserGroup
292 luser-opts | LuserOpt
293 stations_visited | StationVisited
294 routeChange | RouteChange
296 =head2 inflect_plural
298 Just like L</moniker_map> above (can be hash/code-ref, falls back to default
299 if hash key does not exist or coderef returns false), but acts as a map
300 for pluralizing relationship names. The default behavior is to utilize
301 L<Lingua::EN::Inflect::Number/to_PL>.
303 =head2 inflect_singular
305 As L</inflect_plural> above, but for singularizing relationship names.
306 Default behavior is to utilize L<Lingua::EN::Inflect::Number/to_S>.
308 =head2 schema_base_class
310 Base class for your schema classes. Defaults to 'DBIx::Class::Schema'.
312 =head2 result_base_class
314 Base class for your table classes (aka result classes). Defaults to
317 =head2 additional_base_classes
319 List of additional base classes all of your table classes will use.
321 =head2 left_base_classes
323 List of additional base classes all of your table classes will use
324 that need to be leftmost.
326 =head2 additional_classes
328 List of additional classes which all of your table classes will use.
332 List of additional components to be loaded into all of your table
333 classes. A good example would be C<ResultSetManager>.
335 =head2 resultset_components
337 List of additional ResultSet components to be loaded into your table
338 classes. A good example would be C<AlwaysRS>. Component
339 C<ResultSetManager> will be automatically added to the above
340 C<components> list if this option is set.
342 =head2 use_namespaces
344 This is now the default, to go back to L<DBIx::Class::Schema/load_classes> pass
347 Generate result class names suitable for
348 L<DBIx::Class::Schema/load_namespaces> and call that instead of
349 L<DBIx::Class::Schema/load_classes>. When using this option you can also
350 specify any of the options for C<load_namespaces> (i.e. C<result_namespace>,
351 C<resultset_namespace>, C<default_resultset_class>), and they will be added
352 to the call (and the generated result class names adjusted appropriately).
354 =head2 dump_directory
356 This option is designed to be a tool to help you transition from this
357 loader to a manually-defined schema when you decide it's time to do so.
359 The value of this option is a perl libdir pathname. Within
360 that directory this module will create a baseline manual
361 L<DBIx::Class::Schema> module set, based on what it creates at runtime
364 The created schema class will have the same classname as the one on
365 which you are setting this option (and the ResultSource classes will be
366 based on this name as well).
368 Normally you wouldn't hard-code this setting in your schema class, as it
369 is meant for one-time manual usage.
371 See L<DBIx::Class::Schema::Loader/dump_to_dir> for examples of the
372 recommended way to access this functionality.
374 =head2 dump_overwrite
376 Deprecated. See L</really_erase_my_files> below, which does *not* mean
377 the same thing as the old C<dump_overwrite> setting from previous releases.
379 =head2 really_erase_my_files
381 Default false. If true, Loader will unconditionally delete any existing
382 files before creating the new ones from scratch when dumping a schema to disk.
384 The default behavior is instead to only replace the top portion of the
385 file, up to and including the final stanza which contains
386 C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!>
387 leaving any customizations you placed after that as they were.
389 When C<really_erase_my_files> is not set, if the output file already exists,
390 but the aforementioned final stanza is not found, or the checksum
391 contained there does not match the generated contents, Loader will
392 croak and not touch the file.
394 You should really be using version control on your schema classes (and all
395 of the rest of your code for that matter). Don't blame me if a bug in this
396 code wipes something out when it shouldn't have, you've been warned.
398 =head2 overwrite_modifications
400 Default false. If false, when updating existing files, Loader will
401 refuse to modify any Loader-generated code that has been modified
402 since its last run (as determined by the checksum Loader put in its
405 If true, Loader will discard any manual modifications that have been
406 made to Loader-generated code.
408 Again, you should be using version control on your schema classes. Be
409 careful with this option.
411 =head2 custom_column_info
413 Hook for adding extra attributes to the
414 L<column_info|DBIx::Class::ResultSource/column_info> for a column.
416 Must be a coderef that returns a hashref with the extra attributes.
418 Receives the table name, column name and column_info.
422 custom_column_info => sub {
423 my ($table_name, $column_name, $column_info) = @_;
425 if ($column_name eq 'dog' && $column_info->{default_value} eq 'snoopy') {
426 return { is_snoopy => 1 };
430 This attribute can also be used to set C<inflate_datetime> on a non-datetime
431 column so it also receives the L</datetime_timezone> and/or L</datetime_locale>.
433 =head2 datetime_timezone
435 Sets the timezone attribute for L<DBIx::Class::InflateColumn::DateTime> for all
436 columns with the DATE/DATETIME/TIMESTAMP data_types.
438 =head2 datetime_locale
440 Sets the locale attribute for L<DBIx::Class::InflateColumn::DateTime> for all
441 columns with the DATE/DATETIME/TIMESTAMP data_types.
445 File in Perl format, which should return a HASH reference, from which to read
450 None of these methods are intended for direct invocation by regular
451 users of L<DBIx::Class::Schema::Loader>. Some are proxied via
452 L<DBIx::Class::Schema::Loader>.
456 my $CURRENT_V = 'v7';
459 schema_base_class result_base_class additional_base_classes
460 left_base_classes additional_classes components resultset_components
463 # ensure that a peice of object data is a valid arrayref, creating
464 # an empty one or encapsulating whatever's there.
465 sub _ensure_arrayref {
470 $self->{$_} = [ $self->{$_} ]
471 unless ref $self->{$_} eq 'ARRAY';
477 Constructor for L<DBIx::Class::Schema::Loader::Base>, used internally
478 by L<DBIx::Class::Schema::Loader>.
483 my ( $class, %args ) = @_;
485 my $self = { %args };
487 bless $self => $class;
489 if (my $config_file = $self->config_file) {
490 my $config_opts = do $config_file;
492 croak "Error reading config from $config_file: $@" if $@;
494 croak "Config file $config_file must be a hashref" unless ref($config_opts) eq 'HASH';
496 while (my ($k, $v) = each %$config_opts) {
497 $self->{$k} = $v unless exists $self->{$k};
501 $self->_ensure_arrayref(qw/additional_classes
502 additional_base_classes
508 $self->_validate_class_args;
510 push(@{$self->{components}}, 'ResultSetManager')
511 if @{$self->{resultset_components}};
513 $self->{monikers} = {};
514 $self->{classes} = {};
515 $self->{_upgrading_classes} = {};
517 $self->{schema_class} ||= ( ref $self->{schema} || $self->{schema} );
518 $self->{schema} ||= $self->{schema_class};
520 croak "dump_overwrite is deprecated. Please read the"
521 . " DBIx::Class::Schema::Loader::Base documentation"
522 if $self->{dump_overwrite};
524 $self->{dynamic} = ! $self->{dump_directory};
525 $self->{temp_directory} ||= File::Temp::tempdir( 'dbicXXXX',
530 $self->{dump_directory} ||= $self->{temp_directory};
532 $self->version_to_dump($DBIx::Class::Schema::Loader::VERSION);
533 $self->schema_version_to_dump($DBIx::Class::Schema::Loader::VERSION);
535 if ((not ref $self->naming) && defined $self->naming) {
536 my $naming_ver = $self->naming;
538 relationships => $naming_ver,
539 monikers => $naming_ver,
544 for (values %{ $self->naming }) {
545 $_ = $CURRENT_V if $_ eq 'current';
548 $self->{naming} ||= {};
550 if ($self->custom_column_info && ref $self->custom_column_info ne 'CODE') {
551 croak 'custom_column_info must be a CODE ref';
554 $self->_check_back_compat;
556 $self->use_namespaces(1) unless defined $self->use_namespaces;
557 $self->generate_pod(1) unless defined $self->generate_pod;
558 $self->pod_comment_mode('auto') unless defined $self->pod_comment_mode;
559 $self->pod_comment_spillover_length(60) unless defined $self->pod_comment_spillover_length;
564 sub _check_back_compat {
567 # dynamic schemas will always be in 0.04006 mode, unless overridden
568 if ($self->dynamic) {
569 # just in case, though no one is likely to dump a dynamic schema
570 $self->schema_version_to_dump('0.04006');
572 if (not %{ $self->naming }) {
573 warn <<EOF unless $ENV{SCHEMA_LOADER_BACKCOMPAT};
575 Dynamic schema detected, will run in 0.04006 mode.
577 Set the 'naming' attribute or the SCHEMA_LOADER_BACKCOMPAT environment variable
578 to disable this warning.
580 Also consider setting 'use_namespaces => 1' if/when upgrading.
582 See perldoc DBIx::Class::Schema::Loader::Manual::UpgradingFromV4 for more
587 $self->_upgrading_from('v4');
590 $self->naming->{relationships} ||= 'v4';
591 $self->naming->{monikers} ||= 'v4';
593 if ($self->use_namespaces) {
594 $self->_upgrading_from_load_classes(1);
597 $self->use_namespaces(0);
603 # otherwise check if we need backcompat mode for a static schema
604 my $filename = $self->_get_dump_filename($self->schema_class);
605 return unless -e $filename;
607 open(my $fh, '<', $filename)
608 or croak "Cannot open '$filename' for reading: $!";
610 my $load_classes = 0;
611 my $result_namespace = '';
614 if (/^__PACKAGE__->load_classes;/) {
616 } elsif (/result_namespace => '([^']+)'/) {
617 $result_namespace = $1;
618 } elsif (my ($real_ver) =
619 /^# Created by DBIx::Class::Schema::Loader v(\d+\.\d+)/) {
621 if ($load_classes && (not defined $self->use_namespaces)) {
622 warn <<"EOF" unless $ENV{SCHEMA_LOADER_BACKCOMPAT};
624 'load_classes;' static schema detected, turning off 'use_namespaces'.
626 Set the 'use_namespaces' attribute or the SCHEMA_LOADER_BACKCOMPAT environment
627 variable to disable this warning.
629 See perldoc DBIx::Class::Schema::Loader::Manual::UpgradingFromV4 for more
632 $self->use_namespaces(0);
634 elsif ($load_classes && $self->use_namespaces) {
635 $self->_upgrading_from_load_classes(1);
637 elsif ((not $load_classes) && defined $self->use_namespaces
638 && (not $self->use_namespaces)) {
639 $self->_downgrading_to_load_classes(
640 $result_namespace || 'Result'
643 elsif ((not defined $self->use_namespaces)
644 || $self->use_namespaces) {
645 if (not $self->result_namespace) {
646 $self->result_namespace($result_namespace || 'Result');
648 elsif ($result_namespace ne $self->result_namespace) {
649 $self->_rewriting_result_namespace(
650 $result_namespace || 'Result'
655 # XXX when we go past .0 this will need fixing
656 my ($v) = $real_ver =~ /([1-9])/;
659 last if $v eq $CURRENT_V || $real_ver =~ /^0\.\d\d999/;
661 if (not %{ $self->naming }) {
662 warn <<"EOF" unless $ENV{SCHEMA_LOADER_BACKCOMPAT};
664 Version $real_ver static schema detected, turning on backcompat mode.
666 Set the 'naming' attribute or the SCHEMA_LOADER_BACKCOMPAT environment variable
667 to disable this warning.
669 See: 'naming' in perldoc DBIx::Class::Schema::Loader::Base .
671 See perldoc DBIx::Class::Schema::Loader::Manual::UpgradingFromV4 if upgrading
672 from version 0.04006.
676 $self->_upgrading_from($v);
680 $self->naming->{relationships} ||= $v;
681 $self->naming->{monikers} ||= $v;
683 $self->schema_version_to_dump($real_ver);
691 sub _validate_class_args {
695 foreach my $k (@CLASS_ARGS) {
696 next unless $self->$k;
698 my @classes = ref $self->$k eq 'ARRAY' ? @{ $self->$k } : $self->$k;
699 foreach my $c (@classes) {
700 # components default to being under the DBIx::Class namespace unless they
701 # are preceeded with a '+'
702 if ( $k =~ m/components$/ && $c !~ s/^\+// ) {
703 $c = 'DBIx::Class::' . $c;
706 # 1 == installed, 0 == not installed, undef == invalid classname
707 my $installed = Class::Inspector->installed($c);
708 if ( defined($installed) ) {
709 if ( $installed == 0 ) {
710 croak qq/$c, as specified in the loader option "$k", is not installed/;
713 croak qq/$c, as specified in the loader option "$k", is an invalid class name/;
719 sub _find_file_in_inc {
720 my ($self, $file) = @_;
722 foreach my $prefix (@INC) {
723 my $fullpath = File::Spec->catfile($prefix, $file);
724 return $fullpath if -f $fullpath
725 # abs_path throws on Windows for nonexistant files
726 and eval { Cwd::abs_path($fullpath) } ne
727 (eval { Cwd::abs_path(File::Spec->catfile($self->dump_directory, $file)) } || '');
734 my ($self, $class) = @_;
736 my $class_path = $class;
737 $class_path =~ s{::}{/}g;
738 $class_path .= '.pm';
743 sub _find_class_in_inc {
744 my ($self, $class) = @_;
746 return $self->_find_file_in_inc($self->_class_path($class));
752 return $self->_upgrading_from
753 || $self->_upgrading_from_load_classes
754 || $self->_downgrading_to_load_classes
755 || $self->_rewriting_result_namespace
759 sub _rewrite_old_classnames {
760 my ($self, $code) = @_;
762 return $code unless $self->_rewriting;
764 my %old_classes = reverse %{ $self->_upgrading_classes };
766 my $re = join '|', keys %old_classes;
769 $code =~ s/$re/$old_classes{$1} || $1/eg;
775 my ($self, $class) = @_;
777 return if $self->{skip_load_external};
779 # so that we don't load our own classes, under any circumstances
780 local *INC = [ grep $_ ne $self->dump_directory, @INC ];
782 my $real_inc_path = $self->_find_class_in_inc($class);
784 my $old_class = $self->_upgrading_classes->{$class}
785 if $self->_rewriting;
787 my $old_real_inc_path = $self->_find_class_in_inc($old_class)
788 if $old_class && $old_class ne $class;
790 return unless $real_inc_path || $old_real_inc_path;
792 if ($real_inc_path) {
793 # If we make it to here, we loaded an external definition
794 warn qq/# Loaded external class definition for '$class'\n/
797 open(my $fh, '<', $real_inc_path)
798 or croak "Failed to open '$real_inc_path' for reading: $!";
799 my $code = do { local $/; <$fh> };
801 or croak "Failed to close $real_inc_path: $!";
802 $code = $self->_rewrite_old_classnames($code);
804 if ($self->dynamic) { # load the class too
805 # kill redefined warnings
806 my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
807 local $SIG{__WARN__} = sub {
809 unless $_[0] =~ /^Subroutine \S+ redefined/;
815 $self->_ext_stmt($class,
816 qq|# These lines were loaded from '$real_inc_path' found in \@INC.\n|
817 .qq|# They are now part of the custom portion of this file\n|
818 .qq|# for you to hand-edit. If you do not either delete\n|
819 .qq|# this section or remove that file from \@INC, this section\n|
820 .qq|# will be repeated redundantly when you re-create this\n|
821 .qq|# file again via Loader! See skip_load_external to disable\n|
822 .qq|# this feature.\n|
825 $self->_ext_stmt($class, $code);
826 $self->_ext_stmt($class,
827 qq|# End of lines loaded from '$real_inc_path' |
831 if ($old_real_inc_path) {
832 open(my $fh, '<', $old_real_inc_path)
833 or croak "Failed to open '$old_real_inc_path' for reading: $!";
834 $self->_ext_stmt($class, <<"EOF");
836 # These lines were loaded from '$old_real_inc_path',
837 # based on the Result class name that would have been created by an 0.04006
838 # version of the Loader. For a static schema, this happens only once during
839 # upgrade. See skip_load_external to disable this feature.
842 my $code = slurp $old_real_inc_path;
843 $code = $self->_rewrite_old_classnames($code);
845 if ($self->dynamic) {
848 Detected external content in '$old_real_inc_path', a class name that would have
849 been used by an 0.04006 version of the Loader.
851 * PLEASE RENAME THIS CLASS: from '$old_class' to '$class', as that is the
852 new name of the Result.
854 # kill redefined warnings
855 my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
856 local $SIG{__WARN__} = sub {
858 unless $_[0] =~ /^Subroutine \S+ redefined/;
865 $self->_ext_stmt($class, $code);
866 $self->_ext_stmt($class,
867 qq|# End of lines loaded from '$old_real_inc_path' |
874 Does the actual schema-construction work.
882 $self->_tables_list({ constraint => $self->constraint, exclude => $self->exclude })
890 Rescan the database for newly added tables. Does
891 not process drops or changes. Returns a list of
892 the newly added table monikers.
894 The schema argument should be the schema class
895 or object to be affected. It should probably
896 be derived from the original schema_class used
902 my ($self, $schema) = @_;
904 $self->{schema} = $schema;
905 $self->_relbuilder->{schema} = $schema;
908 my @current = $self->_tables_list({ constraint => $self->constraint, exclude => $self->exclude });
909 foreach my $table (@current) {
910 if(!exists $self->{_tables}->{$table}) {
911 push(@created, $table);
915 my $loaded = $self->_load_tables(@created);
917 return map { $self->monikers->{$_} } @$loaded;
921 no warnings 'uninitialized';
924 return if $self->{skip_relationships};
926 if ($self->naming->{relationships} eq 'v4') {
927 require DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_040;
928 return $self->{relbuilder} ||=
929 DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_040->new(
930 $self->schema, $self->inflect_plural, $self->inflect_singular
933 elsif ($self->naming->{relationships} eq 'v5') {
934 require DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_05;
935 return $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder::Compat::v0_05->new (
937 $self->inflect_plural,
938 $self->inflect_singular,
939 $self->relationship_attrs,
943 return $self->{relbuilder} ||= DBIx::Class::Schema::Loader::RelBuilder->new (
945 $self->inflect_plural,
946 $self->inflect_singular,
947 $self->relationship_attrs,
952 my ($self, @tables) = @_;
954 # Save the new tables to the tables list
956 $self->{_tables}->{$_} = 1;
959 $self->_make_src_class($_) for @tables;
961 # sanity-check for moniker clashes
962 my $inverse_moniker_idx;
963 for (keys %{$self->monikers}) {
964 push @{$inverse_moniker_idx->{$self->monikers->{$_}}}, $_;
968 for (keys %$inverse_moniker_idx) {
969 my $tables = $inverse_moniker_idx->{$_};
971 push @clashes, sprintf ("tables %s reduced to the same source moniker '%s'",
972 join (', ', map { "'$_'" } @$tables),
979 die 'Unable to load schema - chosen moniker/class naming style results in moniker clashes. '
980 . 'Either change the naming style, or supply an explicit moniker_map: '
981 . join ('; ', @clashes)
987 $self->_setup_src_meta($_) for @tables;
989 if(!$self->skip_relationships) {
990 # The relationship loader needs a working schema
992 local $self->{dump_directory} = $self->{temp_directory};
993 $self->_reload_classes(\@tables);
994 $self->_load_relationships($_) for @tables;
997 # Remove that temp dir from INC so it doesn't get reloaded
998 @INC = grep $_ ne $self->dump_directory, @INC;
1001 $self->_load_external($_)
1002 for map { $self->classes->{$_} } @tables;
1004 # Reload without unloading first to preserve any symbols from external
1006 $self->_reload_classes(\@tables, 0);
1008 # Drop temporary cache
1009 delete $self->{_cache};
1014 sub _reload_classes {
1015 my ($self, $tables, $unload) = @_;
1017 my @tables = @$tables;
1018 $unload = 1 unless defined $unload;
1020 # so that we don't repeat custom sections
1021 @INC = grep $_ ne $self->dump_directory, @INC;
1023 $self->_dump_to_dir(map { $self->classes->{$_} } @tables);
1025 unshift @INC, $self->dump_directory;
1028 my %have_source = map { $_ => $self->schema->source($_) }
1029 $self->schema->sources;
1031 for my $table (@tables) {
1032 my $moniker = $self->monikers->{$table};
1033 my $class = $self->classes->{$table};
1036 no warnings 'redefine';
1037 local *Class::C3::reinitialize = sub {};
1040 Class::Unload->unload($class) if $unload;
1041 my ($source, $resultset_class);
1043 ($source = $have_source{$moniker})
1044 && ($resultset_class = $source->resultset_class)
1045 && ($resultset_class ne 'DBIx::Class::ResultSet')
1047 my $has_file = Class::Inspector->loaded_filename($resultset_class);
1048 Class::Unload->unload($resultset_class) if $unload;
1049 $self->_reload_class($resultset_class) if $has_file;
1051 $self->_reload_class($class);
1053 push @to_register, [$moniker, $class];
1056 Class::C3->reinitialize;
1057 for (@to_register) {
1058 $self->schema->register_class(@$_);
1062 # We use this instead of ensure_class_loaded when there are package symbols we
1065 my ($self, $class) = @_;
1067 my $class_path = $self->_class_path($class);
1068 delete $INC{ $class_path };
1070 # kill redefined warnings
1071 my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
1072 local $SIG{__WARN__} = sub {
1074 unless $_[0] =~ /^Subroutine \S+ redefined/;
1076 eval "require $class;";
1079 sub _get_dump_filename {
1080 my ($self, $class) = (@_);
1082 $class =~ s{::}{/}g;
1083 return $self->dump_directory . q{/} . $class . q{.pm};
1086 sub _ensure_dump_subdirs {
1087 my ($self, $class) = (@_);
1089 my @name_parts = split(/::/, $class);
1090 pop @name_parts; # we don't care about the very last element,
1091 # which is a filename
1093 my $dir = $self->dump_directory;
1096 mkdir($dir) or croak "mkdir('$dir') failed: $!";
1098 last if !@name_parts;
1099 $dir = File::Spec->catdir($dir, shift @name_parts);
1104 my ($self, @classes) = @_;
1106 my $schema_class = $self->schema_class;
1107 my $schema_base_class = $self->schema_base_class || 'DBIx::Class::Schema';
1109 my $target_dir = $self->dump_directory;
1110 warn "Dumping manual schema for $schema_class to directory $target_dir ...\n"
1111 unless $self->{dynamic} or $self->{quiet};
1114 qq|package $schema_class;\n\n|
1115 . qq|# Created by DBIx::Class::Schema::Loader\n|
1116 . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
1117 . qq|use strict;\nuse warnings;\n\n|
1118 . qq|use base '$schema_base_class';\n\n|;
1120 if ($self->use_namespaces) {
1121 $schema_text .= qq|__PACKAGE__->load_namespaces|;
1122 my $namespace_options;
1124 my @attr = qw/resultset_namespace default_resultset_class/;
1126 unshift @attr, 'result_namespace' unless (not $self->result_namespace) || $self->result_namespace eq 'Result';
1128 for my $attr (@attr) {
1130 $namespace_options .= qq| $attr => '| . $self->$attr . qq|',\n|
1133 $schema_text .= qq|(\n$namespace_options)| if $namespace_options;
1134 $schema_text .= qq|;\n|;
1137 $schema_text .= qq|__PACKAGE__->load_classes;\n|;
1141 local $self->{version_to_dump} = $self->schema_version_to_dump;
1142 $self->_write_classfile($schema_class, $schema_text, 1);
1145 my $result_base_class = $self->result_base_class || 'DBIx::Class::Core';
1147 foreach my $src_class (@classes) {
1149 qq|package $src_class;\n\n|
1150 . qq|# Created by DBIx::Class::Schema::Loader\n|
1151 . qq|# DO NOT MODIFY THE FIRST PART OF THIS FILE\n\n|
1152 . qq|use strict;\nuse warnings;\n\n|
1153 . qq|use base '$result_base_class';\n\n|;
1155 $self->_write_classfile($src_class, $src_text);
1158 # remove Result dir if downgrading from use_namespaces, and there are no
1160 if (my $result_ns = $self->_downgrading_to_load_classes
1161 || $self->_rewriting_result_namespace) {
1162 my $result_namespace = $self->_result_namespace(
1167 (my $result_dir = $result_namespace) =~ s{::}{/}g;
1168 $result_dir = $self->dump_directory . '/' . $result_dir;
1170 unless (my @files = glob "$result_dir/*") {
1175 warn "Schema dump completed.\n" unless $self->{dynamic} or $self->{quiet};
1180 my ($self, $version, $ts) = @_;
1181 return qq|\n\n# Created by DBIx::Class::Schema::Loader|
1184 . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
1187 sub _write_classfile {
1188 my ($self, $class, $text, $is_schema) = @_;
1190 my $filename = $self->_get_dump_filename($class);
1191 $self->_ensure_dump_subdirs($class);
1193 if (-f $filename && $self->really_erase_my_files) {
1194 warn "Deleting existing file '$filename' due to "
1195 . "'really_erase_my_files' setting\n" unless $self->{quiet};
1199 my ($custom_content, $old_md5, $old_ver, $old_ts) = $self->_get_custom_content($class, $filename);
1201 if (my $old_class = $self->_upgrading_classes->{$class}) {
1202 my $old_filename = $self->_get_dump_filename($old_class);
1204 my ($old_custom_content) = $self->_get_custom_content(
1205 $old_class, $old_filename, 0 # do not add default comment
1208 $old_custom_content =~ s/\n\n# You can replace.*\n1;\n//;
1210 if ($old_custom_content) {
1212 "\n" . $old_custom_content . "\n" . $custom_content;
1215 unlink $old_filename;
1218 $custom_content = $self->_rewrite_old_classnames($custom_content);
1221 for @{$self->{_dump_storage}->{$class} || []};
1223 # Check and see if the dump is infact differnt
1227 $compare_to = $text . $self->_sig_comment($old_ver, $old_ts);
1230 if (Digest::MD5::md5_base64($compare_to) eq $old_md5) {
1231 return unless $self->_upgrading_from && $is_schema;
1235 $text .= $self->_sig_comment(
1236 $self->version_to_dump,
1237 POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
1240 open(my $fh, '>', $filename)
1241 or croak "Cannot open '$filename' for writing: $!";
1243 # Write the top half and its MD5 sum
1244 print $fh $text . Digest::MD5::md5_base64($text) . "\n";
1246 # Write out anything loaded via external partial class file in @INC
1248 for @{$self->{_ext_storage}->{$class} || []};
1250 # Write out any custom content the user has added
1251 print $fh $custom_content;
1254 or croak "Error closing '$filename': $!";
1257 sub _default_custom_content {
1258 return qq|\n\n# You can replace this text with custom|
1259 . qq| content, and it will be preserved on regeneration|
1263 sub _get_custom_content {
1264 my ($self, $class, $filename, $add_default) = @_;
1266 $add_default = 1 unless defined $add_default;
1268 return ($self->_default_custom_content) if ! -f $filename;
1270 open(my $fh, '<', $filename)
1271 or croak "Cannot open '$filename' for reading: $!";
1274 qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
1277 my ($md5, $ts, $ver);
1279 if(!$md5 && /$mark_re/) {
1283 # Pull out the previous version and timestamp
1284 ($ver, $ts) = $buffer =~ m/# Created by DBIx::Class::Schema::Loader v(.*?) @ (.*?)$/s;
1287 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"
1288 if !$self->overwrite_modifications && Digest::MD5::md5_base64($buffer) ne $md5;
1297 croak "Cannot not overwrite '$filename' without 'really_erase_my_files',"
1298 . " it does not appear to have been generated by Loader"
1301 # Default custom content:
1302 $buffer ||= $self->_default_custom_content if $add_default;
1304 return ($buffer, $md5, $ver, $ts);
1312 warn "$target: use $_;" if $self->debug;
1313 $self->_raw_stmt($target, "use $_;");
1320 my $schema_class = $self->schema_class;
1322 my $blist = join(q{ }, @_);
1323 warn "$target: use base qw/ $blist /;" if $self->debug && @_;
1324 $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
1327 sub _result_namespace {
1328 my ($self, $schema_class, $ns) = @_;
1329 my @result_namespace;
1331 if ($ns =~ /^\+(.*)/) {
1332 # Fully qualified namespace
1333 @result_namespace = ($1)
1336 # Relative namespace
1337 @result_namespace = ($schema_class, $ns);
1340 return wantarray ? @result_namespace : join '::', @result_namespace;
1343 # Create class with applicable bases, setup monikers, etc
1344 sub _make_src_class {
1345 my ($self, $table) = @_;
1347 my $schema = $self->schema;
1348 my $schema_class = $self->schema_class;
1350 my $table_moniker = $self->_table2moniker($table);
1351 my @result_namespace = ($schema_class);
1352 if ($self->use_namespaces) {
1353 my $result_namespace = $self->result_namespace || 'Result';
1354 @result_namespace = $self->_result_namespace(
1359 my $table_class = join(q{::}, @result_namespace, $table_moniker);
1361 if ((my $upgrading_v = $self->_upgrading_from)
1362 || $self->_rewriting) {
1363 local $self->naming->{monikers} = $upgrading_v
1366 my @result_namespace = @result_namespace;
1367 if ($self->_upgrading_from_load_classes) {
1368 @result_namespace = ($schema_class);
1370 elsif (my $ns = $self->_downgrading_to_load_classes) {
1371 @result_namespace = $self->_result_namespace(
1376 elsif ($ns = $self->_rewriting_result_namespace) {
1377 @result_namespace = $self->_result_namespace(
1383 my $old_class = join(q{::}, @result_namespace,
1384 $self->_table2moniker($table));
1386 $self->_upgrading_classes->{$table_class} = $old_class
1387 unless $table_class eq $old_class;
1390 # this was a bad idea, should be ok now without it
1391 # my $table_normalized = lc $table;
1392 # $self->classes->{$table_normalized} = $table_class;
1393 # $self->monikers->{$table_normalized} = $table_moniker;
1395 $self->classes->{$table} = $table_class;
1396 $self->monikers->{$table} = $table_moniker;
1398 $self->_use ($table_class, @{$self->additional_classes});
1399 $self->_inject($table_class, @{$self->left_base_classes});
1401 if (my @components = @{ $self->components }) {
1402 $self->_dbic_stmt($table_class, 'load_components', @components);
1405 $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
1406 if @{$self->resultset_components};
1407 $self->_inject($table_class, @{$self->additional_base_classes});
1410 sub _resolve_col_accessor_collisions {
1411 my ($self, $col_info) = @_;
1413 my $base = $self->result_base_class || 'DBIx::Class::Core';
1414 my @components = map "DBIx::Class::$_", @{ $self->components || [] };
1418 for my $class ($base, @components) {
1419 eval "require ${class};";
1422 push @methods, @{ Class::Inspector->methods($class) || [] };
1426 @methods{@methods} = ();
1428 while (my ($col, $info) = each %$col_info) {
1429 my $accessor = $info->{accessor} || $col;
1431 next if $accessor eq 'id'; # special case (very common column)
1433 if (exists $methods{$accessor}) {
1434 $info->{accessor} = undef;
1439 # Set up metadata (cols, pks, etc)
1440 sub _setup_src_meta {
1441 my ($self, $table) = @_;
1443 my $schema = $self->schema;
1444 my $schema_class = $self->schema_class;
1446 my $table_class = $self->classes->{$table};
1447 my $table_moniker = $self->monikers->{$table};
1449 my $table_name = $table;
1450 my $name_sep = $self->schema->storage->sql_maker->name_sep;
1452 if ($name_sep && $table_name =~ /\Q$name_sep\E/) {
1453 $table_name = \ $self->_quote_table_name($table_name);
1456 $self->_dbic_stmt($table_class,'table',$table_name);
1458 my $cols = $self->_table_columns($table);
1459 my $col_info = $self->__columns_info_for($table);
1460 if ($self->_is_case_sensitive) {
1461 for my $col (keys %$col_info) {
1462 $col_info->{$col}{accessor} = lc $col
1463 if $col ne lc($col);
1467 # XXX this needs to go away
1468 $col_info = { map { lc($_), $col_info->{$_} } keys %$col_info };
1471 $self->_resolve_col_accessor_collisions($col_info);
1473 my $fks = $self->_table_fk_info($table);
1475 for my $fkdef (@$fks) {
1476 for my $col (@{ $fkdef->{local_columns} }) {
1477 $col_info->{$col}{is_foreign_key} = 1;
1483 map { $_, ($col_info->{$_}||{}) } @$cols
1486 my %uniq_tag; # used to eliminate duplicate uniqs
1488 my $pks = $self->_table_pk_info($table) || [];
1489 @$pks ? $self->_dbic_stmt($table_class,'set_primary_key',@$pks)
1490 : carp("$table has no primary key");
1491 $uniq_tag{ join("\0", @$pks) }++ if @$pks; # pk is a uniq
1493 my $uniqs = $self->_table_uniq_info($table) || [];
1495 my ($name, $cols) = @$_;
1496 next if $uniq_tag{ join("\0", @$cols) }++; # skip duplicates
1497 $self->_dbic_stmt($table_class,'add_unique_constraint', $name, $cols);
1502 sub __columns_info_for {
1503 my ($self, $table) = @_;
1505 my $result = $self->_columns_info_for($table);
1507 while (my ($col, $info) = each %$result) {
1508 $info = { %$info, %{ $self->_custom_column_info ($table, $col, $info) } };
1509 $info = { %$info, %{ $self->_datetime_column_info($table, $col, $info) } };
1511 $result->{$col} = $info;
1519 Returns a sorted list of loaded tables, using the original database table
1527 return keys %{$self->_tables};
1530 # Make a moniker from a table
1531 sub _default_table2moniker {
1532 no warnings 'uninitialized';
1533 my ($self, $table) = @_;
1535 if ($self->naming->{monikers} eq 'v4') {
1536 return join '', map ucfirst, split /[\W_]+/, lc $table;
1538 elsif ($self->naming->{monikers} eq 'v5') {
1539 return join '', map ucfirst, split /[\W_]+/,
1540 Lingua::EN::Inflect::Number::to_S(lc $table);
1542 elsif ($self->naming->{monikers} eq 'v6') {
1543 (my $as_phrase = lc $table) =~ s/_+/ /g;
1544 my $inflected = Lingua::EN::Inflect::Phrase::to_S($as_phrase);
1546 return join '', map ucfirst, split /\W+/, $inflected;
1549 my @words = map lc, split /(?<=[[:lower:]])[\W_]*(?=[[:upper:]])|[\W_]+/, $table;
1550 my $as_phrase = join ' ', @words;
1552 my $inflected = Lingua::EN::Inflect::Phrase::to_S($as_phrase);
1554 return join '', map ucfirst, split /\W+/, $inflected;
1557 sub _table2moniker {
1558 my ( $self, $table ) = @_;
1562 if( ref $self->moniker_map eq 'HASH' ) {
1563 $moniker = $self->moniker_map->{$table};
1565 elsif( ref $self->moniker_map eq 'CODE' ) {
1566 $moniker = $self->moniker_map->($table);
1569 $moniker ||= $self->_default_table2moniker($table);
1574 sub _load_relationships {
1575 my ($self, $table) = @_;
1577 my $tbl_fk_info = $self->_table_fk_info($table);
1578 foreach my $fkdef (@$tbl_fk_info) {
1579 $fkdef->{remote_source} =
1580 $self->monikers->{delete $fkdef->{remote_table}};
1582 my $tbl_uniq_info = $self->_table_uniq_info($table);
1584 my $local_moniker = $self->monikers->{$table};
1585 my $rel_stmts = $self->_relbuilder->generate_code($local_moniker, $tbl_fk_info, $tbl_uniq_info);
1587 foreach my $src_class (sort keys %$rel_stmts) {
1588 my $src_stmts = $rel_stmts->{$src_class};
1589 foreach my $stmt (@$src_stmts) {
1590 $self->_dbic_stmt($src_class,$stmt->{method},@{$stmt->{args}});
1595 # Overload these in driver class:
1597 # Returns an arrayref of column names
1598 sub _table_columns { croak "ABSTRACT METHOD" }
1600 # Returns arrayref of pk col names
1601 sub _table_pk_info { croak "ABSTRACT METHOD" }
1603 # Returns an arrayref of uniqs [ [ foo => [ col1, col2 ] ], [ bar => [ ... ] ] ]
1604 sub _table_uniq_info { croak "ABSTRACT METHOD" }
1606 # Returns an arrayref of foreign key constraints, each
1607 # being a hashref with 3 keys:
1608 # local_columns (arrayref), remote_columns (arrayref), remote_table
1609 sub _table_fk_info { croak "ABSTRACT METHOD" }
1611 # Returns an array of lower case table names
1612 sub _tables_list { croak "ABSTRACT METHOD" }
1614 # Execute a constructive DBIC class method, with debug/dump_to_dir hooks.
1620 # generate the pod for this statement, storing it with $self->_pod
1621 $self->_make_pod( $class, $method, @_ ) if $self->generate_pod;
1623 my $args = dump(@_);
1624 $args = '(' . $args . ')' if @_ < 2;
1625 my $stmt = $method . $args . q{;};
1627 warn qq|$class\->$stmt\n| if $self->debug;
1628 $self->_raw_stmt($class, '__PACKAGE__->' . $stmt);
1632 # generates the accompanying pod for a DBIC class method statement,
1633 # storing it with $self->_pod
1639 if ( $method eq 'table' ) {
1641 my $pcm = $self->pod_comment_mode;
1642 my ($comment, $comment_overflows, $comment_in_name, $comment_in_desc);
1643 if ( $self->can('_table_comment') ) {
1644 $comment = $self->_table_comment($table);
1645 $comment_overflows = ($comment and length $comment > $self->pod_comment_spillover_length);
1646 $comment_in_name = ($pcm eq 'name' or ($pcm eq 'auto' and !$comment_overflows));
1647 $comment_in_desc = ($pcm eq 'description' or ($pcm eq 'auto' and $comment_overflows));
1649 $self->_pod( $class, "=head1 NAME" );
1650 my $table_descr = $class;
1651 $table_descr .= " - " . $comment if $comment and $comment_in_name;
1652 $self->{_class2table}{ $class } = $table;
1653 $self->_pod( $class, $table_descr );
1654 if ($comment and $comment_in_desc) {
1655 $self->_pod( $class, "=head1 DESCRIPTION" );
1656 $self->_pod( $class, $comment );
1658 $self->_pod_cut( $class );
1659 } elsif ( $method eq 'add_columns' ) {
1660 $self->_pod( $class, "=head1 ACCESSORS" );
1661 my $col_counter = 0;
1663 while( my ($name,$attrs) = splice @cols,0,2 ) {
1665 $self->_pod( $class, '=head2 ' . $name );
1666 $self->_pod( $class,
1668 my $s = $attrs->{$_};
1669 $s = !defined $s ? 'undef' :
1670 length($s) == 0 ? '(empty string)' :
1671 ref($s) eq 'SCALAR' ? $$s :
1678 looks_like_number($s) ? $s :
1683 } sort keys %$attrs,
1686 if( $self->can('_column_comment')
1687 and my $comment = $self->_column_comment( $self->{_class2table}{$class}, $col_counter)
1689 $self->_pod( $class, $comment );
1692 $self->_pod_cut( $class );
1693 } elsif ( $method =~ /^(belongs_to|has_many|might_have)$/ ) {
1694 $self->_pod( $class, "=head1 RELATIONS" ) unless $self->{_relations_started} { $class } ;
1695 my ( $accessor, $rel_class ) = @_;
1696 $self->_pod( $class, "=head2 $accessor" );
1697 $self->_pod( $class, 'Type: ' . $method );
1698 $self->_pod( $class, "Related object: L<$rel_class>" );
1699 $self->_pod_cut( $class );
1700 $self->{_relations_started} { $class } = 1;
1704 # Stores a POD documentation
1706 my ($self, $class, $stmt) = @_;
1707 $self->_raw_stmt( $class, "\n" . $stmt );
1711 my ($self, $class ) = @_;
1712 $self->_raw_stmt( $class, "\n=cut\n" );
1715 # Store a raw source line for a class (for dumping purposes)
1717 my ($self, $class, $stmt) = @_;
1718 push(@{$self->{_dump_storage}->{$class}}, $stmt);
1721 # Like above, but separately for the externally loaded stuff
1723 my ($self, $class, $stmt) = @_;
1724 push(@{$self->{_ext_storage}->{$class}}, $stmt);
1727 sub _quote_table_name {
1728 my ($self, $table) = @_;
1730 my $qt = $self->schema->storage->sql_maker->quote_char;
1732 return $table unless $qt;
1735 return $qt->[0] . $table . $qt->[1];
1738 return $qt . $table . $qt;
1741 sub _is_case_sensitive { 0 }
1743 sub _custom_column_info {
1744 my ( $self, $table_name, $column_name, $column_info ) = @_;
1746 if (my $code = $self->custom_column_info) {
1747 return $code->($table_name, $column_name, $column_info) || {};
1752 sub _datetime_column_info {
1753 my ( $self, $table_name, $column_name, $column_info ) = @_;
1755 my $type = $column_info->{data_type} || '';
1756 if ((grep $_, @{ $column_info }{map "inflate_$_", qw/date datetime timestamp/})
1757 or ($type =~ /date|timestamp/i)) {
1758 $result->{timezone} = $self->datetime_timezone if $self->datetime_timezone;
1759 $result->{locale} = $self->datetime_locale if $self->datetime_locale;
1764 # remove the dump dir from @INC on destruction
1768 @INC = grep $_ ne $self->dump_directory, @INC;
1773 Returns a hashref of loaded table to moniker mappings. There will
1774 be two entries for each table, the original name and the "normalized"
1775 name, in the case that the two are different (such as databases
1776 that like uppercase table names, or preserve your original mixed-case
1777 definitions, or what-have-you).
1781 Returns a hashref of table to class mappings. In some cases it will
1782 contain multiple entries per table for the original and normalized table
1783 names, as above in L</monikers>.
1787 L<DBIx::Class::Schema::Loader>
1791 See L<DBIx::Class::Schema::Loader/AUTHOR> and L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
1795 This library is free software; you can redistribute it and/or modify it under
1796 the same terms as Perl itself.
1801 # vim:et sts=4 sw=4 tw=0: