From: Peter Rabbitson Date: Thu, 14 Apr 2016 22:33:17 +0000 (+0200) Subject: Centralize all user-side rsrc calls to go through result_source() X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=e570488ade8f327f47dd3318db3443a348d561d6;p=dbsrgits%2FDBIx-Class-Historic.git Centralize all user-side rsrc calls to go through result_source() This ensures the user will always get a sensible exception when the rsrc metadata object has not yet been initialized (as introduced in 5298bbb5): Before: ~$ perl -e 'use base "DBIx::Class::Core"; __PACKAGE__->add_column("foo")' Can't locate object method "result_source_instance" via package "main" at .../ResultSourceProxy.pm line 29. After: ~$ perl -e 'use base "DBIx::Class::Core"; __PACKAGE__->add_column("foo")' DBIx::Class::Row::result_source(): No ResultSource instance registered for 'main', did you forget to call main->table(...) ? at -e line 1 Add a shitload of assertions to track we are doing the right thing in all cases. This more or less concludes the rsrc changeset necessary to resolve all ambiguities. The next commit adds user-visible warnings when things go off the rails The changeset was successfully tested against the list of distributions in c8b1011e with no ill effects being observed. Thus I am pretty damn confident I rather nailed it >.> --- diff --git a/lib/DBIx/Class/CDBICompat/ColumnCase.pm b/lib/DBIx/Class/CDBICompat/ColumnCase.pm index 56bef61..efbb881 100644 --- a/lib/DBIx/Class/CDBICompat/ColumnCase.pm +++ b/lib/DBIx/Class/CDBICompat/ColumnCase.pm @@ -13,7 +13,7 @@ sub _register_column_group { sub add_columns { my ($class, @cols) = @_; - return $class->result_source_instance->add_columns(map lc, @cols); + return $class->result_source->add_columns(map lc, @cols); } sub has_a { diff --git a/lib/DBIx/Class/CDBICompat/ColumnGroups.pm b/lib/DBIx/Class/CDBICompat/ColumnGroups.pm index 6ead1f7..73f845c 100644 --- a/lib/DBIx/Class/CDBICompat/ColumnGroups.pm +++ b/lib/DBIx/Class/CDBICompat/ColumnGroups.pm @@ -36,7 +36,7 @@ sub _add_column_group { sub add_columns { my ($class, @cols) = @_; - $class->result_source_instance->add_columns(@cols); + $class->result_source->add_columns(@cols); } sub _register_column_group { @@ -148,7 +148,7 @@ sub _mk_group_accessors { } } -sub all_columns { return shift->result_source_instance->columns; } +sub all_columns { return shift->result_source->columns; } sub primary_column { my ($class) = @_; diff --git a/lib/DBIx/Class/CDBICompat/ImaDBI.pm b/lib/DBIx/Class/CDBICompat/ImaDBI.pm index ee9aae0..43537ff 100644 --- a/lib/DBIx/Class/CDBICompat/ImaDBI.pm +++ b/lib/DBIx/Class/CDBICompat/ImaDBI.pm @@ -52,9 +52,12 @@ sub sth_to_objects { $sth->execute(@$execute_args); - my @ret; + my (@ret, $rsrc); while (my $row = $sth->fetchrow_hashref) { - push(@ret, $class->inflate_result($class->result_source_instance, $row)); + push(@ret, $class->inflate_result( + ( $rsrc ||= $class->result_source ), + $row + )); } return @ret; diff --git a/lib/DBIx/Class/CDBICompat/Relationships.pm b/lib/DBIx/Class/CDBICompat/Relationships.pm index a5bfa5e..90ce39b 100644 --- a/lib/DBIx/Class/CDBICompat/Relationships.pm +++ b/lib/DBIx/Class/CDBICompat/Relationships.pm @@ -66,7 +66,7 @@ sub _declare_has_a { } else { $self->belongs_to($col, $f_class); - $rel_info = $self->result_source_instance->relationship_info($col); + $rel_info = $self->result_source->relationship_info($col); } $rel_info->{args} = \%args; @@ -110,14 +110,14 @@ sub has_many { if( !$f_key and !@f_method ) { $class->ensure_class_loaded($f_class); - my $f_source = $f_class->result_source_instance; + my $f_source = $f_class->result_source; ($f_key) = grep { $f_source->relationship_info($_)->{class} eq $class } $f_source->relationships; } $class->next::method($rel, $f_class, $f_key, $args); - my $rel_info = $class->result_source_instance->relationship_info($rel); + my $rel_info = $class->result_source->relationship_info($rel); $args->{mapping} = \@f_method; $args->{foreign_key} = $f_key; $rel_info->{args} = $args; @@ -150,7 +150,7 @@ sub might_have { { proxy => \@columns }); } - my $rel_info = $class->result_source_instance->relationship_info($rel); + my $rel_info = $class->result_source->relationship_info($rel); $rel_info->{args}{import} = \@columns; $class->_extend_meta( diff --git a/lib/DBIx/Class/DB.pm b/lib/DBIx/Class/DB.pm index ea4a5a6..df232b3 100644 --- a/lib/DBIx/Class/DB.pm +++ b/lib/DBIx/Class/DB.pm @@ -176,7 +176,7 @@ native L system. =cut sub resultset_instance { - $_[0]->result_source_instance->resultset + $_[0]->result_source->resultset } =begin hidden @@ -194,7 +194,7 @@ __PACKAGE__->mk_classaccessor('_result_source_instance' => []); # Yep. this is horrific. Basically what's happening here is that # (with good reason) DBIx::Class::Schema copies the result source for # registration. Because we have a retarded setup order forced on us we need -# to actually make our ->result_source_instance -be- the source used, and we +# to actually make our ->result_source -be- the source used, and we # need to get the source name and schema into ourselves. So this makes it # happen. diff --git a/lib/DBIx/Class/InflateColumn.pm b/lib/DBIx/Class/InflateColumn.pm index 39d36f5..c16375d 100644 --- a/lib/DBIx/Class/InflateColumn.pm +++ b/lib/DBIx/Class/InflateColumn.pm @@ -92,10 +92,9 @@ sub inflate_column { $self->throw_exception("InflateColumn can not be used on a column with a declared FilterColumn filter") if defined $colinfo->{_filter_info} and $self->isa('DBIx::Class::FilterColumn'); - $self->throw_exception("No such column $col to inflate") - unless $self->result_source_instance->has_column($col); $self->throw_exception("inflate_column needs attr hashref") unless ref $attrs eq 'HASH'; + $colinfo->{_inflate_info} = $attrs; my $acc = $colinfo->{accessor}; $self->mk_group_accessors('inflated_column' => [ (defined $acc ? $acc : $col), $col]); diff --git a/lib/DBIx/Class/Manual/Cookbook.pod b/lib/DBIx/Class/Manual/Cookbook.pod index 324ff64..ce68fc2 100644 --- a/lib/DBIx/Class/Manual/Cookbook.pod +++ b/lib/DBIx/Class/Manual/Cookbook.pod @@ -125,9 +125,9 @@ almost like you would define a regular ResultSource. # # do not attempt to deploy() this view - __PACKAGE__->result_source_instance->is_virtual(1); + __PACKAGE__->result_source->is_virtual(1); - __PACKAGE__->result_source_instance->view_definition(q[ + __PACKAGE__->result_source->view_definition(q[ SELECT u.* FROM user u INNER JOIN user_friends f ON u.id = f.user_id WHERE f.friend_user_id = ? diff --git a/lib/DBIx/Class/Relationship/BelongsTo.pm b/lib/DBIx/Class/Relationship/BelongsTo.pm index cadca92..50ddc2e 100644 --- a/lib/DBIx/Class/Relationship/BelongsTo.pm +++ b/lib/DBIx/Class/Relationship/BelongsTo.pm @@ -39,16 +39,16 @@ sub belongs_to { $class->throw_exception( "No such column '$f_key' declared yet on ${class} ($guess)" - ) unless $class->result_source_instance->has_column($f_key); + ) unless $class->result_source->has_column($f_key); $class->ensure_class_loaded($f_class); my $f_rsrc = dbic_internal_try { - $f_class->result_source_instance; + $f_class->result_source; } catch { $class->throw_exception( "Foreign class '$f_class' does not seem to be a Result class " - . "(or it simply did not load entirely due to a circular relation chain)" + . "(or it simply did not load entirely due to a circular relation chain): $_" ); }; @@ -81,7 +81,7 @@ sub belongs_to { and (keys %$cond)[0] =~ /^foreign\./ and - $class->result_source_instance->has_column($rel) + $class->result_source->has_column($rel) ) ? 'filter' : 'single'; my $fk_columns = ($acc_type eq 'single' and ref $cond eq 'HASH') diff --git a/lib/DBIx/Class/Relationship/HasMany.pm b/lib/DBIx/Class/Relationship/HasMany.pm index 053eda6..6ef09fb 100644 --- a/lib/DBIx/Class/Relationship/HasMany.pm +++ b/lib/DBIx/Class/Relationship/HasMany.pm @@ -16,7 +16,7 @@ sub has_many { unless (ref $cond) { - my $pri = $class->result_source_instance->_single_pri_col_or_die; + my $pri = $class->result_source->_single_pri_col_or_die; my ($f_key,$guess); if (defined $cond && length $cond) { @@ -30,7 +30,7 @@ sub has_many { # FIXME - this check needs to be moved to schema-composition time... # # only perform checks if the far side appears already loaded -# if (my $f_rsrc = dbic_internal_try { $f_class->result_source_instance } ) { +# if (my $f_rsrc = dbic_internal_try { $f_class->result_source } ) { # $class->throw_exception( # "No such column '$f_key' on foreign class ${f_class} ($guess)" # ) if !$f_rsrc->has_column($f_key); diff --git a/lib/DBIx/Class/Relationship/HasOne.pm b/lib/DBIx/Class/Relationship/HasOne.pm index 46e18e3..8f74bb8 100644 --- a/lib/DBIx/Class/Relationship/HasOne.pm +++ b/lib/DBIx/Class/Relationship/HasOne.pm @@ -24,7 +24,7 @@ sub has_one { sub _has_one { my ($class, $join_type, $rel, $f_class, $cond, $attrs) = @_; unless (ref $cond) { - my $pri = $class->result_source_instance->_single_pri_col_or_die; + my $pri = $class->result_source->_single_pri_col_or_die; my ($f_key,$guess,$f_rsrc); if (defined $cond && length $cond) { @@ -36,7 +36,7 @@ sub _has_one { $class->ensure_class_loaded($f_class); $f_rsrc = dbic_internal_try { - my $r = $f_class->result_source_instance; + my $r = $f_class->result_source; die "There got to be some columns by now... (exception caught and rewritten by catch below)" unless $r->columns; $r; @@ -60,8 +60,8 @@ sub _has_one { # FIXME - this check needs to be moved to schema-composition time... # # only perform checks if the far side was not preloaded above *AND* -# # appears to have been loaded by something else (has a rsrc_instance) -# if (! $f_rsrc and $f_rsrc = dbic_internal_try { $f_class->result_source_instance }) { +# # appears to have been loaded by something else (has a rsrc) +# if (! $f_rsrc and $f_rsrc = dbic_internal_try { $f_class->result_source }) { # $class->throw_exception( # "No such column '$f_key' on foreign class ${f_class} ($guess)" # ) if !$f_rsrc->has_column($f_key); diff --git a/lib/DBIx/Class/ResultSetManager.pm b/lib/DBIx/Class/ResultSetManager.pm index addc8c3..e4adae5 100644 --- a/lib/DBIx/Class/ResultSetManager.pm +++ b/lib/DBIx/Class/ResultSetManager.pm @@ -88,12 +88,11 @@ sub _register_resultset_class { my $self = shift; my $resultset_class = $self . $self->table_resultset_class_suffix; no strict 'refs'; - if (@{"$resultset_class\::ISA"}) { - $self->result_source_instance->resultset_class($resultset_class); - } else { - $self->result_source_instance->resultset_class - ($self->base_resultset_class); - } + $self->result_source->resultset_class( + ( scalar @{"${resultset_class}::ISA"} ) + ? $resultset_class + : $self->base_resultset_class + ); } =head1 FURTHER QUESTIONS? diff --git a/lib/DBIx/Class/ResultSource.pm b/lib/DBIx/Class/ResultSource.pm index a8da52e..d6ca1ed 100644 --- a/lib/DBIx/Class/ResultSource.pm +++ b/lib/DBIx/Class/ResultSource.pm @@ -60,8 +60,8 @@ DBIx::Class::ResultSource - Result source object __PACKAGE__->table_class('DBIx::Class::ResultSource::View'); __PACKAGE__->table('year2000cds'); - __PACKAGE__->result_source_instance->is_virtual(1); - __PACKAGE__->result_source_instance->view_definition( + __PACKAGE__->result_source->is_virtual(1); + __PACKAGE__->result_source->view_definition( "SELECT cdid, artist, title FROM cd WHERE year ='2000'" ); @@ -1043,11 +1043,11 @@ sub unique_constraint_columns { =back - __PACKAGE__->result_source_instance->sqlt_deploy_callback('mycallbackmethod'); + __PACKAGE__->result_source->sqlt_deploy_callback('mycallbackmethod'); or - __PACKAGE__->result_source_instance->sqlt_deploy_callback(sub { + __PACKAGE__->result_source->sqlt_deploy_callback(sub { my ($source_instance, $sqlt_table) = @_; ... } ); @@ -2388,7 +2388,7 @@ sub related_source { else { my $class = $self->relationship_info($rel)->{class}; $self->ensure_class_loaded($class); - $class->result_source_instance; + $class->result_source; } } diff --git a/lib/DBIx/Class/ResultSource/View.pm b/lib/DBIx/Class/ResultSource/View.pm index 3339826..ede6d1d 100644 --- a/lib/DBIx/Class/ResultSource/View.pm +++ b/lib/DBIx/Class/ResultSource/View.pm @@ -21,8 +21,8 @@ DBIx::Class::ResultSource::View - ResultSource object representing a view __PACKAGE__->table_class('DBIx::Class::ResultSource::View'); __PACKAGE__->table('year2000cds'); - __PACKAGE__->result_source_instance->is_virtual(1); - __PACKAGE__->result_source_instance->view_definition( + __PACKAGE__->result_source->is_virtual(1); + __PACKAGE__->result_source->view_definition( "SELECT cdid, artist, title FROM cd WHERE year ='2000'" ); __PACKAGE__->add_columns( @@ -73,13 +73,13 @@ above, you can then: If you modified the schema to include a placeholder - __PACKAGE__->result_source_instance->view_definition( + __PACKAGE__->result_source->view_definition( "SELECT cdid, artist, title FROM cd WHERE year = ?" ); and ensuring you have is_virtual set to true: - __PACKAGE__->result_source_instance->is_virtual(1); + __PACKAGE__->result_source->is_virtual(1); You could now say: @@ -113,14 +113,14 @@ You could now say: =head2 is_virtual - __PACKAGE__->result_source_instance->is_virtual(1); + __PACKAGE__->result_source->is_virtual(1); Set to true for a virtual view, false or unset for a real database-based view. =head2 view_definition - __PACKAGE__->result_source_instance->view_definition( + __PACKAGE__->result_source->view_definition( "SELECT cdid, artist, title FROM cd WHERE year ='2000'" ); @@ -129,7 +129,7 @@ syntaxes. =head2 deploy_depends_on - __PACKAGE__->result_source_instance->deploy_depends_on( + __PACKAGE__->result_source->deploy_depends_on( ["MyApp::Schema::Result::Year","MyApp::Schema::Result::CD"] ); diff --git a/lib/DBIx/Class/ResultSourceProxy.pm b/lib/DBIx/Class/ResultSourceProxy.pm index 70de112..cfd37ca 100644 --- a/lib/DBIx/Class/ResultSourceProxy.pm +++ b/lib/DBIx/Class/ResultSourceProxy.pm @@ -23,7 +23,7 @@ sub set_inherited_ro_instance { sub add_columns { my ($class, @cols) = @_; - my $source = $class->result_source_instance; + my $source = $class->result_source; $source->add_columns(@cols); my $colinfos; @@ -46,7 +46,7 @@ sub add_column { sub add_relationship { my ($class, $rel, @rest) = @_; - my $source = $class->result_source_instance; + my $source = $class->result_source; $source->add_relationship($rel => @rest); $class->register_relationship($rel => $source->relationship_info($rel)); } @@ -55,7 +55,7 @@ sub add_relationship { # legacy resultset_class accessor, seems to be used by cdbi only sub iterator_class { DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call; - shift->result_source_instance->resultset_class(@_) + shift->result_source->resultset_class(@_) } for my $method_to_proxy (qw/ @@ -91,7 +91,8 @@ for my $method_to_proxy (qw/ /) { quote_sub __PACKAGE__."::$method_to_proxy", sprintf( <<'EOC', $method_to_proxy ); DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and DBIx::Class::_Util::fail_on_internal_call; - shift->result_source_instance->%s (@_); + + shift->result_source->%s (@_); EOC } diff --git a/lib/DBIx/Class/ResultSourceProxy/Table.pm b/lib/DBIx/Class/ResultSourceProxy/Table.pm index 4c0807c..53dd26f 100644 --- a/lib/DBIx/Class/ResultSourceProxy/Table.pm +++ b/lib/DBIx/Class/ResultSourceProxy/Table.pm @@ -82,7 +82,7 @@ Gets or sets the table name. =cut sub table { - return $_[0]->result_source_instance->name unless @_ > 1; + return $_[0]->result_source->name unless @_ > 1; my ($class, $table) = @_; diff --git a/lib/DBIx/Class/Row.pm b/lib/DBIx/Class/Row.pm index b0542cb..1097701 100644 --- a/lib/DBIx/Class/Row.pm +++ b/lib/DBIx/Class/Row.pm @@ -190,7 +190,7 @@ sub new { $rsrc ||= $h->resolve; } - $new->result_source($rsrc) if $rsrc; + $new->result_source_instance($rsrc) if $rsrc; if (my $col_from_rel = delete $attrs->{-cols_from_relations}) { @{$new->{_ignore_at_insert}={}}{@$col_from_rel} = (); @@ -625,12 +625,9 @@ sub delete { $self->in_storage(0); } else { - my $rsrc = dbic_internal_try { $self->result_source_instance } - or $self->throw_exception("Can't do class delete without a ResultSource instance"); - my $attrs = @_ > 1 && ref $_[$#_] eq 'HASH' ? { %{pop(@_)} } : {}; my $query = ref $_[0] eq 'HASH' ? $_[0] : {@_}; - $rsrc->resultset->search(@_)->delete; + $self->result_source->resultset->search_rs(@_)->delete; } return $self; } @@ -1174,7 +1171,7 @@ sub copy { my $new = { _column_data => $col_data }; bless $new, ref $self; - $new->result_source($rsrc); + $new->result_source_instance($rsrc); $new->set_inflated_columns($changes); $new->insert; @@ -1433,12 +1430,20 @@ Accessor to the L this object was created from. =cut sub result_source { + # While getter calls are routed through here for sensible exception text + # it makes no sense to have setters do the same thing + DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS + and + @_ > 1 + and + fail_on_internal_call; + # this is essentially a `shift->result_source_instance(@_)` with handholding &{ $_[0]->can('result_source_instance') || $_[0]->throw_exception( - "No result source instance registered for '@{[ $_[0] ]}', did you forget to call @{[ ref $_[0] || $_[0] ]}->table(...) ?" + "No ResultSource instance registered for '@{[ $_[0] ]}', did you forget to call @{[ ref $_[0] || $_[0] ]}->table(...) ?" ) }; } @@ -1589,7 +1594,8 @@ sub throw_exception { if ( ! DBIx::Class::_Util::in_internal_try and - my $rsrc = dbic_internal_try { $self->result_source } + # FIXME - the try is 99% superfluous, but just in case + my $rsrc = dbic_internal_try { $self->result_source_instance } ) { $rsrc->throw_exception(@_) } diff --git a/lib/DBIx/Class/Schema.pm b/lib/DBIx/Class/Schema.pm index d5a8f35..9961c08 100644 --- a/lib/DBIx/Class/Schema.pm +++ b/lib/DBIx/Class/Schema.pm @@ -199,7 +199,7 @@ sub _ns_get_rsrc_instance { 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?: $_" @@ -1398,13 +1398,13 @@ file). You may also need it to register classes at runtime. 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 diff --git a/lib/DBIx/Class/UTF8Columns.pm b/lib/DBIx/Class/UTF8Columns.pm index 38a4dd4..db571a6 100644 --- a/lib/DBIx/Class/UTF8Columns.pm +++ b/lib/DBIx/Class/UTF8Columns.pm @@ -94,7 +94,7 @@ sub utf8_columns { if (@_) { foreach my $col (@_) { $self->throw_exception("column $col doesn't exist") - unless $self->result_source_instance->has_column($col); + unless $self->result_source->has_column($col); } return $self->_utf8_columns({ map { $_ => 1 } @_ }); } else { diff --git a/lib/DBIx/Class/_Util.pm b/lib/DBIx/Class/_Util.pm index b640e76..c459c73 100644 --- a/lib/DBIx/Class/_Util.pm +++ b/lib/DBIx/Class/_Util.pm @@ -1038,9 +1038,10 @@ sub fail_on_internal_call { { package DB; $fr = [ CORE::caller(1) ]; - $argdesc = ref $DB::args[0] - ? DBIx::Class::_Util::refdesc($DB::args[0]) - : ( $DB::args[0] . '' ) + $argdesc = + ( not defined $DB::args[0] ) ? 'UNAVAILABLE' + : ( length ref $DB::args[0] ) ? DBIx::Class::_Util::refdesc($DB::args[0]) + : $DB::args[0] . '' ; }; @@ -1062,7 +1063,7 @@ sub fail_on_internal_call { ; if ( - $argdesc + defined $fr->[0] and $check_fr->[0] =~ /^(?:DBIx::Class|DBICx::)/ and diff --git a/t/lib/DBICTest.pm b/t/lib/DBICTest.pm index b43d4bf..cfc18df 100644 --- a/t/lib/DBICTest.pm +++ b/t/lib/DBICTest.pm @@ -31,6 +31,21 @@ use DBICTest::Util qw( dbg DEBUG_TEST_CONCURRENCY_LOCKS PEEPEENESS ); use DBICTest::Util::LeakTracer qw/populate_weakregistry assert_empty_weakregistry/; + +# The actual ASSERT logic is in BaseSchema for pesky load-order reasons +# Hence run this through once, *before* DBICTest::Schema and friends load +BEGIN { + if ( + DBIx::Class::_ENV_::ASSERT_NO_ERRONEOUS_METAINSTANCE_USE + or + DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS + ) { + require DBIx::Class::Row; + require DBICTest::BaseSchema; + DBICTest::BaseSchema->connect( sub {} ); + } +} + use DBICTest::Schema; use DBIx::Class::_Util qw( detected_reinvoked_destructor scope_guard modver_gt_or_eq ); use Carp; @@ -275,7 +290,7 @@ sub __mk_disconnect_guard { my $clan_connect_caller = '*UNKNOWN*'; my $i; - while ( my ($pack, $file, $line) = caller(++$i) ) { + while ( my ($pack, $file, $line) = CORE::caller(++$i) ) { next if $file eq __FILE__; next if $pack =~ /^DBIx::Class|^Try::Tiny/; $clan_connect_caller = "$file line $line"; diff --git a/t/lib/DBICTest/BaseSchema.pm b/t/lib/DBICTest/BaseSchema.pm index aaaf955..5f52f75 100644 --- a/t/lib/DBICTest/BaseSchema.pm +++ b/t/lib/DBICTest/BaseSchema.pm @@ -7,9 +7,10 @@ use base qw(DBICTest::Base DBIx::Class::Schema); use Fcntl qw(:DEFAULT :seek :flock); use IO::Handle (); -use DBIx::Class::_Util 'scope_guard'; +use DBIx::Class::_Util qw( emit_loud_diag scope_guard set_subname get_subname ); use DBICTest::Util::LeakTracer qw(populate_weakregistry assert_empty_weakregistry); use DBICTest::Util qw( local_umask tmpdir await_flock dbg DEBUG_TEST_CONCURRENCY_LOCKS ); +use Scalar::Util qw( refaddr weaken ); use namespace::clean; if( $ENV{DBICTEST_ASSERT_NO_SPURIOUS_EXCEPTION_ACTION} ) { @@ -216,7 +217,19 @@ END { } } -my $weak_registry = {}; +my ( $weak_registry, $assertion_arounds ) = ( {}, {} ); + +sub DBICTest::__RsrcRedefiner_iThreads_handler__::CLONE { + if( DBIx::Class::_ENV_::ASSERT_NO_ERRONEOUS_METAINSTANCE_USE ) { + %$assertion_arounds = map { + (defined $_) + ? ( refaddr($_) => $_ ) + : () + } values %$assertion_arounds; + + weaken($_) for values %$assertion_arounds; + } +} sub connection { my $self = shift->next::method(@_); @@ -363,6 +376,157 @@ sub connection { ]); } + # + # Check an explicit level of indirection: makes sure that folks doing + # use `base "DBIx::Class::Core"; __PACKAGE__->add_column("foo")` + # will see the correct error message + # + # In the future this all is likely to be folded into a single method in + # some way, but that's a fight for another maint + # + if( DBIx::Class::_ENV_::ASSERT_NO_ERRONEOUS_METAINSTANCE_USE ) { + + for my $class_of_interest ( + 'DBIx::Class::Row', + map { $self->class($_) } ($self->sources) + ) { + + my $orig_rsrc = $class_of_interest->can('result_source') + or die "How did we get here?!"; + + unless ( $assertion_arounds->{refaddr $orig_rsrc} ) { + + my ($origin) = get_subname($orig_rsrc); + + no warnings 'redefine'; + no strict 'refs'; + + *{"${origin}::result_source"} = my $replacement = set_subname "${origin}::result_source" => sub { + + + @_ > 1 + and + (CORE::caller(0))[1] !~ / (?: ^ | [\/\\] ) x?t [\/\\] .+? \.t $ /x + and + emit_loud_diag( + msg => 'Incorrect indirect call of result_source() as setter must be changed to result_source_instance()', + confess => 1, + ); + + + grep { + ! (CORE::caller($_))[7] + and + ( (CORE::caller($_))[3] || '' ) eq '(eval)' + and + ( (CORE::caller($_))[1] || '' ) !~ / (?: ^ | [\/\\] ) x?t [\/\\] .+? \.t $ /x + } (0..2) + and + # these evals are legit + ( (CORE::caller(4))[3] || '' ) !~ /^ (?: + DBIx::Class::Schema::_ns_get_rsrc_instance + | + DBIx::Class::Relationship::BelongsTo::belongs_to + | + DBIx::Class::Relationship::HasOne::_has_one + | + Class::C3::Componentised::.+ + ) $/x + and + emit_loud_diag( + # not much else we can do (aside from exit(1) which is too obnoxious) + msg => 'Incorrect call of result_source() in an eval', + ); + + + &$orig_rsrc; + }; + + weaken( $assertion_arounds->{refaddr $replacement} = $replacement ); + } + + + # no rsrc_instance to mangle + next if $class_of_interest eq 'DBIx::Class::Row'; + + + my $orig_rsrc_instance = $class_of_interest->can('result_source_instance') + or die "How did we get here?!"; + + # Do the around() per definition-site as result_source_instance is a CAG inherited cref + unless ( $assertion_arounds->{refaddr $orig_rsrc_instance} ) { + + my ($origin) = get_subname($orig_rsrc_instance); + + no warnings 'redefine'; + no strict 'refs'; + + *{"${origin}::result_source_instance"} = my $replacement = set_subname "${origin}::result_source_instance" => sub { + + + @_ == 1 + and + # special cased as we do not care whether there is a source + ( (CORE::caller(4))[3] || '' ) ne 'DBIx::Class::Schema::_register_source' + and + # special case because I am paranoid + ( (CORE::caller(4))[3] || '' ) ne 'DBIx::Class::Row::throw_exception' + and + ( (CORE::caller(1))[3] || '' ) !~ / ^ DBIx::Class:: (?: + Row::result_source + | + Row::throw_exception + | + ResultSourceProxy::Table:: (?: _init_result_source_instance | table ) + | + ResultSourceHandle::STORABLE_thaw + ) $ /x + and + (CORE::caller(0))[1] !~ / (?: ^ | [\/\\] ) x?t [\/\\] .+? \.t $ /x + and + emit_loud_diag( + msg => 'Incorrect direct call of result_source_instance() as getter must be changed to result_source()', + confess => 1 + ); + + + grep { + ! (CORE::caller($_))[7] + and + ( (CORE::caller($_))[3] || '' ) eq '(eval)' + and + ( (CORE::caller($_))[1] || '' ) !~ / (?: ^ | [\/\\] ) x?t [\/\\] .+? \.t $ /x + } (0..2) + and + # special cased as we do not care whether there is a source + ( (CORE::caller(4))[3] || '' ) ne 'DBIx::Class::Schema::_register_source' + and + # special case because I am paranoid + ( (CORE::caller(4))[3] || '' ) ne 'DBIx::Class::Row::throw_exception' + and + # special case for Storable, which in turn calls from an eval + ( (CORE::caller(1))[3] || '' ) ne 'DBIx::Class::ResultSourceHandle::STORABLE_thaw' + and + emit_loud_diag( + # not much else we can do (aside from exit(1) which is too obnoxious) + msg => 'Incorrect call of result_source_instance() in an eval', + skip_frames => 1, + show_dups => 1, + ); + + &$orig_rsrc_instance; + }; + + weaken( $assertion_arounds->{refaddr $replacement} = $replacement ); + } + + } + + Class::C3::initialize if DBIx::Class::_ENV_::OLD_MRO; + } + # + # END Check an explicit level of indirection + return $self; } diff --git a/t/resultsource/instance_equivalence.t b/t/resultsource/instance_equivalence.t index 37f054f..90621f9 100644 --- a/t/resultsource/instance_equivalence.t +++ b/t/resultsource/instance_equivalence.t @@ -1,5 +1,7 @@ BEGIN { do "./t/lib/ANFANG.pm" or die ( $@ || $! ) } +BEGIN { $ENV{DBIC_ASSERT_NO_ERRONEOUS_METAINSTANCE_USE} = 0 } + use strict; use warnings; no warnings 'qw';