Sí, ensure
asegura que el código siempre se evalúa. Por eso se llama ensure
. Por lo tanto, es equivalente a Java y C # finally
.
El flujo general de begin
/ rescue
/ else
/ ensure
/ se end
ve así:
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
Puedes dejar afuera 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 puede usar la variable de excepción global para acceder a la última excepción que se generó, pero eso es un poco confuso). Y puede omitir la clase de excepción, en cuyo caso se detectarán todas las excepciones que hereden StandardError
. (Tenga en cuenta que esto no significa que todas las excepciones son capturadas, porque hay excepciones que son instancias de Exception
pero no StandardError
. Excepciones mayoría muy graves que ponen en peligro la integridad del programa, tales como SystemStackError
, NoMemoryError
, SecurityError
, NotImplementedError
, LoadError
, SyntaxError
, ScriptError
, Interrupt
,SignalException
o SystemExit
)
Algunos bloques forman bloques de excepción implícitos. Por ejemplo, las definiciones de métodos son implícitamente también bloques de excepción, por lo que en lugar de escribir
def foo
begin
# ...
rescue
# ...
end
end
solo escribes
def foo
# ...
rescue
# ...
end
o
def foo
# ...
ensure
# ...
end
Lo mismo se aplica a las class
definiciones y module
definiciones.
Sin embargo, en el caso específico sobre el que está preguntando, en realidad hay una expresión 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 using
bloque en C #, excepto que Ruby es lo suficientemente poderoso como para no tener que esperar a que los sumos sacerdotes de Microsoft bajen de la montaña y cambien gentilmente su compilador por usted. En Ruby, puedes implementarlo tú 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
end
Y qué sabes: esto ya está disponible en la biblioteca principal 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 la liberación del recurso se distribuyen 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 C # moderno, en using
realidad es superfluo, porque puede implementar bloques de recursos de estilo Ruby usted 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);
});
begin
bloque.