X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FSchema.pm;h=1bf19653e15c70fa62bffe8a1f600566a6158bd1;hb=97940e368df996e1fe6111fb14f560594dc4c0b2;hp=9961c08d9a9142f8fa32b2050151346cf5304653;hpb=e570488ade8f327f47dd3318db3443a348d561d6;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Schema.pm b/lib/DBIx/Class/Schema.pm index 9961c08..1bf1965 100644 --- a/lib/DBIx/Class/Schema.pm +++ b/lib/DBIx/Class/Schema.pm @@ -6,11 +6,10 @@ use warnings; use base 'DBIx::Class'; use DBIx::Class::Carp; -use Try::Tiny; -use Scalar::Util qw/weaken blessed/; +use Scalar::Util qw( weaken blessed refaddr ); use DBIx::Class::_Util qw( - refcount quote_sub scope_guard - is_exception dbic_internal_try + refdesc refcount quote_sub scope_guard + is_exception dbic_internal_try dbic_internal_catch fail_on_internal_call emit_loud_diag ); use Devel::GlobalDestruction; @@ -27,6 +26,11 @@ __PACKAGE__->mk_classaccessor('default_resultset_attributes' => {}); __PACKAGE__->mk_classaccessor('class_mappings' => {}); __PACKAGE__->mk_classaccessor('source_registrations' => {}); +__PACKAGE__->mk_group_accessors( component_class => 'schema_sanity_checker' ); +__PACKAGE__->schema_sanity_checker( + 'DBIx::Class::Schema::SanityChecker' +); + =head1 NAME DBIx::Class::Schema - composable schemas @@ -200,7 +204,7 @@ sub _ns_get_rsrc_instance { return dbic_internal_try { $rs_class->result_source - } catch { + } dbic_internal_catch { $me->throw_exception ( "Attempt to load_namespaces() class $rs_class failed - are you sure this is a real Result Class?: $_" ); @@ -237,10 +241,6 @@ sub load_namespaces { my @to_register; { - no warnings qw/redefine/; - local *Class::C3::reinitialize = sub { } if DBIx::Class::_ENV_::OLD_MRO; - use warnings qw/redefine/; - # ensure classes are loaded and attached in inheritance order for my $result_class (values %$results_by_source_name) { $class->ensure_class_loaded($result_class); @@ -294,8 +294,6 @@ sub load_namespaces { .'with no corresponding Result class'; } - Class::C3->reinitialize if DBIx::Class::_ENV_::OLD_MRO; - $class->register_class(@$_) for (@to_register); return; @@ -377,10 +375,6 @@ sub load_classes { my @to_register; { - no warnings qw/redefine/; - local *Class::C3::reinitialize = sub { } if DBIx::Class::_ENV_::OLD_MRO; - use warnings qw/redefine/; - foreach my $prefix (keys %comps_for) { foreach my $comp (@{$comps_for{$prefix}||[]}) { my $comp_class = "${prefix}::${comp}"; @@ -397,7 +391,6 @@ sub load_classes { } } } - Class::C3->reinitialize if DBIx::Class::_ENV_::OLD_MRO; foreach my $to (@to_register) { $class->register_class(@$to); @@ -454,6 +447,42 @@ Example: use base qw/DBIx::Class::Schema/; __PACKAGE__->default_resultset_attributes( { software_limit => 1 } ); +=head2 schema_sanity_checker + +=over 4 + +=item Arguments: L provider + +=item Return Value: L provider + +=item Default value: L + +=back + +On every call to L if the value of this attribute evaluates to +true, DBIC will invoke +C<< L<$schema_sanity_checker|/schema_sanity_checker>->L($schema) >> +before returning. The return value of this invocation is ignored. + +B to +L this +feature was introduced. Blindly disabling the checker on existing projects +B after upgrade to C<< DBIC >= v0.082900 >>. + +Example: + + package My::Schema; + use base qw/DBIx::Class::Schema/; + __PACKAGE__->schema_sanity_checker('My::Schema::SanityChecker'); + + # or to disable all checks: + __PACKAGE__->schema_sanity_checker(''); + +Note: setting the value to C B have the desired effect, +due to an implementation detail of L inherited +accessors. In order to disable any and all checks you must set this +attribute to an empty string as shown in the second example above. + =head2 exception_action =over 4 @@ -552,7 +581,7 @@ version, overload L instead. =cut -sub connect { +sub connect :DBIC_method_is_indirect_sugar { DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call; shift->clone->connection(@_); } @@ -835,7 +864,7 @@ those values. =cut -sub populate { +sub populate :DBIC_method_is_indirect_sugar { DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call; my ($self, $name, $data) = @_; @@ -859,12 +888,17 @@ Similar to L except sets the storage object and connection data B on C<$self>. You should probably be calling L to get a properly L Schema object instead. +If the accessor L returns a true value C<$checker>, +the following call will take place before return: +C<< L<$checker|/schema_sanity_checker>->L)|DBIx::Class::Schema::SanityChecker/perform_schema_sanity_checks> >> + =head3 Overloading Overload C to change the behaviour of C. =cut +my $default_off_stderr_blurb_emitted; sub connection { my ($self, @info) = @_; return $self if !@info && $self->storage; @@ -879,7 +913,7 @@ sub connection { dbic_internal_try { $self->ensure_class_loaded ($storage_class); } - catch { + dbic_internal_catch { $self->throw_exception( "Unable to load storage class ${storage_class}: $_" ); @@ -888,7 +922,12 @@ sub connection { my $storage = $storage_class->new( $self => $args||{} ); $storage->connect_info(\@info); $self->storage($storage); - return $self; + + if( my $checker = $self->schema_sanity_checker ) { + $checker->perform_schema_sanity_checks($self); + } + + $self; } sub _normalize_storage_type { @@ -947,10 +986,6 @@ sub compose_namespace { #$schema->class_mappings({}); { - no warnings qw/redefine/; - local *Class::C3::reinitialize = sub { } if DBIx::Class::_ENV_::OLD_MRO; - use warnings qw/redefine/; - foreach my $source_name ($self->sources) { my $orig_source = $self->source($source_name); @@ -970,7 +1005,8 @@ sub compose_namespace { for qw(class source resultset); } - Class::C3->reinitialize() if DBIx::Class::_ENV_::OLD_MRO; + # needed to cover the newly installed stuff via quote_sub above + 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 @@ -1172,7 +1208,7 @@ This guard was activated starting", 1; } - catch { + dbic_internal_catch { # We call this to get the necessary warnings emitted and disregard the RV # as it's definitely an exception if we got as far as this catch{} block is_exception( @@ -1466,7 +1502,7 @@ sub _register_source { $derived_rsrc->schema($self); weaken $derived_rsrc->{schema} - if length ref($self); + if length( my $schema_class = ref($self) ); my %reg = %{$self->source_registrations}; $reg{$source_name} = $derived_rsrc; @@ -1498,6 +1534,44 @@ sub _register_source { $map{$result_class} = $source_name; $self->class_mappings(\%map); + + + my $schema_class_level_rsrc; + if ( + # we are called on a schema instance, not on the class + length $schema_class + + and + + # the schema class also has a registration with the same name + $schema_class_level_rsrc = dbic_internal_try { $schema_class->source($source_name) } + + and + + # what we are registering on the schema instance *IS* derived + # from the class-level (top) rsrc... + ( grep { $_ == $derived_rsrc } $result_class_level_rsrc->__derived_instances ) + + and + + # ... while the schema-class-level has stale-markers + keys %{ $schema_class_level_rsrc->{__metadata_divergencies} || {} } + ) { + my $msg = + "The ResultSource instance you just registered on '$self' as " + . "'$source_name' seems to have no relation to $schema_class->" + . "source('$source_name') which in turn is marked stale (likely due " + . "to recent $result_class->... direct class calls). This is almost " + . "always a mistake: perhaps you forgot a cycle of " + . "$schema_class->unregister_source( '$source_name' ) / " + . "$schema_class->register_class( '$source_name' => '$result_class' )" + ; + + DBIx::Class::_ENV_::ASSERT_NO_ERRONEOUS_METAINSTANCE_USE + ? emit_loud_diag( msg => $msg, confess => 1 ) + : carp_unique($msg) + ; + } } $derived_rsrc; @@ -1599,7 +1673,7 @@ sub compose_connection { dbic_internal_try { require DBIx::Class::ResultSetProxy; } - catch { + dbic_internal_catch { $self->throw_exception ("No arguments to load_classes and couldn't load DBIx::Class::ResultSetProxy ($_)") }; @@ -1620,6 +1694,9 @@ sub compose_connection { my $schema = $self->compose_namespace($target, 'DBIx::Class::ResultSetProxy'); quote_sub "${target}::schema", '$s', { '$s' => \$schema }; + # needed to cover the newly installed stuff via quote_sub above + Class::C3->reinitialize if DBIx::Class::_ENV_::OLD_MRO; + $schema->connection(@info); foreach my $source_name ($schema->sources) { my $source = $schema->source($source_name);