Meneruskan beberapa kelas kesalahan ke klausa penyelamatan Ruby dengan cara KERING

100

Saya memiliki beberapa kode yang perlu menyelamatkan beberapa jenis pengecualian di ruby:

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

Yang ingin saya lakukan adalah menyimpan daftar jenis pengecualian yang ingin saya selamatkan di suatu tempat dan meneruskan jenis tersebut ke klausa penyelamatan:

EXCEPTIONS = [FooException, BarException]

lalu:

rescue EXCEPTIONS

Apakah ini mungkin, dan apakah mungkin tanpa beberapa panggilan hack-y eval? Saya tidak berharap karena saya melihat TypeError: class or module required for rescue clauseketika saya mencoba di atas.

apb
sumber
2
Bagaimana dengan penyelamatan * PENGECUALIAN?
Roma

Jawaban:

197

Anda dapat menggunakan array dengan operator percikan *.

EXCEPTIONS = [FooException, BarException]

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

Jika Anda akan menggunakan konstanta untuk array seperti di atas (dengan EXCEPTIONS), perhatikan bahwa Anda tidak dapat mendefinisikannya dalam definisi, dan juga jika Anda mendefinisikannya di kelas lain, Anda harus merujuknya dengan namespace-nya. Sebenarnya, itu tidak harus konstan.


Operator Percikan

Operator percikan *"membongkar" sebuah array dalam posisinya sehingga

rescue *EXCEPTIONS

artinya sama dengan

rescue FooException, BarException

Anda juga dapat menggunakannya dalam literal array sebagai

[BazException, *EXCEPTIONS, BangExcepion]

yang sama dengan

[BazException, FooException, BarException, BangExcepion]

atau dalam posisi argumen

method(BazException, *EXCEPTIONS, BangExcepion)

yang berarti

method(BazException, FooException, BarException, BangExcepion)

[] meluas ke kekosongan:

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

Satu perbedaan antara ruby ​​1.8 dan ruby ​​1.9 adalah dengan nil.

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

Hati-hati dengan objek yang to_adidefinisikan, karena to_aakan diterapkan dalam kasus seperti ini:

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

Dengan jenis objek lain, ia mengembalikan dirinya sendiri.

[1, *2, 3] # => [1, 2, 3]
sawa
sumber
2
Ini tampaknya bekerja bahkan di ruby ​​1.8.7. Apa istilah untuk menggunakan karakter '*' di depan EXCEPTIONSkasus ini? Ingin belajar lebih banyak.
apb
2
@Anda Ini disebut percikan. Biasanya memiliki efek mendekomposisi array menjadi objek yang dipisahkan koma. Ketika digunakan dalam posisi penerimaan argumen dari definisi metode, itu dilakukan dengan cara lain: menempatkan argumen bersama-sama ke dalam sebuah array. Ini cukup berguna dalam berbagai kesempatan. Perlu diketahui bahwa ini berfungsi dengan 1.8.7. Saya mengedit jawaban saya sesuai.
sawa
20
Perhatikan bahwa jika Anda ingin mengakses contoh pengecualian, gunakan sintaks ini: rescue InvalidRequestError, CardError => e(lihat mikeferrier.com/2012/05/19/… )
Peter Ehrlich
Sintaks ini berfungsi dengan baik:, rescue *EXCEPTIONS => ewhere EXCEPTIONSis array of exception class names.
aks
3

Sedangkan jawabannya diberikan oleh @sawa secara teknis benar, saya kira hal itu menyalahgunakan mekanisme penanganan pengecualian Ruby.

Seperti yang disarankan oleh komentar Peter Ehrlich (dengan menunjuk ke posting blog lama oleh Mike Ferrier ), Ruby sudah dilengkapi dengan mekanisme penanganan pengecualian 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'

Dengan menggunakan teknik ini, kita dapat mengakses objek pengecualian, yang biasanya memiliki beberapa informasi berharga di dalamnya.

Ron Klein
sumber
1

Saya baru saja mengalami masalah ini dan menemukan solusi alternatif. Dalam kasus Anda FooExceptiondanBarException semua akan menjadi kelas pengecualian khusus dan terutama jika semuanya terkait secara tematis, Anda dapat menyusun hierarki pewarisan sedemikian rupa sehingga semuanya akan mewarisi dari kelas induk yang sama dan kemudian hanya menyelamatkan kelas induk.

Misalnya saya punya tiga pengecualian: FileNamesMissingError, InputFileMissingError, dan OutputDirectoryErrorbahwa saya ingin menyelamatkan dengan satu pernyataan. Saya membuat kelas pengecualian lain yang dipanggil FileLoadErrordan kemudian menyiapkan tiga pengecualian di atas untuk mewarisi darinya. Saya kemudian diselamatkan saja FileLoadError.

Seperti ini:

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
Mikhail Golubitsky
sumber