複数のエラークラスをルビーのレスキュー句にDRY方式で渡す


100

Rubyで複数の種類の例外を救済する必要があるコードがいくつかあります。

begin
  a = rand
  if a > 0.5
    raise FooException
  else
    raise BarException
  end
rescue FooException, BarException
  puts "rescued!"
end

私がしたいことは、どこかで救済したい例外タイプのリストを何らかの方法で保存し、それらのタイプを救済句に渡すことです:

EXCEPTIONS = [FooException, BarException]

その後:

rescue EXCEPTIONS

これは可能ですか、そして本当にハックのような呼び出しがなくても可能evalですか?TypeError: class or module required for rescue clause上記のことを試してみたとき、私は望んでいませんでした。


2
レスキューの* EXCEPTIONSはどうですか?
ローマ

回答:


197

splat演算子で配列を使用できます*

EXCEPTIONS = [FooException, BarException]

begin
  a = rand
  if a > 0.5
    raise FooException
  else
    raise BarException
  end
rescue *EXCEPTIONS
  puts "rescued!"
end

上記のように(を使用してEXCEPTIONS)配列の定数を使用する場合、定義内で定義することはできません。また、他のクラスで定義する場合は、その名前空間で参照する必要があります。実際には、定数である必要はありません。


スプラットオペレーター

splatオペレーターは*、配列をその位置に「アンパック」して、

rescue *EXCEPTIONS

と同じ意味

rescue FooException, BarException

次のように配列リテラル内で使用することもできます

[BazException, *EXCEPTIONS, BangExcepion]

それは同じです

[BazException, FooException, BarException, BangExcepion]

または引数の位置

method(BazException, *EXCEPTIONS, BangExcepion)

つまり

method(BazException, FooException, BarException, BangExcepion)

[] 空虚に拡張:

[a, *[], b] # => [a, b]

ruby 1.8とruby 1.9の違いの1つは、nilです。

[a, *nil, b] # => [a, b]       (ruby 1.9)
[a, *nil, b] # => [a, nil, b]  (ruby 1.8)

このような場合に適用されるようにto_a、が定義されているオブジェクトには注意してくださいto_a

[a, *{k: :v}, b] # => [a, [:k, :v], b]

他のタイプのオブジェクトでは、それ自体を返します。

[1, *2, 3] # => [1, 2, 3]

2
これはruby 1.8.7でも機能するようです。EXCEPTIONSこの場合の前に「*」文字を使用する用語は何ですか?もう少し学びたいです。
apb '25

2
@アンディスプラットと呼ばれています。通常、配列をコンマ区切りのオブジェクトに分解する効果があります。メソッド定義の引数の受け取り位置で使用すると、別の方法で引数を配列にまとめます。さまざまな場面で非常に役立ちます。1.8.7で動作することを知っておくと便利です。私はそれに応じて私の答えを編集しました。
佐和

20
例外インスタンスにアクセスする場合は、次の構文を使用することに注意してくださいrescue InvalidRequestError, CardError => emikeferrier.com/2012/05/19/…を参照)
Peter Ehrlich

この構文はうまく動作しますrescue *EXCEPTIONS => e。ここで、EXCEPTIONS例外クラス名の配列です。
AKS

3

一方で答え @sawaによって与えられたが、技術的に正しいと、私はそれがRubyの例外処理メカニズムを悪用だと思います。

Peter Ehrlichのコメントが示唆しているように(Mike Ferrierの古いブログ投稿を指すことで)、RubyにはすでにDRY例外ハンドラーメカニズムが備わっています。

puts 'starting up'
begin
  case rand(3)
  when 0
    ([] + '')
  when 1
    (foo)
  when 2
    (3 / 0)
  end
rescue TypeError, NameError => e
  puts "oops: #{e.message}"
rescue Exception => e
  puts "ouch, #{e}"
end
puts 'done'

この手法を使用することで、例外オブジェクトにアクセスできます。例外オブジェクトには通常、いくつかの貴重な情報が含まれています。


1

私はこの問題に遭遇し、別の解決策を見つけました。ケースであなたFooExceptionとはBarException、すべてのカスタム例外クラスであることを行っていると、彼らはすべてのテーマに関連している場合は特に、あなたは彼らがすべて同じ親クラスから継承しては、親クラスを救出することをあなたの継承階層は、このような構造ができます。

たとえばFileNamesMissingError、3つの例外がありました:、、InputFileMissingErrorおよびOutputDirectoryError1つのステートメントで救出したいと思ったこと。別の例外クラスを呼び出しFileLoadError、それから継承する上記の3つの例外を設定しました。その後、私だけを救出したFileLoadError

このような:

class FileLoadError < StandardError
end

class FileNamesMissingError < FileLoadError
end

class InputFileMissingError < FileLoadError
end

class OutputDirectoryError < FileLoadError
end

[FileNamesMissingError,
 InputFileMissingError,
 OutputDirectoryError].each do |error| 
   begin  
     raise error
   rescue FileLoadError => e
     puts "Rescuing #{e.class}."
   end 
end
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.