diff --git a/lib/DBIx/Class/Schema.pm b/lib/DBIx/Class/Schema.pm index 1df100577..c6f2360b2 100644 --- a/lib/DBIx/Class/Schema.pm +++ b/lib/DBIx/Class/Schema.pm @@ -101,6 +101,11 @@ All of the namespace and classname options are by default relative to the schema classname. To specify a fully-qualified name, prefix it with a literal C<+>. For example, C<+Other::NameSpace::Result>. +Experimental: It is also possible to load the result source classes when they +are requested by L. Giving C as an argument to +L will enable this. This will decrease start up time if you +have large schemas. + =head3 Warnings You will be warned if ResultSet classes are discovered for which there @@ -136,6 +141,12 @@ L to some other class, you will be warned like this: resultset_namespace => '+Another::Place::RSets', ); + # Postpone loading of result sources + My::Schema->load_namespaces( + lazy_load => 1, + # ... + ); + To search multiple namespaces for either Result or ResultSet classes, use an arrayref of namespaces for that option. In the case that the same result (or resultset) class exists in multiple namespaces, later @@ -211,6 +222,7 @@ sub load_namespaces { my $result_namespace = delete $args{result_namespace} || 'Result'; my $resultset_namespace = delete $args{resultset_namespace} || 'ResultSet'; my $default_resultset_class = delete $args{default_resultset_class}; + my $lazy_load = delete $args{lazy_load}; $class->throw_exception('load_namespaces: unknown option(s): ' . join(q{,}, map { qq{'$_'} } keys %args)) @@ -232,7 +244,18 @@ sub load_namespaces { my %results = $class->_map_namespaces(@$result_namespace); my %resultsets = $class->_map_namespaces(@$resultset_namespace); + if($lazy_load) { + for(keys %results) { + # $source_class => [$source_name, $resultset_class] + $class->class_mappings->{$results{$_}} = [ $_, $resultsets{$_} || $default_resultset_class ]; + # $source_name => [$source_class, $resultset_class] + $class->source_registrations->{$_} = [ $results{$_}, $resultsets{$_} || $default_resultset_class ]; + } + return; + } + my @to_register; + { no warnings qw/redefine/; local *Class::C3::reinitialize = sub { } if DBIx::Class::_ENV_::OLD_MRO; @@ -589,17 +612,30 @@ sub source { unless @_; my $source_name = shift; - my $sreg = $self->source_registrations; - return $sreg->{$source_name} if exists $sreg->{$source_name}; + + if(exists $sreg->{$source_name}) { + my $source = $sreg->{$source_name}; + return $self->_lazy_source($source_name, @$source) if ref $source eq 'ARRAY'; + return $source; + } # if we got here, they probably passed a full class name my $mapped = $self->class_mappings->{$source_name}; - $self->throw_exception("Can't find source for ${source_name}") - unless $mapped && exists $sreg->{$mapped}; + + $self->throw_exception("Can't find source for ${source_name}") unless $mapped; + return $self->_lazy_source($mapped->[0], $source_name, $mapped->[1]) if ref $mapped eq 'ARRAY'; return $sreg->{$mapped}; } +sub _lazy_source { + my($self, $source_name, $source_class, $resultset_class) = @_; + + $self->ensure_class_loaded($source_class); + $source_class->resultset_class($resultset_class) if $resultset_class; + $self->source_registrations->{$source_name} = $self->register_class($source_name, $source_class); +} + =head2 class =over 4 @@ -1013,7 +1049,7 @@ sub clone { }; bless $clone, (ref $self || $self); - $clone->$_(undef) for qw/class_mappings source_registrations storage/; + $clone->$_(undef) for qw/class_mappings source_registrations storage /; $clone->_copy_state_from($self); diff --git a/t/39load_namespaces_lazy.t b/t/39load_namespaces_lazy.t new file mode 100644 index 000000000..bd2daba26 --- /dev/null +++ b/t/39load_namespaces_lazy.t @@ -0,0 +1,39 @@ +use strict; +use warnings; +use Test::More; + +use lib qw(t/lib); +use DBICTest; # do not remove even though it is not used + +plan tests => 12; + +my $warnings; +eval { + local $SIG{__WARN__} = sub { $warnings .= shift }; + package DBICNSTest; + use base qw/DBIx::Class::Schema/; + __PACKAGE__->load_namespaces(lazy_load => 1, default_resultset_class => 'RSBase'); +}; +ok !$@, 'namespace is loaded' or diag $@; +ok !$warnings, 'no warnings'; + +is_deeply( + [ sort DBICNSTest->sources ], + [ qw/ A B D R / ], + 'sources() available' +); + +for(DBICNSTest->sources) { + is ref DBICNSTest->source_registrations->{$_}, 'ARRAY', "$_ is lazy"; +} + +my $source_r = DBICNSTest->source('R'); +isa_ok($source_r, 'DBIx::Class::ResultSource::Table'); +my $rset_r = DBICNSTest->resultset('R'); +isa_ok($rset_r, 'DBICNSTest::RSBase'); +ok ref $source_r->related_source('a'), 'managed to load related'; + +my $source_a = DBICNSTest->source('A'); +isa_ok($source_a, 'DBIx::Class::ResultSource::Table'); +my $rset_a = DBICNSTest->resultset('A'); +isa_ok($rset_a, 'DBICNSTest::ResultSet::A'); diff --git a/t/lib/DBICNSTest/Result/R.pm b/t/lib/DBICNSTest/Result/R.pm new file mode 100644 index 000000000..dd98019f1 --- /dev/null +++ b/t/lib/DBICNSTest/Result/R.pm @@ -0,0 +1,13 @@ +package DBICNSTest::Result::R; + +use warnings; +use strict; + +use base qw/DBIx::Class::Core/; +__PACKAGE__->table('r'); +__PACKAGE__->add_columns('r'); +__PACKAGE__->belongs_to( + a => 'DBICNSTest::Result::A', + { 'foreign.a' => 'this.r' }, +); +1;