Ruby - exceções
A execução e a exceção sempre andam juntas. Se você estiver abrindo um arquivo que não existe, se você não lidou com esta situação adequadamente, seu programa é considerado de má qualidade.
O programa pára se ocorrer uma exceção. Portanto, as exceções são usadas para lidar com vários tipos de erros, que podem ocorrer durante a execução de um programa e tomar a ação apropriada em vez de interromper completamente o programa.
Ruby fornece um bom mecanismo para lidar com exceções. Incluímos o código que poderia gerar uma exceção em um bloco de início / fim e usar cláusulas de resgate para informar a Ruby os tipos de exceções que queremos tratar.
Sintaxe
begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
else
# Other exceptions
ensure
# Always will be executed
end
Tudo, do início ao resgate, está protegido. Se ocorrer uma exceção durante a execução deste bloco de código, o controle é passado para o bloco entre o resgate e o fim .
Para cada resgate cláusula no início do bloco, Ruby compara a exceção levantada contra cada um dos parâmetros, por sua vez. A correspondência será bem-sucedida se a exceção nomeada na cláusula de resgate for o mesmo que o tipo da exceção lançada atualmente ou se for uma superclasse dessa exceção.
No caso de uma exceção não corresponder a nenhum dos tipos de erro especificados, podemos usar uma cláusula else após todas as cláusulas de resgate .
Exemplo
#!/usr/bin/ruby
begin
file = open("/unexistant_file")
if file
puts "File opened successfully"
end
rescue
file = STDIN
end
print file, "==", STDIN, "\n"
Isso produzirá o seguinte resultado. Você pode ver que STDIN é substituído pelo arquivo porque a abertura falhou.
#<IO:0xb7d16f84>==#<IO:0xb7d16f84>
Usando instrução de nova tentativa
Você pode capturar uma exceção usando resgate do bloco e, em seguida, uso repetição declaração para executar começar bloco desde o início.
Sintaxe
begin
# Exceptions raised by this code will
# be caught by the following rescue clause
rescue
# This block will capture all types of exceptions
retry # This will move control to the beginning of begin
end
Exemplo
#!/usr/bin/ruby
begin
file = open("/unexistant_file")
if file
puts "File opened successfully"
end
rescue
fname = "existant_file"
retry
end
A seguir está o fluxo do processo -
- Ocorreu uma exceção na abertura.
- Foi resgatar. fname foi reatribuído.
- Por nova tentativa foi para o início do início.
- Este arquivo é aberto com sucesso.
- Continuou o processo essencial.
NOTE- Observe que se o arquivo de nome substituído não existir, este código de exemplo será repetido infinitamente. Tenha cuidado ao usar a repetição para um processo de exceção.
Usando a declaração de aumento
Você pode usar o aumento declaração para gerar uma exceção. O método a seguir levanta uma exceção sempre que é chamado. Sua segunda mensagem será impressa.
Sintaxe
raise
OR
raise "Error Message"
OR
raise ExceptionType, "Error Message"
OR
raise ExceptionType, "Error Message" condition
A primeira forma simplesmente levanta novamente a exceção atual (ou um RuntimeError se não houver exceção atual). Isso é usado em tratadores de exceção que precisam interceptar uma exceção antes de passá-la adiante.
A segunda forma cria uma nova exceção RuntimeError , definindo sua mensagem para a string fornecida. Essa exceção é então gerada na pilha de chamadas.
A terceira forma usa o primeiro argumento para criar uma exceção e então define a mensagem associada para o segundo argumento.
A quarta forma é semelhante à terceira, mas você pode adicionar qualquer instrução condicional como a menos que para gerar uma exceção.
Exemplo
#!/usr/bin/ruby
begin
puts 'I am before the raise.'
raise 'An error has occurred.'
puts 'I am after the raise.'
rescue
puts 'I am rescued.'
end
puts 'I am after the begin block.'
Isso produzirá o seguinte resultado -
I am before the raise.
I am rescued.
I am after the begin block.
Mais um exemplo mostrando o uso de aumento -
#!/usr/bin/ruby
begin
raise 'A test exception.'
rescue Exception => e
puts e.message
puts e.backtrace.inspect
end
Isso produzirá o seguinte resultado -
A test exception.
["main.rb:4"]
Usando a declaração de segurança
Às vezes, você precisa garantir que algum processamento seja feito no final de um bloco de código, independentemente de uma exceção ter sido levantada. Por exemplo, você pode ter um arquivo aberto na entrada do bloco e precisa ter certeza de que ele seja fechado quando o bloco sair.
A cláusula garanta faz exatamente isso. Garantir vai após a última cláusula de resgate e contém um pedaço de código que sempre será executado quando o bloco terminar. Não importa se o bloco sai normalmente, se ele levanta e resgata uma exceção, ou se é encerrado por uma exceção não capturada, o bloco garantir será executado.
Sintaxe
begin
#.. process
#..raise exception
rescue
#.. handle error
ensure
#.. finally ensure execution
#.. This will always execute.
end
Exemplo
begin
raise 'A test exception.'
rescue Exception => e
puts e.message
puts e.backtrace.inspect
ensure
puts "Ensuring execution"
end
Isso produzirá o seguinte resultado -
A test exception.
["main.rb:4"]
Ensuring execution
Usando outra instrução
Se a cláusula else estiver presente, ela seguirá as cláusulas de resgate e antes de qualquer garantia .
O corpo de uma cláusula else é executado apenas se nenhuma exceção for levantada pelo corpo principal do código.
Sintaxe
begin
#.. process
#..raise exception
rescue
# .. handle error
else
#.. executes if there is no exception
ensure
#.. finally ensure execution
#.. This will always execute.
end
Exemplo
begin
# raise 'A test exception.'
puts "I'm not raising exception"
rescue Exception => e
puts e.message
puts e.backtrace.inspect
else
puts "Congratulations-- no errors!"
ensure
puts "Ensuring execution"
end
Isso produzirá o seguinte resultado -
I'm not raising exception
Congratulations-- no errors!
Ensuring execution
A mensagem de erro levantada pode ser capturada usando $! variável.
Pegar e jogar
Embora o mecanismo de exceção de aumento e resgate seja ótimo para abandonar a execução quando as coisas dão errado, às vezes é bom ser capaz de pular de alguma construção profundamente aninhada durante o processamento normal. É aqui que pegar e jogar são úteis.
A captura define um bloco que é rotulado com o nome fornecido (que pode ser um símbolo ou uma string). O bloqueio é executado normalmente até que um lançamento seja encontrado.
Sintaxe
throw :lablename
#.. this will not be executed
catch :lablename do
#.. matching catch will be executed after a throw is encountered.
end
OR
throw :lablename condition
#.. this will not be executed
catch :lablename do
#.. matching catch will be executed after a throw is encountered.
end
Exemplo
O exemplo a seguir usa um lançamento para encerrar a interação com o usuário se '!' é digitado em resposta a qualquer prompt.
def promptAndGet(prompt)
print prompt
res = readline.chomp
throw :quitRequested if res == "!"
return res
end
catch :quitRequested do
name = promptAndGet("Name: ")
age = promptAndGet("Age: ")
sex = promptAndGet("Sex: ")
# ..
# process information
end
promptAndGet("Name:")
Você deve tentar o programa acima em sua máquina porque ele precisa de interação manual. Isso produzirá o seguinte resultado -
Name: Ruby on Rails
Age: 3
Sex: !
Name:Just Ruby
Exceção de classe
As classes e módulos padrão do Ruby levantam exceções. Todas as classes de exceção formam uma hierarquia, com a classe Exception no topo. O próximo nível contém sete tipos diferentes -
- Interrupt
- NoMemoryError
- SignalException
- ScriptError
- StandardError
- SystemExit
Há uma outra exceção neste nível, Fatal, mas o interpretador Ruby só usa isso internamente.
Tanto ScriptError quanto StandardError têm várias subclasses, mas não precisamos entrar em detalhes aqui. O importante é que, se criarmos nossas próprias classes de exceção, elas precisam ser subclasses da classe Exception ou de uma de suas descendentes.
Vejamos um exemplo -
class FileSaveError < StandardError
attr_reader :reason
def initialize(reason)
@reason = reason
end
end
Agora, olhe para o exemplo a seguir, que usará esta exceção -
File.open(path, "w") do |file|
begin
# Write out the data ...
rescue
# Something went wrong!
raise FileSaveError.new($!)
end
end
A linha importante aqui é aumentar FileSaveError.new ($!) . Chamamos raise para sinalizar que ocorreu uma exceção, passando a ela uma nova instância de FileSaveError, com a razão de que a exceção específica causou falha na gravação dos dados.