2011-08-15 21 views
9

¿Cuál es la mejor manera de lidiar con las excepciones lanzadas en un método de encadenamiento en Perl? que desea asignar un valor de 0 o undef si cualquiera de los métodos tiro encadenado una excepción¿Cómo manejo los errores en cadenas de métodos en Perl?

Ejemplo de código:

my $x = $obj->get_obj->get_other_obj->get_another_obj->do_something; 

cuál es la mejor manera de hacerlo? ¿Debo completar una declaración try/catch/finally cada vez? El contexto que quiero aplicar es: estoy trabajando en desarrollo web usando Catalyst y DBIC y hago muchos resultados encadenados y si algunos de estos resultados arrojan una excepción solo quiero asignar un valor de 0 o undef y luego tratar este error en la plantilla (estoy usando Template Toolkit). Si hay otra forma de hacerlo sin envolver cada llamada en try/catch, házmelo saber. Si conoce una mejor manera de tratar este tipo de error en el mismo contexto (Catalyst/DBIC/TT), sugiérale. Un ejemplo práctico sería cuando el usuario busca algo y esto no existe.

Respuesta

8

Lo manejo devolviendo un objeto nulo en el punto de falla. Ese objeto responde a cada método simplemente retornando a sí mismo, por lo que sigue haciéndolo hasta que se come los métodos restantes. Al final, mira en $x para ver si es el resultado esperado o este objeto nulo.

He aquí un ejemplo de tal cosa:

use v5.12; 

package Null { 
    my $null = bless {}, __PACKAGE__; 
    sub AUTOLOAD { $null } 
    } 

Por cada método llamado, los autoLoad lo intercepta y devuelve el objeto vacío.

Cuando se produce un error, devuelve uno de estos objetos nulos. En el medio de una cadena de métodos, todavía recuperas un objeto para que Perl no explote cuando llamas al siguiente método.

sub get_other_obj { 
    ...; 
    return Null->new if $error; 
    ...; 
    } 

Al final de la cadena, puede comprobar lo que recibió para ver si se trata de un objeto nulo. Si eso es lo que tienes, algo malo pasó.

Esa es la idea básica. Puede mejorar la clase Null para que recuerde un mensaje y dónde se creó, o agregue algunos métodos polimórficos (como sub is_success { 0 }) para que funcione bien con las interfaces de los objetos que esperaba obtener.

Pensé que había escrito algo largo sobre esto en alguna parte, pero ahora no puedo encontrarlo.

+0

Un problema con esto: los métodos de Setter utilizarán una entrada nula para significar devolver el cur valor de alquiler Por ejemplo, '$ foo-> Name (" David ") establecerá el nombre a' David', y '$ foo-> Name' devolverá el nombre actual. Por lo tanto, un retorno nulo de un método podría ser una entrada válida para otro método. –

+0

Eso no es realmente un problema. Es un no-op. Los métodos posteriores no hacen nada. No está pasando el objeto nulo como argumento; es el referente. Si el método anterior no devuelve un objeto, no puede encadenar de todos modos. –

+0

Lo siento, no entendí. ¿Cómo podría aplicarlo en un DBIC encadenando resultados? – nsbm

-1

Una idea sería crear una clase que use overload para devolver un valor falso cuando se evalúa un objeto de instancia en contextos de cadena/número/booleano, pero aún así permitir que se invoquen métodos sobre él. Un método AUTOLOAD siempre puede devolver $self permitiendo que una cadena de método propague el mismo error.

2

Puede escribir un método de escalar, que envolverá una cadena método de gestión de errores:

my $try = sub { 
    @_ > 1 or return bless {ok => $_[0]} => 'Try'; 

    my ($self, $method) = splice @_, 0, 2; 
    my $ret; 
    eval { 
     $ret = $self->$method(@_); 
    1} or return bless {error => [email protected]} => 'Try'; 
    bless {ok => $ret} => 'Try' 
}; 

{package Try; 
    use overload fallback => 1, '""' => sub {$_[0]{ok}}; 
    sub AUTOLOAD { 
     my ($method) = our $AUTOLOAD =~ /([^:]+)$/; 
     $_[0]{ok} ? $_[0]{ok}->$try($method, @_[1..$#_]) : $_[0] 
    } 
    sub DESTROY {} 
    sub error {$_[0]{error}} 
} 

utilizarlo:

{package Obj; 
    sub new {bless [0]} 
    sub set {$_[0][0] = $_[1]; $_[0]} 
    sub add {$_[0][0] += ($_[1] || 1); $_[0]} 
    sub show {print "Obj: $_[0][0]\n"} 
    sub dies {die "an error occured"} 
} 

my $obj = Obj->new; 

say "ok 1" if $obj->$try(set => 5)->add->add->show; # prints "Obj 7" 
                # and "ok 1" 

say "ok 2" if $obj->$try('dies')->add->add->show; # prints nothing 

say $obj->$try('dies')->add->add->show->error; # prints "an error occured..." 

La primera línea del método $try también permite la siguiente sintaxis :

say "ok 3" if $obj->$try->set(5)->add->add->show; 
Cuestiones relacionadas