Merge 'DBIx-Class-current' into 'load_namespaces'
Brandon L. Black [Thu, 27 Jul 2006 05:58:59 +0000 (05:58 +0000)]
r7351@moloko (orig r2607):  blblack | 2006-07-23 16:31:35 -0500
 r7299@moloko (orig r2592):  matthewt | 2006-07-22 19:23:56 -0500
 yes, I didn't get the merge quite right. again.
 r7347@moloko (orig r2603):  matthewt | 2006-07-23 14:31:41 -0500
 yeah, yeah, yeah

r7352@moloko (orig r2608):  blblack | 2006-07-23 16:33:15 -0500
version bump to 0.07999_01, where I guess it will sit until we release such a thing
r7376@moloko (orig r2618):  blblack | 2006-07-26 21:31:56 -0500
 r7337@moloko (orig r2593):  blblack | 2006-07-23 09:49:21 -0500
 first draft of storage exception stuff

r7377@moloko (orig r2619):  blblack | 2006-07-26 21:32:04 -0500
 r7338@moloko (orig r2594):  blblack | 2006-07-23 10:49:14 -0500
 that was stupid

r7378@moloko (orig r2620):  blblack | 2006-07-26 21:32:11 -0500
 r7339@moloko (orig r2595):  blblack | 2006-07-23 11:18:32 -0500
 infect the storage subdrivers

r7379@moloko (orig r2621):  blblack | 2006-07-26 21:32:26 -0500
 r7344@moloko (orig r2600):  blblack | 2006-07-23 13:58:53 -0500
 further refinements to storage_exceptions

r7380@moloko (orig r2622):  blblack | 2006-07-26 21:43:46 -0500
 r7345@moloko (orig r2601):  blblack | 2006-07-23 14:07:58 -0500
 better DESTROY handling

r7381@moloko (orig r2623):  blblack | 2006-07-26 21:43:54 -0500
 r7349@moloko (orig r2605):  blblack | 2006-07-23 15:34:15 -0500
 force/assume RaiseError/PrintError
 also fixed small issue in DESTROY from last update

r7382@moloko (orig r2624):  blblack | 2006-07-26 21:44:01 -0500
 r7356@moloko (orig r2609):  blblack | 2006-07-24 01:36:20 -0500
 Storage holds a weakref to $schema now
 Storage uses Schema to throw exceptions
 Schema has "exception_action" accessor for custom exception objects and such

r7383@moloko (orig r2625):  blblack | 2006-07-26 21:44:10 -0500
 r7374@moloko (orig r2616):  blblack | 2006-07-26 20:48:37 -0500
 allow exception_action to suppress things, clean up docs

r7384@moloko (orig r2626):  blblack | 2006-07-26 21:44:16 -0500
 r7375@moloko (orig r2617):  blblack | 2006-07-26 21:31:19 -0500
 add a couple of tests for the new exception stuff

r7385@moloko (orig r2627):  blblack | 2006-07-26 21:49:49 -0500
 r7358@moloko (orig r2610):  castaway | 2006-07-24 07:16:00 -0500
 1) Add an explicit error to columns_info_for if the given schema/table combination produces no results.
 2) Upper case the input for Oracle.

 r7359@moloko (orig r2611):  blblack | 2006-07-24 08:20:56 -0500
 next::method, not next::columns_info_for
 r7365@moloko (orig r2614):  blblack | 2006-07-25 19:25:24 -0500
 very minor cleanups to columns_info_for
 r7370@moloko (orig r2615):  matthewt | 2006-07-26 11:13:59 -0500
 bugfix for pathological prefetch case

r7387@moloko (orig r2629):  dwc | 2006-07-27 00:11:20 -0500
 r11078@fortuna (orig r2628):  dwc | 2006-07-27 01:07:24 -0400
 FAQ update: Minor correction from Richard Jolly, mention search_rs, wrap lines, and minor grammar corrections

1  2 
lib/DBIx/Class/Schema.pm

