use warnings;
use base 'DBIx::Class';
-use mro 'c3';
use DBIx::Class::Carp;
use Try::Tiny;
my $rs_class = ref ($_[0]) || $_[0];
return dbic_internal_try {
- $rs_class->result_source_instance
+ $rs_class->result_source
} catch {
$me->throw_exception (
"Attempt to load_namespaces() class $rs_class failed - are you sure this is a real Result Class?: $_"
=cut
sub source {
- my $self = shift;
+ my ($self, $source_name) = @_;
$self->throw_exception("source() expects a source name")
- unless @_;
+ unless $source_name;
- my $source_name = shift;
+ my $source_registrations;
+
+ my $rsrc =
+ ( $source_registrations = $self->source_registrations )->{$source_name}
+ ||
+ # if we got here, they probably passed a full class name
+ $source_registrations->{ $self->class_mappings->{$source_name} || '' }
+ ||
+ $self->throw_exception( "Can't find source for ${source_name}" )
+ ;
+
+ # DO NOT REMOVE:
+ # We need to prevent alterations of pre-existing $@ due to where this call
+ # sits in the overall stack ( *unless* of course there is an actual error
+ # to report ). set_mro does alter $@ (and yes - it *can* throw an exception)
+ # We do not use local because set_mro *can* throw an actual exception
+ # We do not use a try/catch either, as on one hand it would slow things
+ # down for no reason (we would always rethrow), but also because adding *any*
+ # try/catch block below will segfault various threading tests on older perls
+ # ( which in itself is a FIXME but ENOTIMETODIG )
+ my $old_dollarat = $@;
+
+ no strict 'refs';
+ mro::set_mro($_, 'c3') for
+ grep
+ {
+ # some pseudo-sources do not have a result/resultset yet
+ defined $_
+ and
+ (
+ (
+ ${"${_}::__INITIAL_MRO_UPON_DBIC_LOAD__"}
+ ||= mro::get_mro($_)
+ )
+ ne
+ 'c3'
+ )
+ }
+ map
+ { length ref $_ ? ref $_ : $_ }
+ ( $rsrc, $rsrc->result_class, $rsrc->resultset_class )
+ ;
- my $sreg = $self->source_registrations;
- return $sreg->{$source_name} if exists $sreg->{$source_name};
+ # DO NOT REMOVE - see comment above
+ $@ = $old_dollarat;
- # 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};
- return $sreg->{$mapped};
+ $rsrc;
}
=head2 class
my $target_class = "${target}::${source_name}";
$self->inject_base($target_class, $orig_source->result_class, ($base || ()) );
- # register_source examines result_class, and then returns us a clone
- my $new_source = $schema->register_source($source_name, bless
- { %$orig_source, result_class => $target_class },
- ref $orig_source,
+ $schema->register_source(
+ $source_name,
+ $orig_source->clone(
+ result_class => $target_class
+ ),
);
-
- if ($target_class->can('result_source_instance')) {
- # give the class a schema-less source copy
- $target_class->result_source_instance( bless
- { %$new_source, schema => ref $new_source->{schema} || $new_source->{schema} },
- ref $new_source,
- );
- }
}
# Legacy stuff, not inserting INDIRECT assertions
Class::C3->reinitialize() if DBIx::Class::_ENV_::OLD_MRO;
+ # Give each composed class yet another *schema-less* source copy
+ # this is used for the freeze/thaw cycle
+ #
+ # This is not covered by any tests directly, but is indirectly exercised
+ # in t/cdbi/sweet/08pager by re-setting the schema on an existing object
+ # FIXME - there is likely a much cheaper way to take care of this
+ for my $source_name ($self->sources) {
+
+ my $target_class = "${target}::${source_name}";
+
+ $target_class->result_source_instance(
+ $self->source($source_name)->clone(
+ result_class => $target_class,
+ schema => ( ref $schema || $schema ),
+ )
+ );
+ }
+
return $schema;
}
$self->class_mappings({ %{$from->class_mappings} });
$self->source_registrations({ %{$from->source_registrations} });
- foreach my $source_name ($from->sources) {
- my $source = $from->source($source_name);
- my $new = $source->new($source);
- # we use extra here as we want to leave the class_mappings as they are
- # but overwrite the source_registrations entry with the new source
- $self->register_extra_source($source_name => $new);
- }
+ # we use extra here as we want to leave the class_mappings as they are
+ # but overwrite the source_registrations entry with the new source
+ $self->register_extra_source( $_ => $from->source($_) )
+ for $from->sources;
if ($from->storage) {
$self->storage($from->storage);
Registers a class which isa DBIx::Class::ResultSourceProxy. Equivalent to
calling:
- $schema->register_source($source_name, $component_class->result_source_instance);
+ $schema->register_source($source_name, $component_class->result_source);
=cut
sub register_class {
my ($self, $source_name, $to_register) = @_;
- $self->register_source($source_name => $to_register->result_source_instance);
+ $self->register_source($source_name => $to_register->result_source);
}
=head2 register_source
sub register_extra_source { shift->_register_source(@_, { extra => 1 }) }
sub _register_source {
- my ($self, $source_name, $source, $params) = @_;
+ my ($self, $source_name, $supplied_rsrc, $params) = @_;
- $source = $source->new({ %$source, source_name => $source_name });
+ my $derived_rsrc = $supplied_rsrc->clone({
+ source_name => $source_name,
+ });
- $source->schema($self);
- weaken $source->{schema} if ref($self);
+ # Do not move into the clone-hashref above: there are things
+ # on CPAN that do hook 'sub schema' </facepalm>
+ # https://metacpan.org/source/LSAUNDERS/DBIx-Class-Preview-1.000003/lib/DBIx/Class/ResultSource/Table/Previewed.pm#L9-38
+ $derived_rsrc->schema($self);
+
+ weaken $derived_rsrc->{schema}
+ if length ref($self);
my %reg = %{$self->source_registrations};
- $reg{$source_name} = $source;
+ $reg{$source_name} = $derived_rsrc;
$self->source_registrations(\%reg);
- return $source if $params->{extra};
+ return $derived_rsrc if $params->{extra};
- my $rs_class = $source->result_class;
- if ($rs_class and my $rsrc = dbic_internal_try { $rs_class->result_source_instance } ) {
+ my( $result_class, $result_class_level_rsrc );
+ if (
+ $result_class = $derived_rsrc->result_class
+ and
+ # There are known cases where $rs_class is *ONLY* an inflator, without
+ # any hint of a rsrc (e.g. DBIx::Class::KiokuDB::EntryProxy)
+ $result_class_level_rsrc = dbic_internal_try { $result_class->result_source_instance }
+ ) {
my %map = %{$self->class_mappings};
- if (
- exists $map{$rs_class}
+
+ carp (
+ "$result_class already had a registered source which was replaced by "
+ . 'this call. Perhaps you wanted register_extra_source(), though it is '
+ . 'more likely you did something wrong.'
+ ) if (
+ exists $map{$result_class}
and
- $map{$rs_class} ne $source_name
+ $map{$result_class} ne $source_name
and
- $rsrc ne $_[2] # orig_source
- ) {
- carp
- "$rs_class already had a registered source which was replaced by this call. "
- . 'Perhaps you wanted register_extra_source(), though it is more likely you did '
- . 'something wrong.'
- ;
- }
+ $result_class_level_rsrc != $supplied_rsrc
+ );
- $map{$rs_class} = $source_name;
+ $map{$result_class} = $source_name;
$self->class_mappings(\%map);
}
- return $source;
+ $derived_rsrc;
}
my $global_phase_destroy;
my $source = $schema->source($source_name);
my $class = $source->result_class;
#warn "$source_name $class $source ".$source->storage;
- $class->mk_classaccessor(result_source_instance => $source);
+
+ $class->mk_group_accessors( inherited => [ result_source_instance => '_result_source' ] );
+ # explicit set-call, avoid mro update lag
+ $class->set_inherited( result_source_instance => $source );
+
$class->mk_classaccessor(resultset_instance => $source->resultset);
$class->mk_classaccessor(class_resolver => $schema);
}