Birden fazla hata sınıfını Ruby'nin kurtarma maddesine DRY tarzında geçirme


102

Ruby'de birden çok istisna türünü kurtarması gereken bazı kodum var:

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

Yapmak istediğim şey, bir yerde kurtarmak istediğim istisna türlerinin listesini bir şekilde saklamak ve bu türleri kurtarma maddesine aktarmak:

EXCEPTIONS = [FooException, BarException]

ve sonra:

rescue EXCEPTIONS

Bu mümkün mü ve gerçekten hack-y aramaları olmadan mümkün evalmü? TypeError: class or module required for rescue clauseYukarıdakileri denediğimde gördüğüm için umutlu değilim .


2
Peki ya kurtarma * İSTİSNALAR?
Roman

Yanıtlar:


201

Uyarlama operatörüyle bir dizi kullanabilirsiniz *.

EXCEPTIONS = [FooException, BarException]

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

Dizi için yukarıdaki gibi bir sabit kullanacaksanız (with EXCEPTIONS), onu bir tanım içinde tanımlayamayacağınızı ve ayrıca başka bir sınıfta tanımlarsanız, ona kendi ad alanıyla başvurmanız gerektiğini unutmayın. Aslında sabit olmak zorunda değil.


Uyarı Operatörü

Uyarlama operatörü konumunda *bir diziyi "paketten çıkarır", böylece

rescue *EXCEPTIONS

aynı anlama gelir

rescue FooException, BarException

Bunu bir dizi değişmezinde de kullanabilirsiniz.

[BazException, *EXCEPTIONS, BangExcepion]

aynı olan

[BazException, FooException, BarException, BangExcepion]

veya bir argüman konumunda

method(BazException, *EXCEPTIONS, BangExcepion)

bunun anlamı

method(BazException, FooException, BarException, BangExcepion)

[] boşluğa genişler:

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

Yakut 1.8 ile yakut 1.9 arasındaki bir fark ile nil.

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

Bu gibi durumlarda uygulanacağı gibi to_a, üzerinde tanımlanmış olan nesnelere dikkat edin to_a:

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

Diğer nesne türleri ile kendini geri döndürür.

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

2
Bu Ruby 1.8.7'de bile işe yarıyor gibi görünüyor. EXCEPTIONSBu durumda önünde '*' karakterini kullanmak için kullanılan terim nedir ? Biraz daha öğrenmek isterim.
apb

2
@Andy Buna splat denir. Genellikle bir diziyi virgülle ayrılmış nesnelere ayırma etkisine sahiptir. Bir yöntem tanımının argüman alma pozisyonunda kullanıldığında, diğer yolu yapar: argümanları bir diziye bir araya getirir. Çeşitli durumlarda oldukça kullanışlıdır. 1.8.7 ile çalıştığını bilmek güzel. Cevabımı buna göre düzenledim.
sawa

21
İstisna örneğine erişmek istiyorsanız, şu sözdizimini kullanın: rescue InvalidRequestError, CardError => e(bkz. Mikeferrier.com/2012/05/19/… )
Peter Ehrlich

1
Bu sözdizimi gayet iyi çalışıyor: rescue *EXCEPTIONS => eburada EXCEPTIONSbir istisna sınıfı isimleri dizisi.
aks

4

İken cevap @sawa tarafından verilen teknik olarak doğru, ben Ruby'nin istisna işleme mekanizmasını kötüye düşünüyorum.

Tarafından açıklama olarak Peter Ehrlich (eski için işaret tarafından anlaşılacağı Mike Ferrier blog post ), Ruby zaten KURU istisna işleyicisi mekanizması ile donatılmıştır:

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'

Bu tekniği kullanarak, içinde genellikle bazı değerli bilgiler bulunan istisna nesnesine erişebiliriz.


1

Bu sorunla karşılaştım ve alternatif bir çözüm buldum. Sizin FooExceptionve BarExceptionhepinizin özel istisna sınıfları olması durumunda ve özellikle hepsi tematik olarak ilişkili ise, miras hiyerarşinizi, hepsi aynı ana sınıftan miras alacak ve sonra yalnızca ana sınıfı kurtaracak şekilde yapılandırabilirsiniz.

Mesela ben üç istisnalar vardı: FileNamesMissingError, InputFileMissingErrorve OutputDirectoryErrorben tek bir deyimle kurtarmaya istediğini söyledi. Başka bir istisna sınıfını çağırdım FileLoadErrorve ardından yukarıdaki üç istisnayı ondan miras alacak şekilde ayarladım. Daha sonra sadece kurtardım FileLoadError.

Bunun gibi:

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.