diff --combined lib/DBIx/Class/Schema.pm
@@@ -12,6 -12,7 +12,7 @@@ __PACKAGE__->mk_classdata('class_mappin
  __PACKAGE__->mk_classdata('source_registrations' => {});
  __PACKAGE__->mk_classdata('storage_type' => '::DBI');
  __PACKAGE__->mk_classdata('storage');
+ __PACKAGE__->mk_classdata('exception_action');
  
  =head1 NAME
  
@@@ -278,155 -279,6 +279,155 @@@ sub load_classes 
    }
  }
  
 +=head2 load_namespaces
 +
 +=over 4
 +
 +=item Arguments: %options?
 +
 +=back
 +
 +This is an alternative to L</load_classes> above which assumes an alternative
 +layout for automatic class loading.  It assumes that all ResultSource classes
 +to be loaded are underneath a sub-namespace of the schema called
 +"ResultSource", any corresponding ResultSet classes to be underneath a
 +sub-namespace of the schema called "ResultSet", and any corresponing
 +Result classes to be underneath a sub-namespace of the schema called "Result".
 +
 +All of those sub-namespaces are configurable if you don't like the defaults,
 +via the options C<resultsource_namespace>, C<resultset_namespace>, and
 +C<result_namespace>, respectively.
 +
 +If (and only if) you specify the option C<default_resultset_base>, any found
 +ResultSource classes that have no manually-created corresponding ResultSet
 +class will have one created for them in memory in the C<resultset_namespace>,
 +based on C<default_resultset_base>.
 +
 +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<+>.
 +
 +This method requires L<Module::Find> to be installed on the system.
 +
 +Example:
 +
 +  # load My::Schema::ResultSource::CD, My::Schema::ResultSource::Artist,
 +  #    My::Schema::ResultSet::CD, etc...
 +  My::Schema->load_namespaces;
 +
 +  # Override everything...
 +  My::Schema->load_namespaces(
 +    resultsource_namespace => 'RSources',
 +    resultset_namespace => 'RSets',
 +    result_namespace => 'Results',
 +    default_resultset_base => 'RSetBase',
 +  );
 +  # ... and if there is a My::Schema::RSources::Foo, but no matching
 +  #   My::Schema::RSets::Foo, then My::Schema::RSets::Foo will be created
 +  #   for you in memory, based on My::Schema::RSetBase
 +
 +  # Put things in other namespaces
 +  My::Schema->load_namespaces(
 +    resultsource_namespace => '+Some::Place::RSources',
 +    resultset_namespace => '+Another::Place::RSets',
 +    result_namespace => '+Crazy::Stuff::Results',
 +    default_resultset_base => '+You::Never::Know::RSetBase',
 +  );
 +
 +
 +=cut
 +
 +sub load_namespaces {
 +  my ($class, %args) = @_;
 +
 +  my $resultsource_namespace = $args{resultsource_namespace} || 'ResultSource';
 +  my $resultset_namespace    = $args{resultset_namespace}    || 'ResultSet';
 +  my $result_namespace       = $args{result_namespace}       || 'Result';
 +  my $default_resultset_base = $args{default_resultset_base};
 +
 +  foreach ($resultsource_namespace, $resultset_namespace,
 +           $result_namespace,       $default_resultset_base) {
 +    next if !$_;
 +    $_ = $class . '::' . $_ if !s/^\+//;
 +  }
 +
 +  eval "require Module::Find";
 +  $class->throw_exception("Couldn't load Module::Find ($@)") if $@;
 +
 +  my %sources = map { (substr($_, length "${resultsource_namespace}::"), $_) }
 +      Module::Find::findallmod($resultsource_namespace);
 +
 +  my %resultsets = map { (substr($_, length "${resultset_namespace}::"), $_) }
 +      Module::Find::findallmod($resultset_namespace);
 +
 +  my %results = map { (substr($_, length "${result_namespace}::"), $_) }
 +      Module::Find::findallmod($result_namespace);
 +
 +  my @to_register;
 +  {
 +    no warnings qw/redefine/;
 +    local *Class::C3::reinitialize = sub { };
 +    use warnings qw/redefine/;
 +
 +    foreach my $source (keys %sources) {
 +      my $source_class = $sources{$source};
 +      $class->ensure_class_loaded($source_class);
 +      $source_class->source_name($source) unless $source_class->source_name;
 +
 +      my $rs_class = delete $resultsets{$source};
 +      my $rs_set = $source_class->resultset_class;
 +      if(!$rs_set || $rs_set eq 'DBIx::Class::ResultSet') {
 +        if($rs_class) {
 +          $class->ensure_class_loaded($rs_class);
 +          $source_class->resultset_class($rs_class);
 +        }
 +        elsif($default_resultset_base) {
 +          $class->ensure_class_loaded($default_resultset_base);
 +          $rs_class = "$resultset_namespace\::$source";
 +          { no strict qw/refs/; @{"$rs_class\::ISA"} = ($default_resultset_base); }
 +          $source_class->resultset_class($rs_class);
 +        }
 +      }
 +      elsif($rs_set && $rs_class) {
 +        warn "We found ResultSet class '$rs_class' for '$source', but it seems "
 +           . "that you had already set '$source' to use '$rs_set' instead";
 +      }
 +
 +      push(@to_register, [ $source_class->source_name, $source_class ]);
 +    }
 +  }
 +
 +  foreach (sort keys %resultsets) {
 +    warn "load_namespaces found ResultSet class $_ with no "
 +      . 'corresponding ResultSource';
 +  }
 +
 +  Class::C3->reinitialize;
 +  $class->register_class(@$_) for (@to_register);
 +
 +  foreach my $source (keys %sources) {
 +    my $r_class = delete $results{$source};
 +    if($r_class) {
 +      my $r_set = $class->source($source)->result_class;
 +      if(!$r_set || $r_set eq $sources{$source}) {
 +        $class->ensure_class_loaded($r_class);
 +        $class->source($source)->result_class($r_class);
 +      }
 +      else {
 +        warn "We found Result class '$r_class' for '$source', but it seems "
 +           . "that you had already set '$source' to use '$r_set' instead";
 +      }
 +    }
 +  }
 +
 +  foreach (sort keys %results) {
 +    warn "load_namespaces found Result class $_ with no "
 +      . 'corresponding ResultSource';
 +  }
 +
 +  return;
 +}
 +
  =head2 compose_connection
  
  =over 4
