X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FSchema.pm;h=033c0b73756268951461304dd3d8e5f873e68539;hb=867f1b28c4934e62ad3545c8607b2bf4c145b34a;hp=3c992ab33e89b1522ce355360600661b2240bad3;hpb=a8c2c746a5bb98671cbe3a58f26cd115b7770cf9;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Schema.pm b/lib/DBIx/Class/Schema.pm index 3c992ab..033c0b7 100644 --- a/lib/DBIx/Class/Schema.pm +++ b/lib/DBIx/Class/Schema.pm @@ -4,13 +4,11 @@ use strict; use warnings; use DBIx::Class::Exception; -use Carp::Clan qw/^DBIx::Class|^Try::Tiny/; +use DBIx::Class::Carp; use Try::Tiny; use Scalar::Util 'weaken'; -use File::Spec; use Sub::Name 'subname'; -use Module::Find(); -use Storable(); +use B 'svref_2object'; use namespace::clean; use base qw/DBIx::Class/; @@ -78,20 +76,32 @@ particular which module inherits off which. __PACKAGE__->load_namespaces(); __PACKAGE__->load_namespaces( - result_namespace => 'Res', - resultset_namespace => 'RSet', - default_resultset_class => '+MyDB::Othernamespace::RSet', - ); + result_namespace => 'Res', + resultset_namespace => 'RSet', + default_resultset_class => '+MyDB::Othernamespace::RSet', + ); + +With no arguments, this method uses L to load all of the +Result and ResultSet classes under the namespace of the schema from +which it is called. For example, C will by default find +and load Result classes named C and ResultSet +classes named C. + +ResultSet classes are associated with Result class of the same name. +For example, C will get the ResultSet class +C if it is present. + +Both Result and ResultSet namespaces are configurable via the +C and C options. -With no arguments, this method uses L to load all your -Result classes from a sub-namespace F under your Schema class' -namespace, i.e. with a Schema of I all files in -I are assumed to be Result classes. +Another option, C specifies a custom default +ResultSet class for Result classes with no corresponding ResultSet. -It also finds all ResultSet classes in the namespace F and -loads them into the appropriate Result classes using for you. The -matching is done by assuming the package name of the ResultSet class -is the same as that of the Result class. +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>. + +=head3 Warnings You will be warned if ResultSet classes are discovered for which there are no matching Result classes like this: @@ -104,19 +114,7 @@ L to some other class, you will be warned like this: We found ResultSet class '$rs_class' for '$result', but it seems that you had already set '$result' to use '$rs_set' instead -Both of the sub-namespaces are configurable if you don't like the defaults, -via the options C and C. - -If (and only if) you specify the option C, any found -Result classes for which we do not find a corresponding -ResultSet class will have their C set to -C. - -All of the namespace and classname options to this method are relative to -the schema classname by default. To specify a fully-qualified name, prefix -it with a literal C<+>. - -Examples: +=head3 Examples # load My::Schema::Result::CD, My::Schema::Result::Artist, # My::Schema::ResultSet::CD, etc... @@ -138,10 +136,10 @@ Examples: resultset_namespace => '+Another::Place::RSets', ); -If you'd like to use multiple namespaces of each type, simply use an arrayref -of namespaces for that option. In the case that the same result -(or resultset) class exists in multiple namespaces, the latter entries in -your list of namespaces will override earlier ones. +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 +entries in the list of namespaces will override earlier ones. My::Schema->load_namespaces( # My::Schema::Results_C::Foo takes precedence over My::Schema::Results_B::Foo : @@ -168,6 +166,7 @@ sub _findallmod { my $proto = shift; my $ns = shift || ref $proto || $proto; + require Module::Find; my @mods = Module::Find::findallmod($ns); # try to untaint module names. mods where this fails @@ -239,12 +238,14 @@ sub load_namespaces { my @to_register; { - no warnings 'redefine'; - local *Class::C3::reinitialize = sub { }; - use warnings 'redefine'; + 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 - $class->ensure_class_loaded($_) foreach(values %results); + for my $res (values %results) { + $class->ensure_class_loaded($res); + } my %inh_idx; my @subclass_last = sort { @@ -292,7 +293,8 @@ sub load_namespaces { . 'corresponding Result class'; } - Class::C3->reinitialize; + Class::C3->reinitialize if DBIx::Class::_ENV_::OLD_MRO; + $class->register_class(@$_) for (@to_register); return; @@ -375,7 +377,9 @@ sub load_classes { my @to_register; { no warnings qw/redefine/; - local *Class::C3::reinitialize = sub { }; + 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}"; @@ -392,7 +396,7 @@ sub load_classes { } } } - Class::C3->reinitialize; + Class::C3->reinitialize if DBIx::Class::_ENV_::OLD_MRO; foreach my $to (@to_register) { $class->register_class(@$to); @@ -584,7 +588,13 @@ source name. =cut sub source { - my ($self, $moniker) = @_; + my $self = shift; + + $self->throw_exception("source() expects a source name") + unless @_; + + my $moniker = shift; + my $sreg = $self->source_registrations; return $sreg->{$moniker} if exists $sreg->{$moniker}; @@ -898,8 +908,10 @@ sub compose_namespace { my $schema = $self->clone; { no warnings qw/redefine/; + local *Class::C3::reinitialize = sub { } if DBIx::Class::_ENV_::OLD_MRO; + use warnings qw/redefine/; + no strict qw/refs/; -# local *Class::C3::reinitialize = sub { }; foreach my $moniker ($schema->sources) { my $source = $schema->source($moniker); my $target_class = "${target}::${moniker}"; @@ -918,7 +930,7 @@ sub compose_namespace { $schema->register_source($moniker, $source); } } -# Class::C3->reinitialize(); + Class::C3->reinitialize() if DBIx::Class::_ENV_::OLD_MRO; { no strict 'refs'; no warnings 'redefine'; @@ -992,18 +1004,25 @@ sub svp_rollback { =over 4 +=item Arguments: %attrs? + =item Return Value: $new_schema =back Clones the schema and its associated result_source objects and returns the -copy. +copy. The resulting copy will have the same attributes as the source schema, +except for those attributes explicitly overriden by the provided C<%attrs>. =cut sub clone { - my ($self) = @_; - my $clone = { (ref $self ? %$self : ()) }; + my $self = shift; + + my $clone = { + (ref $self ? %$self : ()), + (@_ == 1 && ref $_[0] eq 'HASH' ? %{ $_[0] } : @_), + }; bless $clone, (ref $self || $self); $clone->class_mappings({ %{$clone->class_mappings} }); @@ -1027,8 +1046,8 @@ sub clone { =back -Throws an exception. Defaults to using L to report errors from -user's perspective. See L for details on overriding +Throws an exception. Obeys the exemption rules of L to report +errors from outer-user's perspective. See L for details on overriding this method's behavior. If L is turned on, C's default behavior will provide a detailed stack trace. @@ -1175,6 +1194,8 @@ format. sub ddl_filename { my ($self, $type, $version, $dir, $preversion) = @_; + require File::Spec; + my $filename = ref($self); $filename =~ s/::/-/g; $filename = File::Spec->catfile($dir, "$filename-$version-$type.sql"); @@ -1194,18 +1215,20 @@ reference to any schema, so are rather useless. sub thaw { my ($self, $obj) = @_; local $DBIx::Class::ResultSourceHandle::thaw_schema = $self; + require Storable; return Storable::thaw($obj); } =head2 freeze -This doesn't actually do anything more than call L, it is just +This doesn't actually do anything more than call L, it is just provided here for symmetry. =cut sub freeze { - return Storable::freeze($_[1]); + require Storable; + return Storable::nfreeze($_[1]); } =head2 dclone @@ -1227,6 +1250,7 @@ objects so their references to the schema object sub dclone { my ($self, $obj) = @_; local $DBIx::Class::ResultSourceHandle::thaw_schema = $self; + require Storable; return Storable::dclone($obj); } @@ -1370,6 +1394,43 @@ sub _register_source { $self->class_mappings(\%map); } +{ + my $global_phase_destroy; + + # SpeedyCGI runs END blocks every cycle but keeps object instances + # hence we have to disable the globaldestroy hatch, and rely on the + # eval trap below (which appears to work, but is risky done so late) + END { $global_phase_destroy = 1 unless $CGI::SpeedyCGI::i_am_speedy } + + sub DESTROY { + return if $global_phase_destroy; + + my $self = shift; + my $srcs = $self->source_registrations; + + for my $moniker (keys %$srcs) { + # find first source that is not about to be GCed (someone other than $self + # holds a reference to it) and reattach to it, weakening our own link + # + # during global destruction (if we have not yet bailed out) this will throw + # which will serve as a signal to not try doing anything else + if (ref $srcs->{$moniker} and svref_2object($srcs->{$moniker})->REFCNT > 1) { + local $@; + eval { + $srcs->{$moniker}->schema($self); + 1; + } or do { + $global_phase_destroy = 1; + last; + }; + + weaken $srcs->{$moniker}; + last; + } + } + } +} + sub _unregister_source { my ($self, $moniker) = @_; my %reg = %{$self->source_registrations};