While on its surface this was a good idea, it actually hides problems even
more: by the time we arrive at a useful hook-point to check the current MRO,
something likely already changed it from under us, and the old effects are
all masked away for good.
So instead scale back as much as possible, and set 'c3' where needed as
lazily as practical. In order to satisfy the mro requirements imposed by
5e0eea35 we do the "flip" during the ->source() stage.
Additionally we record the original setting any time we switch the mro on
foreign classes (two such spots in the codebase). A later commit will use
this information to add the final bit of sanity to this clusterfuck.
use warnings;
use base qw( DBIx::Class::MethodAttributes Class::Accessor::Grouped );
-use mro 'c3';
use Scalar::Util 'blessed';
use DBIx::Class::_Util 'fail_on_internal_call';
) {
$_[0]->ensure_class_loaded($class);
- mro::set_mro( $class, 'c3' );
-
${"${class}::__LOADED__BY__DBIC__CAG__COMPONENT_CLASS__"}
= do { \(my $anon = 'loaded') };
}
# this warns of subtle bugs introduced by UTF8Columns hacky handling of store_column
# if and only if it is placed before something overriding store_column
-#
-# and also enforces C3 mro on all components
-my $mro_already_set;
sub inject_base {
my $class = shift;
my ($target, @complist) = @_;
unshift @target_isa, $comp;
}
- # only examine from $_[2] onwards
- # C::C3::C already sets c3 on $_[1]
- mro::set_mro( $_ => 'c3' ) for grep {
- $mro_already_set->{$_} ? 0 : ( $mro_already_set->{$_} = 1 )
- } @_[1 .. $#_];
-
$class->next::method(@_);
}
use DBIx::Class::_Util qw( uniq refdesc visit_namespaces );
use Scalar::Util qw( weaken refaddr );
-use mro 'c3';
use namespace::clean;
my ( $attr_cref_registry, $attr_cache_active );
use warnings;
use base 'DBIx::Class';
-use mro 'c3';
use DBIx::Class::Carp;
use DBIx::Class::ResultSetColumn;
use warnings;
use base 'DBIx::Class::ResultSource::RowParser';
-use mro 'c3';
use DBIx::Class::Carp;
use DBIx::Class::_Util qw( UNRESOLVABLE_CONDITION dbic_internal_try fail_on_internal_call );
use warnings;
use base 'DBIx::Class';
-use mro 'c3';
use Try::Tiny;
use warnings;
use base 'DBIx::Class::ResultSource';
-use mro 'c3';
=head1 NAME
use warnings;
use base 'DBIx::Class::ResultSource';
-use mro 'c3';
__PACKAGE__->mk_group_accessors(
'simple' => qw(is_virtual view_definition deploy_depends_on) );
use warnings;
use base 'DBIx::Class';
-use mro 'c3';
use DBIx::Class::_Util qw( quote_sub fail_on_internal_call );
use namespace::clean;
use warnings;
use base 'DBIx::Class';
-use mro 'c3';
use DBIx::Class::Carp;
use Try::Tiny;
||
$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 )
+ ;
+
+ # DO NOT REMOVE - see comment above
+ $@ = $old_dollarat;
+
+ $rsrc;
}
=head2 class
if ($driver) {
my $storage_class = "DBIx::Class::Storage::DBI::${driver}";
if ($self->load_optional_class($storage_class)) {
- mro::set_mro($storage_class, 'c3');
+
+ no strict 'refs';
+ mro::set_mro($storage_class, 'c3') if
+ (
+ ${"${storage_class}::__INITIAL_MRO_UPON_DBIC_LOAD__"}
+ ||= mro::get_mro($storage_class)
+ )
+ ne
+ 'c3'
+ ;
+
bless $self, $storage_class;
$self->_rebless();
}
use Test::More;
use DBICTest;
use DBIx::Class::Optional::Dependencies;
+use DBIx::Class::_Util 'uniq';
my @global_ISA_tail = qw(
DBIx::Class
Class::Accessor::Grouped
);
-is(
- mro::get_mro($_),
- 'c3',
- "Correct mro on base class '$_'",
-) for grep { $_ =~ /^DBIx::Class/ } @global_ISA_tail;
-
{
package AAA;
use base "DBIx::Class::Core";
+ use mro 'c3';
}
{
my $art = DBICTest->init_schema->resultset("Artist")->next;
-check_ancestry($_) for (
- ref( $art ),
- ref( $art->result_source ),
- ref( $art->result_source->resultset ),
- ref( $art->result_source->schema ),
- ( map
- { ref $art->result_source->schema->source($_) }
- $art->result_source->schema->sources
- ),
- qw( AAA BBB CCC ),
- ((! DBIx::Class::Optional::Dependencies->req_ok_for('cdbicompat') ) ? () : do {
- unshift @INC, 't/cdbi/testlib';
- map { eval "require $_" or die $@; $_ } qw(
- Film Lazy Actor ActorAlias ImplicitInflate
- );
- }),
-);
+check_ancestry($_) for uniq map
+ { length ref $_ ? ref $_ : $_ }
+ (
+ $art,
+ $art->result_source,
+ $art->result_source->resultset,
+ ( map
+ { $_, $_->result_class, $_->resultset_class }
+ map
+ { $art->result_source->schema->source($_) }
+ $art->result_source->schema->sources
+ ),
+ qw( AAA BBB CCC ),
+ ((! DBIx::Class::Optional::Dependencies->req_ok_for('cdbicompat') ) ? () : do {
+ unshift @INC, 't/cdbi/testlib';
+ map { eval "require $_" or die $@; $_ } qw(
+ Film Lazy Actor ActorAlias ImplicitInflate
+ );
+ }),
+ )
+;
use DBIx::Class::Storage::DBI::Sybase::Microsoft_SQL_Server;
"Correct end of \@ISA for '$class'"
);
- # check the remainder
- for my $c (@linear_ISA) {
- # nothing to see there
- next if $c =~ /^DBICTest::/;
-
- next if mro::get_mro($c) eq 'c3';
-
- fail( "Incorrect mro '@{[ mro::get_mro($c) ]}' on '$c' (parent of '$class')" );
- }
+ is(
+ mro::get_mro($class),
+ 'c3',
+ "Expected mro on class '$class' automatically set",
+ );
}
done_testing;