Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Làm Chủ Xử Lý Ngoại Lệ Ruby: Mẹo Thông Minh Cho Rescue Chọn Lọc

Nếu bạn đã làm việc với exception của Ruby trước đây, bạn biết bạn có thể chỉ định exception nào được rescue và nào không:

begin
  raise ArgumentError
rescue ArgumentError
  # Rescues the `ArgumentError`
end

...và bạn có thể biết rằng khi bạn rescue một "parent", bạn rescue tất cả "children" của nó cũng vậy.

begin
  raise ArgumentError
rescue StandardError
  # Rescues `ArgumentError`, vì nó kế thừa từ
  # `StandardError`
end

Khi tôi nói "parent" và "child", tôi chỉ đơn giản đề cập đến class inheritance. Ở đâu đó sâu trong source Ruby có thứ tương đương với:

class ArgumentError < StandardError
  ...
end

Một mẹo thú vị

Câu hỏi của tôi: làm sao Ruby biết nếu bất kỳ exception cho trước nào kế thừa từ class bạn chỉ định?

Cách tiếp cận rõ ràng nhất sẽ là dùng phương thức is_a? hoặc kind_of?. Chúng ta có thể tưởng tượng nó trông như:

if the_exception.is_a?(StandardError)
  # do the rescue
end

Nhưng đó không phải điều xảy ra. Thay vào đó, Ruby dùng toán tử === thú vị hơn.

if StandardError === the_exception
  # do the rescue
end

Nếu bạn chưa bao giờ dùng a === b, nó thường trả lời câu hỏi "does a inherently belong to the group defined by b"? Đây là một số ví dụ:

(1..10) === 5 # true
('a'..'f') === "z" # false
String === "hello" # true
String === 1 # false
/[0-9]{3}/ === "hello123" # true
/[0-9]{3}/ === "hello" # false

Vì === chỉ là một phương thức Ruby bình thường như ==, chúng ta có thể tự định nghĩa nó:

class RedThings
  def self.===(thing)
    thing.color == :red
  end
end

Vậy, chúng ta biết gì? Chúng ta biết rescue dùng === để xác định exception nào được rescue. Và chúng ta biết chúng ta có thể tự định nghĩa === method. Nghĩa là chúng ta có thể tạo một class quyết định on-the-fly exception nào được rescue:

class SevereMatcher
  def self.===(exception)
    exception.message =~ /severe/
  end
end
begin
  raise RuntimeError, "Something severe happened"
rescue SevereMatcher
  # rescues tất cả exception có từ "severe" trong
  # message, bất kể class.
end

Khi bạn biết mẹo này, giới hạn duy nhất là trí tưởng tượng của bạn.

Kết luận

Tôi thừa nhận: bạn có thể không bao giờ cần tạo một dynamic exception matcher. Nhưng đây là ví dụ thực sự thú vị về cách một chi tiết triển khai tưởng chừng như tầm thường như dùng === thay vì kind_of? làm cho Ruby linh hoạt và thú vị hơn nhiều.