Chuyển nhiều lớp lỗi đến điều khoản giải cứu của ruby ​​theo kiểu KHÔ


100

Tôi có một số mã cần giải cứu nhiều loại ngoại lệ trong ruby:

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

Điều tôi muốn làm là bằng cách nào đó lưu trữ danh sách các loại ngoại lệ mà tôi muốn giải cứu ở đâu đó và chuyển các loại đó vào mệnh đề giải cứu:

EXCEPTIONS = [FooException, BarException]

và sau đó:

rescue EXCEPTIONS

Liệu điều này có khả thi không, và liệu có khả thi nếu không có một số cuộc gọi thực sự hack-y eval? Tôi không hy vọng rằng tôi đang nhìn thấy TypeError: class or module required for rescue clausekhi tôi cố gắng ở trên.


2
Còn về giải cứu * NGOẠI LỆ?
Roman

Câu trả lời:


197

Bạn có thể sử dụng một mảng với toán tử splat *.

EXCEPTIONS = [FooException, BarException]

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

Nếu bạn định sử dụng một hằng số cho mảng như trên (với EXCEPTIONS), hãy lưu ý rằng bạn không thể định nghĩa nó trong một định nghĩa, và nếu bạn định nghĩa nó trong một số lớp khác, bạn phải tham chiếu đến nó với không gian tên của nó. Trên thực tế, nó không nhất thiết phải là một hằng số.


Nhà điều hành Splat

Toán tử splat *"giải nén" một mảng ở vị trí của nó để

rescue *EXCEPTIONS

có nghĩa là giống như

rescue FooException, BarException

Bạn cũng có thể sử dụng nó trong một mảng ký tự như

[BazException, *EXCEPTIONS, BangExcepion]

giống như

[BazException, FooException, BarException, BangExcepion]

hoặc ở một vị trí đối số

method(BazException, *EXCEPTIONS, BangExcepion)

nghĩa là

method(BazException, FooException, BarException, BangExcepion)

[] mở rộng đến chỗ trống:

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

Một sự khác biệt giữa ruby ​​1.8 và ruby ​​1.9 là với nil.

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

Hãy cẩn thận với các đối tượng to_ađược xác định trên đó , như to_asẽ được áp dụng trong các trường hợp như vậy:

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

Với các loại đối tượng khác, nó tự trả về.

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

2
Điều này dường như hoạt động ngay cả trong ruby ​​1.8.7. Thuật ngữ sử dụng ký tự '*' phía trước EXCEPTIONStrong trường hợp này là gì? Muốn tìm hiểu thêm một chút.
apb

2
@Andy Nó được gọi là splat. Nó thường có tác dụng phân rã một mảng thành các đối tượng được phân tách bằng dấu phẩy. Khi được sử dụng trong vị trí nhận đối số của một định nghĩa phương thức, nó thực hiện theo cách khác: đặt các đối số lại với nhau thành một mảng. Nó khá hữu ích trong nhiều trường hợp khác nhau. Thật tốt khi biết rằng nó hoạt động với 1.8.7. Tôi đã chỉnh sửa câu trả lời của mình cho phù hợp.
sawa

20
Lưu ý rằng nếu bạn muốn truy cập vào dụ ngoại lệ, sử dụng cú pháp sau: rescue InvalidRequestError, CardError => e(xem mikeferrier.com/2012/05/19/... )
Peter Ehrlich

Cú pháp này hoạt động tốt:, rescue *EXCEPTIONS => etrong đó EXCEPTIONSlà một mảng tên lớp ngoại lệ.
aks

3

Mặc dù câu trả lời được đưa ra bởi @sawa là đúng về mặt kỹ thuật, nhưng tôi nghĩ nó sử dụng sai cơ chế xử lý ngoại lệ của Ruby.

Như nhận xét của Peter Ehrlich gợi ý (bằng cách trỏ đến một bài đăng trên blogcủa Mike Ferrier ), Ruby đã được trang bị một cơ chế xử lý ngoại lệ 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'

Bằng cách sử dụng kỹ thuật này, chúng ta có thể truy cập đối tượng ngoại lệ, đối tượng thường có một số thông tin có giá trị trong đó.


1

Tôi vừa gặp phải vấn đề này và tìm thấy một giải pháp thay thế. Trong trường hợp của bạn FooExceptionBarExceptiontất cả sẽ là các lớp ngoại lệ tùy chỉnh và đặc biệt nếu tất cả chúng đều có liên quan đến chủ đề, bạn có thể cấu trúc hệ thống phân cấp kế thừa của mình để tất cả chúng sẽ kế thừa từ cùng một lớp cha và sau đó chỉ cứu lớp cha.

Ví dụ tôi có ba trường hợp ngoại lệ: FileNamesMissingError, InputFileMissingError, và OutputDirectoryErrorrằng tôi muốn cứu hộ với một tuyên bố. Tôi đã thực hiện một lớp ngoại lệ khác được gọi FileLoadErrorvà sau đó thiết lập ba ngoại lệ ở trên để kế thừa từ nó. Sau đó tôi chỉ giải cứu FileLoadError.

Như thế này:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.