การส่งผ่านคลาสข้อผิดพลาดหลายคลาสไปยังประโยคการช่วยเหลือของ Ruby ในแบบแห้ง


102

ฉันมีรหัสบางอย่างที่ต้องการช่วยเหลือข้อยกเว้นหลายประเภทในทับทิม:

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

สิ่งที่ฉันต้องการทำคือเก็บรายการประเภทข้อยกเว้นที่ฉันต้องการช่วยเหลือที่ไหนสักแห่งและส่งต่อประเภทเหล่านั้นไปยังส่วนช่วยเหลือ:

EXCEPTIONS = [FooException, BarException]

แล้ว:

rescue EXCEPTIONS

นี้เป็นไปได้และมันเป็นไปได้โดยไม่ต้องโทรจริงๆสับ-Y บางอย่างeval? ฉันไม่หวังว่าจะได้เห็นTypeError: class or module required for rescue clauseเมื่อฉันพยายามข้างต้น


2
สิ่งที่เกี่ยวกับการช่วยเหลือ * ข้อยกเว้น?
โรมัน

คำตอบ:


201

*คุณสามารถใช้อาร์เรย์กับผู้ประกอบการเครื่องหมาย

EXCEPTIONS = [FooException, BarException]

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

หากคุณกำลังจะใช้ค่าคงที่สำหรับอาร์เรย์ตามข้างบน (ด้วยEXCEPTIONS) โปรดทราบว่าคุณไม่สามารถกำหนดได้ภายในนิยามและถ้าคุณกำหนดมันในคลาสอื่นคุณต้องอ้างถึงด้วยเนมสเปซ จริงๆแล้วไม่จำเป็นต้องเป็นค่าคงที่


ตัวดำเนินการ Splat

ตัวดำเนินการ*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]

หนึ่งความแตกต่างระหว่างทับทิม 1.8 และ 1.9 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
ดูเหมือนว่าจะใช้งานได้แม้ในทับทิม 1.8.7 คำศัพท์สำหรับการใช้อักขระ '*' ที่อยู่ข้างหน้าEXCEPTIONSในกรณีนี้คืออะไร? ต้องการเรียนรู้เพิ่มเติมอีกเล็กน้อย.
APB

2
@ แอนดี้มันเรียกว่า splat โดยปกติจะมีผลในการสลายอาร์เรย์เป็นวัตถุที่คั่นด้วยเครื่องหมายจุลภาค เมื่อใช้ในตำแหน่งการรับอาร์กิวเมนต์ของนิยามเมธอดมันจะทำอีกวิธีหนึ่งคือใส่อาร์กิวเมนต์เข้าด้วยกันในอาร์เรย์ มันค่อนข้างมีประโยชน์ในโอกาสต่างๆ สิ่งที่ควรรู้ว่าใช้ได้กับ 1.8.7 ฉันแก้ไขคำตอบของฉันตามนั้น
sawa

21
โปรดทราบว่าหากคุณต้องการเข้าถึงอินสแตนซ์ข้อยกเว้นให้ใช้ไวยากรณ์นี้: rescue InvalidRequestError, CardError => e(ดูmikeferrier.com/2012/05/19/… )
Peter Ehrlich

1
รูปแบบนี้ทำงานได้ดี: rescue *EXCEPTIONS => eที่EXCEPTIONSเป็นอาร์เรย์ของชื่อชั้นยกเว้น
aks

4

แม้ว่าคำตอบของ @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, InputFileMissingErrorและOutputDirectoryErrorว่าฉันอยากจะช่วยเหลือด้วยกับคำสั่งอย่างใดอย่างหนึ่ง ฉันสร้างคลาสข้อยกเว้นอื่นที่เรียกว่าFileLoadErrorจากนั้นตั้งค่าข้อยกเว้นสามข้อข้างต้นเพื่อสืบทอดจากคลาสนั้น 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.