X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FSchema.pm;h=76395aea3afabdf232182f9ecf888814233fec2c;hb=38e29e8a4f9dc755af31c591292cb5a2ab72d015;hp=c55eefd1cff7edcdb65021d71c658f842d54c433;hpb=fb13a49f17a0e0a49638080a4bd826fb3702aebe;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Schema.pm b/lib/DBIx/Class/Schema.pm index c55eefd..76395ae 100644 --- a/lib/DBIx/Class/Schema.pm +++ b/lib/DBIx/Class/Schema.pm @@ -3,7 +3,8 @@ package DBIx::Class::Schema; use strict; use warnings; -use DBIx::Class::Exception; +use base 'DBIx::Class'; + use DBIx::Class::Carp; use Try::Tiny; use Scalar::Util qw/weaken blessed/; @@ -12,8 +13,6 @@ use B 'svref_2object'; use Devel::GlobalDestruction; use namespace::clean; -use base qw/DBIx::Class/; - __PACKAGE__->mk_classdata('class_mappings' => {}); __PACKAGE__->mk_classdata('source_registrations' => {}); __PACKAGE__->mk_classdata('storage_type' => '::DBI'); @@ -74,12 +73,13 @@ particular which module inherits off which. =back + package MyApp::Schema; __PACKAGE__->load_namespaces(); __PACKAGE__->load_namespaces( result_namespace => 'Res', resultset_namespace => 'RSet', - default_resultset_class => '+MyDB::Othernamespace::RSet', + default_resultset_class => '+MyApp::Othernamespace::RSet', ); With no arguments, this method uses L to load all of the @@ -155,8 +155,7 @@ entries in the list of namespaces will override earlier ones. # be stripped. sub _expand_relative_name { my ($class, $name) = @_; - return if !$name; - $name = $class . '::' . $name if ! ($name =~ s/^\+//); + $name =~ s/^\+// or $name = "${class}::${name}"; return $name; } @@ -164,31 +163,26 @@ sub _expand_relative_name { # namespace of $class. Untaints all findings as they can be assumed # to be safe sub _findallmod { - my $proto = shift; - my $ns = shift || ref $proto || $proto; - require Module::Find; - - # untaint result - return map { $_ =~ /(.+)/ } Module::Find::findallmod($ns); + return map + { $_ =~ /(.+)/ } # untaint result + Module::Find::findallmod( $_[1] || ref $_[0] || $_[0] ) + ; } # returns a hash of $shortname => $fullname for every package # found in the given namespaces ($shortname is with the $fullname's # namespace stripped off) sub _map_namespaces { - my ($class, @namespaces) = @_; - - my @results_hash; - foreach my $namespace (@namespaces) { - push( - @results_hash, - map { (substr($_, length "${namespace}::"), $_) } - $class->_findallmod($namespace) - ); + my ($me, $namespaces) = @_; + + my %res; + for my $ns (@$namespaces) { + $res{ substr($_, length "${ns}::") } = $_ + for $me->_findallmod($ns); } - @results_hash; + \%res; } # returns the result_source_instance for the passed class/object, @@ -211,17 +205,18 @@ sub load_namespaces { my $result_namespace = delete $args{result_namespace} || 'Result'; my $resultset_namespace = delete $args{resultset_namespace} || 'ResultSet'; + my $default_resultset_class = delete $args{default_resultset_class}; + $default_resultset_class = $class->_expand_relative_name($default_resultset_class) + if $default_resultset_class; + $class->throw_exception('load_namespaces: unknown option(s): ' . join(q{,}, map { qq{'$_'} } keys %args)) if scalar keys %args; - $default_resultset_class - = $class->_expand_relative_name($default_resultset_class); - for my $arg ($result_namespace, $resultset_namespace) { - $arg = [ $arg ] if !ref($arg) && $arg; + $arg = [ $arg ] if ( $arg and ! ref $arg ); $class->throw_exception('load_namespaces: namespace arguments must be ' . 'a simple string or an arrayref') @@ -230,8 +225,8 @@ sub load_namespaces { $_ = $class->_expand_relative_name($_) for (@$arg); } - my %results = $class->_map_namespaces(@$result_namespace); - my %resultsets = $class->_map_namespaces(@$resultset_namespace); + my $results_by_source_name = $class->_map_namespaces($result_namespace); + my $resultsets_by_source_name = $class->_map_namespaces($resultset_namespace); my @to_register; { @@ -240,54 +235,56 @@ sub load_namespaces { use warnings qw/redefine/; # ensure classes are loaded and attached in inheritance order - for my $res (values %results) { - $class->ensure_class_loaded($res); + for my $result_class (values %$results_by_source_name) { + $class->ensure_class_loaded($result_class); } my %inh_idx; - my @subclass_last = sort { + my @source_names_by_subclass_last = sort { ($inh_idx{$a} ||= - scalar @{mro::get_linear_isa( $results{$a} )} + scalar @{mro::get_linear_isa( $results_by_source_name->{$a} )} ) <=> ($inh_idx{$b} ||= - scalar @{mro::get_linear_isa( $results{$b} )} + scalar @{mro::get_linear_isa( $results_by_source_name->{$b} )} ) - } keys(%results); + } keys(%$results_by_source_name); - foreach my $result (@subclass_last) { - my $result_class = $results{$result}; + foreach my $source_name (@source_names_by_subclass_last) { + my $result_class = $results_by_source_name->{$source_name}; - my $rs_class = delete $resultsets{$result}; - my $rs_set = $class->_ns_get_rsrc_instance ($result_class)->resultset_class; + my $preset_resultset_class = $class->_ns_get_rsrc_instance ($result_class)->resultset_class; + my $found_resultset_class = delete $resultsets_by_source_name->{$source_name}; - if($rs_set && $rs_set ne 'DBIx::Class::ResultSet') { - if($rs_class && $rs_class ne $rs_set) { - carp "We found ResultSet class '$rs_class' for '$result', but it seems " - . "that you had already set '$result' to use '$rs_set' instead"; + if($preset_resultset_class && $preset_resultset_class ne 'DBIx::Class::ResultSet') { + if($found_resultset_class && $found_resultset_class ne $preset_resultset_class) { + carp "We found ResultSet class '$found_resultset_class' matching '$results_by_source_name->{$source_name}', but it seems " + . "that you had already set the '$results_by_source_name->{$source_name}' resultet to '$preset_resultset_class' instead"; } } - elsif($rs_class ||= $default_resultset_class) { - $class->ensure_class_loaded($rs_class); - if(!$rs_class->isa("DBIx::Class::ResultSet")) { - carp "load_namespaces found ResultSet class $rs_class that does not subclass DBIx::Class::ResultSet"; + # elsif - there may be *no* default_resultset_class, in which case we fallback to + # DBIx::Class::Resultset and there is nothing to check + elsif($found_resultset_class ||= $default_resultset_class) { + $class->ensure_class_loaded($found_resultset_class); + if(!$found_resultset_class->isa("DBIx::Class::ResultSet")) { + carp "load_namespaces found ResultSet class '$found_resultset_class' that does not subclass DBIx::Class::ResultSet"; } - $class->_ns_get_rsrc_instance ($result_class)->resultset_class($rs_class); + $class->_ns_get_rsrc_instance ($result_class)->resultset_class($found_resultset_class); } - my $source_name = $class->_ns_get_rsrc_instance ($result_class)->source_name || $result; + my $source_name = $class->_ns_get_rsrc_instance ($result_class)->source_name || $source_name; push(@to_register, [ $source_name, $result_class ]); } } - foreach (sort keys %resultsets) { - carp "load_namespaces found ResultSet class $_ with no " - . 'corresponding Result class'; + foreach (sort keys %$resultsets_by_source_name) { + carp "load_namespaces found ResultSet class '$resultsets_by_source_name->{$_}' " + .'with no corresponding Result class'; } Class::C3->reinitialize if DBIx::Class::_ENV_::OLD_MRO; @@ -367,7 +364,7 @@ sub load_classes { } } else { my @comp = map { substr $_, length "${class}::" } - $class->_findallmod; + $class->_findallmod($class); $comps_for{$class} = \@comp; } @@ -564,7 +561,7 @@ Lists names of all the sources registered on this Schema object. =cut -sub sources { return keys %{shift->source_registrations}; } +sub sources { keys %{shift->source_registrations} } =head2 source @@ -618,8 +615,7 @@ Retrieves the Result class name for the given source name. =cut sub class { - my ($self, $source_name) = @_; - return $self->source($source_name)->result_class; + return shift->source(shift)->result_class; } =head2 txn_do @@ -738,59 +734,42 @@ found in L. =over 4 -=item Arguments: L<$source_name|DBIx::Class::ResultSource/source_name>, \@data; +=item Arguments: L<$source_name|DBIx::Class::ResultSource/source_name>, [ \@column_list, \@row_values+ ] | [ \%col_data+ ] -=item Return Value: L<\@$results|DBIx::Class::Manual::ResultClass> | undef +=item Return Value: L<\@result_objects|DBIx::Class::Manual::ResultClass> (scalar context) | L<@result_objects|DBIx::Class::Manual::ResultClass> (list context) =back -Pass this method a resultsource name, and an arrayref of -arrayrefs. The arrayrefs should contain a list of column names, -followed by one or many sets of matching data for the given columns. - -In void context, C in L is used -to insert the data, as this is a fast method. However, insert_bulk currently -assumes that your datasets all contain the same type of values, using scalar -references in a column in one row, and not in another will probably not work. +A convenience shortcut to L. Equivalent to: -Otherwise, each set of data is inserted into the database using -L, and an arrayref of the Result -objects is returned. + $schema->resultset($source_name)->populate([...]); -e.g. +=over 4 - $schema->populate('Artist', [ - [ qw/artistid name/ ], - [ 1, 'Popular Band' ], - [ 2, 'Indie Band' ], - ... - ]); +=item NOTE -Since wantarray context is basically the same as looping over $rs->create(...) -you won't see any performance benefits and in this case the method is more for -convenience. Void context sends the column information directly to storage -using s bulk insert method. So the performance will be much better for -storages that support this method. +The context of this method call has an important effect on what is +submitted to storage. In void context data is fed directly to fastpath +insertion routines provided by the underlying storage (most often +L), bypassing the L and +L calls on the +L class, including any +augmentation of these methods provided by components. For example if you +are using something like L to create primary +keys for you, you will find that your PKs are empty. In this case you +will have to explicitly force scalar or list context in order to create +those values. -Because of this difference in the way void context inserts rows into your -database you need to note how this will effect any loaded components that -override or augment insert. For example if you are using a component such -as L to populate your primary keys you MUST use -wantarray context if you want the PKs automatically created. +=back =cut sub populate { my ($self, $name, $data) = @_; - if(my $rs = $self->resultset($name)) { - if(defined wantarray) { - return $rs->populate($data); - } else { - $rs->populate($data); - } - } else { - $self->throw_exception("$name is not a resultset"); - } + my $rs = $self->resultset($name) + or $self->throw_exception("'$name' is not a resultset"); + + return $rs->populate($data); } =head2 connection @@ -817,11 +796,13 @@ sub connection { my ($self, @info) = @_; return $self if !@info && $self->storage; - my ($storage_class, $args) = ref $self->storage_type ? - ($self->_normalize_storage_type($self->storage_type),{}) : ($self->storage_type, {}); + my ($storage_class, $args) = ref $self->storage_type + ? $self->_normalize_storage_type($self->storage_type) + : $self->storage_type + ; + + $storage_class =~ s/^::/DBIx::Class::Storage::/; - $storage_class = 'DBIx::Class::Storage'.$storage_class - if $storage_class =~ m/^::/; try { $self->ensure_class_loaded ($storage_class); } @@ -830,7 +811,8 @@ sub connection { "Unable to load storage class ${storage_class}: $_" ); }; - my $storage = $storage_class->new($self=>$args); + + my $storage = $storage_class->new( $self => $args||{} ); $storage->connect_info(\@info); $self->storage($storage); return $self; @@ -853,7 +835,7 @@ sub _normalize_storage_type { =item Arguments: $target_namespace, $additional_base_class? -=item Retur Value: $new_schema +=item Return Value: $new_schema =back @@ -1019,7 +1001,7 @@ sub svp_rollback { Clones the schema and its associated result_source objects and returns the copy. The resulting copy will have the same attributes as the source schema, -except for those attributes explicitly overriden by the provided C<%attrs>. +except for those attributes explicitly overridden by the provided C<%attrs>. =cut @@ -1076,7 +1058,6 @@ default behavior will provide a detailed stack trace. =cut -my $false_exception_action_warned; sub throw_exception { my $self = shift; @@ -1089,13 +1070,12 @@ sub throw_exception { ." (original error: $_[0])" ); } - elsif(! $false_exception_action_warned++) { - carp ( - "The exception_action handler installed on $self returned false instead" - .' of throwing an exception. This behavior has been deprecated, adjust your' - .' handler to always rethrow the supplied error.' - ); - } + + carp_unique ( + "The exception_action handler installed on $self returned false instead" + .' of throwing an exception. This behavior has been deprecated, adjust your' + .' handler to always rethrow the supplied error.' + ); } DBIx::Class::Exception->throw($_[0], $self->stacktrace); @@ -1114,8 +1094,7 @@ Attempts to deploy the schema to the current storage using L. See L for a list of values for C<\%sqlt_args>. The most common value for this would be C<< { add_drop_table => 1 } >> to have the SQL produced include a C statement for each table -created. For quoting purposes supply C and -C. +created. For quoting purposes supply C. Additionally, the DBIx::Class parser accepts a C parameter as a hash ref or an array ref, containing a list of source to deploy. If present, then