Re: Better handling of constraint failures?

[prev] [thread] [next] [Date index for 2004/08/12]

From: Daniel Collis Puro
Subject: Re: Better handling of constraint failures?
Date: 20:01 on 12 Aug 2004
--=-aGovAYBNOr+jCG92j7+q
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

Folks,

Here's the solution I came up with: Is this something that can make it
into Class::DBI proper?

I overrode add_constraint() in my Class::DBI subclass and have it now
return more useful information into %info in _croak().

sub add_constraint {
    my $class = shift;
    $class->_invalid_object_method('add_constraint()') if ref $class;
    my $name = shift or return $class->_croak("Constraint needs a
name");
    my $column = $class->find_column(+shift)
      or return $class->_croak("Constraint $name needs a valid column");
    my $code = shift
      or return $class->_croak("Constraint $name needs a code
reference");
    return $class->_croak("Constraint $name '$code' is not a code
reference")
      unless ref($code) eq "CODE";
    
    $column->is_constrained(1);
    $class->add_trigger(
"before_set_$column" => sub {
    my ($self, $value, $column_values) = @_;
    $code->($value, $self, $column, $column_values)
      or return $self->_croak(
      "$class $column fails '$name' constraint with '$value'"
            ,                                                    #new
      (                                                    #new
       exception_type=>'constraint_failure',    #new
       column=>$column,                            #new
       value=>$value,                                #new
       constraint_name=>$name                 #new
      )                                                   #new
            );
}
       );
}

I then catch that information in my overridden croak():


################################################################################
sub _croak {
    my ($self, $message, %info) = @_;
    Carp::croak(@_) if(not %info);
    if  ($info{exception_type} eq 'constraint_failure') {
$self->{__constraint_errors}->{$info{column}}=\%info;
    } else {
Carp::croak(@_);
    }
}
########################################


And then I use it in my scripts like this:

$namespace->namespace($new_value);
$namespace->description($new_description);

my $errors=$object->{__constraint_errors};
if(not %$errors){
  $namespace->update();
}

and then I pass $errors to my form creating subroutine to output errors
for specific fields if they exist.

This minor tweak allows you to catch constraint errors and then
re-present a form to a user WITHOUT having to code my validation
routines in two different places.

The sketchy bit is putting info into the $object hash directly. . .

I think it should be expected that folks override _croak(), but
overriding add_constraint is a bit more troubling.  

Fortunately, this shouldn't have a big impact on folks programming with
Class::DBI because I'm just populating %info with some meaningful stuff.
If you don't override _croak(), stuff will croak() per the usual. 

The parts I changed in add_constraint() are marked with "#new" at the
end. I can give you a diff, if you want it.

Thoughts?  I think more informational exceptions would generally be a
nice feature.

-DJCP





        -- 
        Dan Collis Puro
Chief Engineer
GeekUprising Internet Consultants
http://www.geekuprising.com
dan@xxxxxxxxxxxx.xxx
781-775-3942

--=-aGovAYBNOr+jCG92j7+q
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 7bit

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN">
<HTML>
<HEAD>
  <META HTTP-EQUIV="Content-Type" CONTENT="text/html; CHARSET=UTF-8">
  <META NAME="GENERATOR" CONTENT="GtkHTML/3.0.10">
</HEAD>
<BODY>
Folks,<BR>
<BR>
Here's the solution I came up with: Is this something that can make it into Class::DBI proper?<BR>
<BR>
I overrode add_constraint() in my Class::DBI subclass and have it now return more useful information into %info in _croak().<BR>
<BR>
sub add_constraint {<BR>
&nbsp;&nbsp;&nbsp; my $class = shift;<BR>
&nbsp;&nbsp;&nbsp; $class-&gt;_invalid_object_method('add_constraint()') if ref $class;<BR>
&nbsp;&nbsp;&nbsp; my $name = shift or return $class-&gt;_croak(&quot;Constraint needs a name&quot;);<BR>
&nbsp;&nbsp;&nbsp; my $column = $class-&gt;find_column(+shift)<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; or return $class-&gt;_croak(&quot;Constraint $name needs a valid column&quot;);<BR>
&nbsp;&nbsp;&nbsp; my $code = shift<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; or return $class-&gt;_croak(&quot;Constraint $name needs a code reference&quot;);<BR>
&nbsp;&nbsp;&nbsp; return $class-&gt;_croak(&quot;Constraint $name '$code' is not a code reference&quot;)<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unless ref($code) eq &quot;CODE&quot;;<BR>
&nbsp;&nbsp;&nbsp; <BR>
&nbsp;&nbsp;&nbsp; $column-&gt;is_constrained(1);<BR>
&nbsp;&nbsp;&nbsp; $class-&gt;add_trigger(<BR>
&quot;before_set_$column&quot; =&gt; sub {<BR>
&nbsp;&nbsp;&nbsp; my ($self, $value, $column_values) = @_;<BR>
&nbsp;&nbsp;&nbsp; $code-&gt;($value, $self, $column, $column_values)<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; or return $self-&gt;_croak(<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;$class $column fails '$name' constraint with '$value'&quot;<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #new<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #new<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exception_type=&gt;'constraint_failure',&nbsp;&nbsp;&nbsp; #new<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; column=&gt;$column,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #new<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value=&gt;$value,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #new<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constraint_name=&gt;$name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #new<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #new<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<BR>
}<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<BR>
}<BR>
<BR>
I then catch that information in my overridden croak():<BR>
<BR>
<BR>
################################################################################<BR>
sub _croak {<BR>
&nbsp;&nbsp;&nbsp; my ($self, $message, %info) = @_;<BR>
&nbsp;&nbsp;&nbsp; Carp::croak(@_) if(not %info);<BR>
&nbsp;&nbsp;&nbsp; if&nbsp; ($info{exception_type} eq 'constraint_failure') {<BR>
$self-&gt;{__constraint_errors}-&gt;{$info{column}}=\%info;<BR>
&nbsp;&nbsp;&nbsp; } else {<BR>
Carp::croak(@_);<BR>
&nbsp;&nbsp;&nbsp; }<BR>
}<BR>
########################################<BR>
<BR>
<BR>
And then I use it in my scripts like this:<BR>
<BR>
$namespace-&gt;namespace($new_value);<BR>
$namespace-&gt;description($new_description);<BR>
<BR>
my $errors=$object-&gt;{__constraint_errors};<BR>
if(not %$errors){<BR>
&nbsp; $namespace-&gt;update();<BR>
}<BR>
<BR>
and then I pass $errors to my form creating subroutine to output errors for specific fields if they exist.<BR>
<BR>
This minor tweak allows you to catch constraint errors and then re-present a form to a user WITHOUT having to code my validation routines in two different places.<BR>
<BR>
The sketchy bit is putting info into the $object hash directly. . .<BR>
<BR>
I think it should be expected that folks override _croak(), but overriding add_constraint is a bit more troubling.&nbsp; <BR>
<BR>
Fortunately, this shouldn't have a big impact on folks programming with Class::DBI because I'm just populating %info with some meaningful stuff. If you don't override _croak(), stuff will croak() per the usual. <BR>
<BR>
The parts I changed in add_constraint() are marked with &quot;#new&quot; at the end. I can give you a diff, if you want it.<BR>
<BR>
Thoughts?&nbsp; I think more informational exceptions would generally be a nice feature.<BR>
<BR>
-DJCP<BR>
<BR>
<BR>
<PRE><TABLE CELLSPACING="0" CELLPADDING="0" WIDTH="100%">
<TR>
</TR>
<TR>
</TR>
<TR>
<TD>
<BR>
<PRE>-- 
Dan Collis Puro
Chief Engineer
GeekUprising Internet Consultants
http://www.geekuprising.com
dan@xxxxxxxxxxxx.xxx
781-775-3942</PRE>
</TD>
</TR>
</TABLE>
</PRE>
</BODY>
</HTML>

--=-aGovAYBNOr+jCG92j7+q--

Re: Better handling of constraint failures?
Drew Taylor 17:27 on 12 Aug 2004

Re: Better handling of constraint failures?
Clayton L. Scott 19:29 on 12 Aug 2004

Re: Better handling of constraint failures?
Drew Taylor 19:54 on 12 Aug 2004

Re: Better handling of constraint failures?
Clayton L. Scott 19:53 on 12 Aug 2004

Re: Better handling of constraint failures?
Clayton L. Scott 20:01 on 12 Aug 2004

Re: Better handling of constraint failures?
Perrin Harkins 19:51 on 12 Aug 2004

Re: Better handling of constraint failures?
Daniel Collis Puro 20:01 on 12 Aug 2004

Re: Better handling of constraint failures?
Clayton L. Scott 20:04 on 12 Aug 2004

Re: Better handling of constraint failures?
Daniel Collis Puro 20:42 on 12 Aug 2004

Re: Better handling of constraint failures?
Clayton Scott 21:49 on 12 Aug 2004

Re: Better handling of constraint failures?
Tony Bowden 13:52 on 24 Oct 2004

Generated at 11:34 on 01 Dec 2004 by mariachi v0.52