Programming Ruby(読書ノート)-10章(異常)

5401 ワード

異常はリターンコードに比べて,誤り状況を論理コードからよりよく分離することができ,すなわち正常な論理のみに関心を持つことができ,あるいは自分が処理できる異常状況に関心を持つことができる.一方、戻りコードが1つのcallの後ろに複数のサブオペレーションがある場合、異常が発生すると、呼び出し者はどのステップでエラーが発生したのか分かりにくい.
10.1異常類
すべての組み込み例外クラスは、Exceptionクラスを継承します.
10.2処理異常
#  begin...rescue...end        
require 'open-uri'
page= "podcasts"
file_name = "#{page}.html"
web_page = open("http://pragprog.com/#{page}")
output = File.open(file_name,"w")
begin
  while line = web_page.gets
    output.puts line
  end
  output.close
rescue Exception
  STDERR.puts "Failed to download#{page}:#{$!}"
  output.close
  File.delete(file_name)
  raise
end
# $!     (Global variable)           

このメカニズムはjavaと同じです
# java catch  ,     rescue。
#   rescue              ,     $!  
begin
  eval  string
rescue SyntaxError,NameError=>boom
  print"String doesn't compile:"+boom
rescue StandardError=>bang
  print"Error running script:"+bang
end

複数のrescueの場合のマッチングルールはcase文に似ています.実際にparameter==$!,rescueが指定した例外クラスが$と!オブジェクトのクラスが同じまたはそのサブクラスである場合、rescueのコードが実行されます.
rescueの後にキャプチャされた例外を指定しなくても、デフォルトではStandardErrorが使用されます.
rescueは、Exceptionクラスのオブジェクトを返す限り、式の後になります.
 
システムエラー
オペレーティングシステムの操作を呼び出すと、エラーがエラーコードを返し、rubyはモジュールErrnoにSystemCallErrorのサブクラスとしてパッケージされます.
 
異常後のリソースクリーンアップ(Finally)
ensureを使用して、例外後にバッチを実行するコードを保証します.ensureは最後のrescueの後ろに置かれています.
f = File.open("testFile")
begin
  #
rescue
  #
ensure
  f.close
end
#open       begin  ,          ,       

rescueはまたelseを使用して異常が発生していない場合に実行する必要があるコードを処理することもできる.
f=File.open("testfile")
begin
  #..process
rescue
  #..handleerror
else
  puts"Congratulations--noerrors!"
ensure
  f.close
end

 
エラー後に再試行
エラーが発生したら、retryブロックを使用して再実行します(パラメータを変更した後).
retryブロックはrescueブロックに入れて使用します.
#        ,  @estmp  ,    
@esmtp = true
begin
  if @estmp then @command.ehlo(helodom)
                   else @command.helo(helodom)
  end

rescue ProtocolError
  if @estmp then
    @estmp = false
    retry
  else
    raise
  end
end

 
10.3 Raising Exceptions(throwとは異なる)
アクティブraiseは例外です.raiseもfailもObjectクラスのメソッド(違い?)です.
構造方法は3つあります
raise
raise "bad mp3 encoding"
raise InterfaceException, "keyboard failure", caller

1つ目は、現在の異常$を投げ出せ!(rescueブロックの場合)、またはRuntimeError($!が存在しない場合).
2つ目は、新しいRuntimeError例外を作成し、説明を設定することです.
3つ目は、新しい例外オブジェクトを作成し、2番目のパラメータの内容を割り当て、現在の実行スタックを3番目のパラメータに設定します.Object#callerメソッドは、現在の実行スタックを返します.
#       ,    
raise ArgumentError, "Name too big", caller[1..-1]

 
カスタムException
#           。
class RetryException < RuntimeError
  attr :ok_to_retry
  def initialize(ok_to_retry)
    @ok_to_retry = ok_to_retry
  end
end
#        
def read_data(socket)
  data = socket.read(512)
  if data.nil?
    raise RetryException.new(true), "transient read error"
  end
  #..normal processing
end
#rescue   
begin
  stuff = read_data(socket)
  #..process stuff
rescue RetryException => detail
  retry if detail.ok_to_retry
  raise
end

 
10.4 catch and throw
goto文に似ていますか?ある正常な状況では、実行体から飛び出して、実行されたこの部分を放棄したい.この場合catchとthrowを使用できます.
word_list = File.open("wordlist")
catch (:done) do
  result = []
  while line = word_list.gets
    word = line.chomp
    throw :done unless word =~ /^\w+$/
    result << word
  end
  puts result.reverse
end

catchは、ラベル名(通常symbolまたはstring)を指定することによってコードブロックを定義します.throwが発生しない場合は、ブロックコード全体が正常に実行されます.
throwが発生するとrubyはスタックを一致するcatchポイントに圧縮し、現在の実行ブロックを終了する.throwにはオプションのパラメータがあり、値を割り当てるとcatchブロックの戻り値を変数に割り当てることができます.
word_list=File.open("wordlist")
word_in_error=catch(:done) do
  result=[]
  while line=word_list.gets
    word=line.chomp
    throw(:done,word) unless word=~/^\w+$/
    result<<word
  end
  puts result.reverse
end
if word_in_error
  puts"Failed:'#{word_in_error}'found,but a word was expected"
end
produces:
Failed:'*wow*'found,butawordwasexpected

throwは必ずしもcatchと静的に1つのブロック内に存在するわけではありません.次に、throwとなるメソッドを定義し、呼び出し時にcatchを使用します.
def prompt_and_get(prompt)
  print prompt
  res = readline.chomp
  throw :quit_requested if res=="!"
  res
end
catch:quit_requested do
  name =prompt_and_get("Name:")
  age =prompt_and_get("Age: ")
  sex =prompt_and_get("Sex: ")
  #..
  #processinformation
end