Sí, ensure
garantiza que el código siempre se evalúe. Es por eso que se llama ensure
. Por lo tanto, es equivalente a Java's y C# 's finally
.
El flujo general de begin
/rescue
/else
/ensure
/end
se parece a esto:
begin
# something which might raise an exception
rescue SomeExceptionClass => some_variable
# code that deals with some exception
rescue SomeOtherException => some_other_variable
# code that deals with some other exception
else
# code that runs only if *no* exception was raised
ensure
# ensure that this code always runs, no matter what
# does not change the final value of the block
end
Usted puede dejar de lado rescue
, ensure
o else
. También puede omitir las variables, en cuyo caso no podrá inspeccionar la excepción en su código de manejo de excepciones. (Bueno, siempre puedes usar la variable de excepción global para acceder a la última excepción que se generó, pero eso es un poco raro.) Y puedes omitir la clase de excepción, en cuyo caso se detectarán todas las excepciones que hereden de StandardError
. (Tenga en cuenta que esto no significa que todas las excepciones están atrapadas, porque hay excepciones que son casos de Exception
pero no StandardError
.excepciones en su mayoría muy graves que ponen en peligro la integridad del programa como SystemStackError
, NoMemoryError
, SecurityError
, NotImplementedError
, LoadError
, SyntaxError
, ScriptError
, Interrupt
, o SignalException
SystemExit
.)
Algunos bloques forman bloques excepción implícita. Por ejemplo, las definiciones de método son implícitamente también bloques de excepción, así que en vez de escribir
def foo
begin
# ...
rescue
# ...
end
end
se escriben simplemente
def foo
# ...
rescue
# ...
end
o
def foo
# ...
ensure
# ...
end
Lo mismo se aplica a class
definiciones y module
definiciones.
Sin embargo, en el caso específico que usted está preguntando, en realidad hay un modismo mucho mejor. En general, cuando trabajas con algún recurso que necesitas limpiar al final, lo haces pasando un bloque a un método que hace toda la limpieza por ti. Es similar a un bloque using
en C#, excepto que Ruby es realmente lo suficientemente poderoso como para no tener que esperar a que los sumos sacerdotes de Microsoft bajen de la montaña y cambien graciosamente el compilador. En Rubí, sólo puede implementar por sí mismo:
# This is what you want to do:
File.open('myFile.txt', 'w') do |file|
file.puts content
end
# And this is how you might implement it:
def File.open(filename, mode='r', perm=nil, opt=nil)
yield filehandle = new(filename, mode, perm, opt)
ensure
filehandle.close unless filehandle.nil?
end
Y lo sabes: este es ya disponible en la biblioteca central como File.open
. Pero es un patrón general que también puede usar en su propio código, para implementar cualquier tipo de limpieza de recursos (a la using
en C#) o transacciones o cualquier otra cosa que pueda pensar.
El único caso donde esto no funciona, si la adquisición y liberación del recurso se distribuye en diferentes partes del programa. Pero si está localizado, como en su ejemplo, entonces puede usar fácilmente estos bloques de recursos.
Por cierto: en la moderna C#, using
es en realidad superflua, porque se puede aplicar bloques de recursos de estilo de Ruby a sí mismo:
class File
{
static T open<T>(string filename, string mode, Func<File, T> block)
{
var handle = new File(filename, mode);
try
{
return block(handle);
}
finally
{
handle.Dispose();
}
}
}
// Usage:
File.open("myFile.txt", "w", (file) =>
{
file.WriteLine(contents);
});
+1 por "escribió un capítulo en un libro de Ruby bastante bueno". –
Tenga en cuenta que, aunque las sentencias 'ensure' se ejecutan al final, no son el valor de retorno. – Chris
Desea agregar que puede tener un catch-all para mostrar cada tipo de excepción haciendo esto: 'begin ... rescue Exception => e ... puts e.message; pone e.backtrace.inspect ... end'. – Automatico