2012-07-12 13 views
18

¿Hay alguna forma de rescatar todas las excepciones bajo un cierto espacio de nombres?¿Cómo rescatar todas las excepciones bajo un cierto espacio de nombres?

Por ejemplo, deseo rescatar todas las excepciones Errno :: * (Errno :: ECONNRESET, Errno :: ETIMEDOUT). Puedo seguir y enumerarlos en mi línea de excepción, pero me preguntaba si puedo hacer algo como eso.

begin 
    # my code 
rescue Errno 
    # handle exception 
end 

La idea anterior no parece funcionar, por lo tanto, ¿hay algo similar que pueda funcionar?

+0

¿Intentó rescatar todo, revisar el espacio de nombres y volver a subirlo si no? –

+0

@dave Me preguntaba sobre todo si hay una manera más fácil/más limpia de atrapar excepciones basadas en el espacio de nombres. – gylaz

+0

No, a menos que haya algo en común, como se indica en las respuestas. –

Respuesta

24

Todo el Errno exceptions subclass SystemCallError:

Módulo Errno se crea dinámicamente para mapear estos errores del sistema operativo para las clases de Ruby, con cada número de error al generar su propia subclase de SystemCallError. Como la subclase se crea en el módulo Errno, su nombre comenzará en Errno::.

por lo que podría atrapar SystemCallError y luego hacer una simple comprobación nombre:

rescue SystemCallError => e 
    raise e if(e.class.name.start_with?('Errno::')) 
    # do your thing... 
end 
3

Todas las clases en Errno son subclases de SystemCallError. Y todas las subclases de SystemCallError son clases bajo Errno. Los 2 conjuntos son idénticos, así que solo rescata SystemCallError. Esto supone que no está utilizando una lib externa que se suma a una y no a la otra.

verificar la identidad de los 2 juegos (utilizando active_support):

Errno.constants.map {|name| 
    Errno.const_get(name) 
}.select{|const| 
    Class === const 
}.uniq.map(&:to_s).sort == 
    SystemCallError.subclasses.map(&:to_s).sort 

Esto devuelve true para mí.

Así que, aplicado a su ejemplo:

begin 
    # my code 
rescue SystemCallError 
    # handle exception 
end 
+0

"todas las subclases de SystemCallError son clases bajo Errno" no necesariamente se mantienen. Todas las excepciones de Errno están documentadas como SystemCallErrors, pero no hay garantía de que todos los SystemCallErrors sean Errnos. –

+0

@muistooshort ¿mi código de verificación no funciona entonces? Asumo que la búsqueda de 'ObjectSpace' es suficiente para descubrir clases no documentadas. A menos que una lib de Ruby incorporada agregue más subclases. – Kelvin

+0

El problema es la parte "Esto devuelve' verdadero' para mí "y, en particular, el * para mí *. Una gema (o incluso la aplicación misma) podría proporcionar una subclase de SystemCallError que no es un Errno. –

1

Aquí es una solución más genérica, en el caso de que quería rescatar algunos tipos ErrNo y no otros.

crear un módulo personalizado para ser incluido por todas las clases de error que queremos rescatar

module MyErrnoModule; end 

Personalizar esta matriz a su gusto, hasta el "cada uno" llamada.

Errno.constants.map {|name| 
    Errno.const_get(name) 
}.select{|const| 
    Class === const 
}.uniq.each {|klass| 
    klass.class_eval { 
    include MyErrnoModule 
    } 
} 

prueba:

begin 
    raise Errno::EPERM 
rescue MyErrnoModule 
    p "rescued #{$!.inspect}" 
end 

Resultado de la prueba:

"rescued #<Errno::EPERM: Operation not permitted>" 

Conjeturaría este realiza un poco mejor que una solución que necesita para comprobar el nombre de la excepción.

3

Aquí hay otra interesante alternative. Se puede adaptar a lo que quieras.

pegar parte más interesante:

def match_message(regexp) 
    lambda{ |error| regexp === error.message } 
end 

begin 
    raise StandardError, "Error message about a socket." 
rescue match_message(/socket/) => error 
    puts "Error #{error} matches /socket/; ignored." 
end 

Ver el sitio original para la solución de rubí 1.8.7.

Resulta que lambda no aceptó mis versiones ruby ​​más recientes. Parece que la opción es utilizar lo que funcionó en 1.8.7 pero eso es más lenta IM (para crear una nueva clase en todas las comparaciones Así que no recomiendo usarlo y ni siquiera lo he probado:.

def exceptions_matching(&block) 
    Class.new do 
    def self.===(other) 
     @block.call(other) 
    end 
    end.tap do |c| 
    c.instance_variable_set(:@block, block) 
    end 
end 

begin 
    raise "FOOBAR: We're all doomed!" 
rescue exceptions_matching { |e| e.message =~ /^FOOBAR/ } 
    puts "rescued!" 
end 

Si Alguien sabe cuando ruby ​​eliminó el soporte lambda en rescue por favor comente

+1

+1 Nunca supe que podría pasar un lambda como una excepción matcher – Kelvin

+1

'clase o módulo requerido para la cláusula de rescate (TypeError)' parece que no funciona? –

+0

Interesante porque me ha funcionado a la hora de escribir. Si revisas la fuente a la que me refería, puedes ver que no fue mi invención y funcionó para otras personas. Actualmente solo puedo hacerlo funcionar bajo ruby ​​1.9.x. También mis upvotes muestran que ha funcionado para algunas personas. También veo a una persona que dice que funciona en 2.1.2 (https://gist.github.com/panthomakos/1230515#gistcomment-1298427). Ahora me cuesta encontrar dónde usé algo similar ya que me pregunto si dejó de funcionar silenciosamente. – akostadinov

Cuestiones relacionadas