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.