@@@ -601,7 -453,7 +602,7 @@@ sub connection 
    $self->throw_exception(
      "No arguments to load_classes and couldn't load ${storage_class} ($@)"
    ) if $@;
-   my $storage = $storage_class->new;
+   my $storage = $storage_class->new($self);
    $storage->connect_info(\@info);
    $self->storage($storage);
    return $self;
@@@ -780,6 -632,7 +781,7 @@@ sub clone 
      my $new = $source->new($source);
      $clone->register_source($moniker => $new);
    }
+   $clone->storage->set_schema($clone) if $clone->storage;
    return $clone;
  }
  
@@@ -819,6 -672,38 +821,38 @@@ sub populate 
    return @created;
  }
  
+ =head2 exception_action
+ =over 4
+ =item Arguments: $code_reference
+ =back
+ If C<exception_action> is set for this class/object, L</throw_exception>
+ will prefer to call this code reference with the exception as an argument,
+ rather than its normal <croak> action.
+ Your subroutine should probably just wrap the error in the exception
+ object/class of your choosing and rethrow.  If, against all sage advice,
+ you'd like your C<exception_action> to suppress a particular exception
+ completely, simply have it return true.
+ Example:
+    package My::Schema;
+    use base qw/DBIx::Class::Schema/;
+    use My::ExceptionClass;
+    __PACKAGE__->exception_action(sub { My::ExceptionClass->throw(@_) });
+    __PACKAGE__->load_classes;
+    # or:
+    my $schema_obj = My::Schema->connect( .... );
+    $schema_obj->exception_action(sub { My::ExceptionClass->throw(@_) });
+    # suppress all exceptions, like a moron:
+    $schema_obj->exception_action(sub { 1 });
  =head2 throw_exception
  
  =over 4
  =back
  
  Throws an exception. Defaults to using L<Carp::Clan> to report errors from
- user's perspective.
+ user's perspective.  See L</exception_action> for details on overriding
+ this method's behavior.
  
  =cut
  
  sub throw_exception {
-   my ($self) = shift;
-   croak @_;
+   my $self = shift;
+   croak @_ if !$self->exception_action || !$self->exception_action->(@_);
  }
  
  =head2 deploy (EXPERIMENTAL)