Sim, ensure
garante que o código seja sempre avaliado. É por isso que é chamado ensure
. Portanto, é equivalente a Java e C # 's finally
.
O fluxo geral de begin
/ rescue
/ else
/ ensure
/ end
é assim:
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
Você pode deixar de fora rescue
, ensure
ou else
. Você também pode deixar de fora as variáveis e, nesse caso, não poderá inspecionar a exceção no seu código de manipulação de exceções. (Bem, você sempre pode usar a variável de exceção global para acessar a última exceção que foi gerada, mas isso é um pouco hacky.) E você pode deixar de fora a classe de exceção. Nesse caso, todas as exceções herdadas StandardError
serão capturadas. (Por favor note que este não significa que todas as exceções são capturadas, porque há exceções que são instâncias de Exception
, mas não StandardError
. Exceções Principalmente muito graves que comprometem a integridade do programa, como SystemStackError
, NoMemoryError
, SecurityError
, NotImplementedError
, LoadError
, SyntaxError
, ScriptError
, Interrupt
,SignalException
ou SystemExit
.)
Alguns blocos formam blocos de exceção implícitos. Por exemplo, definições de método são implicitamente também blocos de exceção, portanto, em vez de escrever
def foo
begin
# ...
rescue
# ...
end
end
você escreve apenas
def foo
# ...
rescue
# ...
end
ou
def foo
# ...
ensure
# ...
end
O mesmo se aplica às class
definições e module
definições.
No entanto, no caso específico que você está perguntando, existe realmente um idioma muito melhor. Em geral, quando você trabalha com algum recurso que precisa limpar no final, faz isso passando um bloco para um método que faz toda a limpeza para você. É semelhante a um using
bloco em C #, exceto que Ruby é realmente poderoso o suficiente para que você não precise esperar que os sumos sacerdotes da Microsoft desçam da montanha e graciosamente alterem seu compilador para você. No Ruby, você pode simplesmente implementá-lo:
# 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
E o que você sabe: isso já está disponível na biblioteca principal como File.open
. Mas é um padrão geral que você também pode usar em seu próprio código, para implementar qualquer tipo de limpeza de recurso (à la using
em C #) ou transações ou qualquer outra coisa que você possa imaginar.
O único caso em que isso não funciona, se a aquisição e a liberação do recurso forem distribuídas por diferentes partes do programa. Mas se estiver localizado, como no seu exemplo, você poderá usar facilmente esses blocos de recursos.
BTW: no C # moderno, using
é realmente supérfluo, porque você pode implementar os blocos de recursos no estilo Ruby:
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
bloco.