Buscar..


Un bloque de manejo de error básico

Hagamos una función para dividir dos números, eso es muy confiado en su entrada:

def divide(x, y)
  return x/y
end

Esto funcionará bien para muchas entradas:

> puts divide(10, 2)
5

Pero no todos

> puts divide(10, 0)
ZeroDivisionError: divided by 0

> puts divide(10, 'a')
TypeError: String can't be coerced into Fixnum

Podemos reescribir la función envolviendo la operación de división de riesgo en un bloque de begin... end para verificar errores, y usar una cláusula de rescue para emitir un mensaje y devolver nil si hay un problema.

def divide(x, y)
  begin
    return x/y
  rescue
    puts "There was an error"
    return nil
  end
end

> puts divide(10, 0)
There was an error

> puts divide(10, 'a')
There was an error

Guardando el error

Puede guardar el error si desea utilizarlo en la cláusula de rescue

def divide(x, y)
  begin
    x/y
  rescue => e
    puts "There was a %s (%s)" % [e.class, e.message]
    puts e.backtrace
  end
end

> divide(10, 0)
There was a ZeroDivisionError (divided by 0)
       from (irb):10:in `/'
       from (irb):10
       from /Users/username/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'

> divide(10, 'a')
There was a TypeError (String can't be coerced into Fixnum)
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/workspace.rb:87:in `eval'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/workspace.rb:87:in `evaluate'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/context.rb:380:in `evaluate'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:489:in `block (2 levels) in eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:623:in `signal_status'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:486:in `block in eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:246:in `block (2 levels) in each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:232:in `loop'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:232:in `block in each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:231:in `catch'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:231:in `each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:485:in `eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:395:in `block in start'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:394:in `catch'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:394:in `start'
/Users/username/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'

Comprobación de errores diferentes

Si desea hacer cosas diferentes según el tipo de error, use varias cláusulas de rescue , cada una con un tipo de error diferente como argumento.

def divide(x, y)
  begin
    return x/y
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
    return nil
  rescue TypeError
    puts "Division only works on numbers!"
    return nil
  end
end

> divide(10, 0)
Don't divide by zero!

> divide(10, 'a')
Division only works on numbers!

Si desea guardar el error para utilizarlo en el bloque de rescue :

rescue ZeroDivisionError => e

Utilice una cláusula de rescue sin argumentos para detectar errores de un tipo no especificado en otra cláusula de rescue .

def divide(x, y)
  begin
    return x/y
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
    return nil
  rescue TypeError
    puts "Division only works on numbers!"
    return nil
  rescue => e
    puts "Don't do that (%s)" % [e.class]
    return nil
  end
end

> divide(nil, 2)
Don't do that (NoMethodError)

En este caso, tratar de dividir nil por 2 no es un ZeroDivisionError o un TypeError , por lo que se maneja con la cláusula de rescue predeterminada, que imprime un mensaje que nos permite saber que era un NoMethodError .

Reintentando

En una cláusula de rescue , puede usar retry para ejecutar la cláusula de begin nuevamente, probablemente después de cambiar la circunstancia que causó el error.

def divide(x, y)
  begin
    puts "About to divide..."
    return x/y
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
    y = 1
    retry
  rescue TypeError
    puts "Division only works on numbers!"
    return nil
  rescue => e
    puts "Don't do that (%s)" % [e.class]
    return nil
  end
end

Si pasamos parámetros que sabemos que causarán un TypeError , la cláusula de begin se ejecuta (marcada aquí imprimiendo "Acerca de la división") y el error se detecta como antes, y se devuelve nil :

> divide(10, 'a')
About to divide...
Division only works on numbers!
 => nil

Pero si pasamos parámetros que causarán un ZeroDivisionError , se ejecuta la cláusula begin , se captura el error, el divisor cambia de 0 a 1, y luego el retry hace que el bloque begin se ejecute nuevamente (desde arriba), ahora con un diferente y La segunda vez no hay ningún error y la función devuelve un valor.

> divide(10, 0)
About to divide...     # First time, 10 ÷ 0
Don't divide by zero!
About to divide...     # Second time 10 ÷ 1
=> 10

Comprobando si no se produjo ningún error

Puede usar una cláusula else para el código que se ejecutará si no se produce ningún error.

def divide(x, y)
  begin
    z = x/y
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
  rescue TypeError
    puts "Division only works on numbers!"
    return nil
  rescue => e
    puts "Don't do that (%s)" % [e.class]
    return nil
  else
    puts "This code will run if there is no error."
    return z
  end
end

La cláusula else no se ejecuta si hay un error que transfiere el control a una de las cláusulas de rescue :

> divide(10,0)
Don't divide by zero!
=> nil

Pero si no se produce ningún error, la cláusula else ejecuta:

> divide(10,2)
This code will run if there is no error.
=> 5

Tenga en cuenta que la cláusula else no se ejecutará si regresa de la cláusula begin

def divide(x, y)
  begin
    z = x/y
    return z                 # Will keep the else clause from running!
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
  else
    puts "This code will run if there is no error."
    return z
  end
end

> divide(10,2)
=> 5

Código que siempre debe correr

Utilice un ensure cláusula si hay código que siempre se desea ejecutar.

def divide(x, y)
  begin
    z = x/y
    return z
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
  rescue TypeError
    puts "Division only works on numbers!"
    return nil
  rescue => e
    puts "Don't do that (%s)" % [e.class]
    return nil
  ensure
    puts "This code ALWAYS runs."
  end
end

La ensure cláusula se ejecutará cuando hay un error:

> divide(10, 0)
Don't divide by zero!   # rescue clause
This code ALWAYS runs.   # ensure clause
=> nil

Y cuando no hay error:

> divide(10, 2)
This code ALWAYS runs.   # ensure clause
=> 5

La cláusula de garantía es útil cuando quiere asegurarse, por ejemplo, de que los archivos están cerrados.

Tenga en cuenta que, a diferencia de la else cláusula, el ensure cláusula se ejecuta antes del begin o rescue cláusula devuelve un valor. Si el ensure cláusula tiene un return que va a anular el return valor de cualquier otra cláusula!



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow