Nếu bạn đã làm việc với các ngoại lệ của Ruby trước đây, bạn biết bạn có thể chỉ định ngoại lệ nào được giải cứu và ngoại lệ 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 giải cứu một "cha mẹ", bạn cũng giải cứu tất cả "con cái" của nó.
begin
raise ArgumentError
rescue StandardError
# Rescues `ArgumentError`, because it inherits from
# `StandardError`
end
Khi tôi nói "parent" và "child", tôi chỉ đơn giản là đang đề cập đến sự kế thừa của lớp. Ở đâu đó sâu trong mã nguồn Ruby có một cái gì đó tương đương với điều này:
class ArgumentError < StandardError
...
end
Một thủ thuật thú vị
Đây là câu hỏi của tôi:làm thế nào Ruby biết được nếu có bất kỳ ngoại lệ nhất định nào kế thừa từ lớp bạn đã chỉ định?
Cách tiếp cận rõ ràng nhất sẽ là sử dụng is_a?
hoặc kind_of?
phương pháp. Chúng ta có thể tưởng tượng nó trông như thế này:
if the_exception.is_a?(StandardError)
# do the rescue
end
Nhưng đó không phải là những gì xảy ra. Thay vào đó, Ruby sử dụng ===
thú vị hơn nhà điều hành.
if StandardError === the_exception
# do the rescue
end
Nếu bạn chưa bao giờ sử dụng a === b
, nó thường trả lời câu hỏi "a có thuộc nhóm được xác định bởi b không"? Dưới đâ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
Bởi vì ===
chỉ là một phương thức ruby thông thường như ==
, chúng ta có thể tự định nghĩa nó:
class RedThings
def self.===(thing)
thing.color == :red
end
end
Vì vậy, những gì chúng ta biết? Chúng tôi biết rằng rescue
sử dụng ===
để xác định những trường hợp ngoại lệ nào được giải cứu. Và chúng tôi biết rằng chúng tôi có thể xác định ===
của riêng mình phương pháp. Điều đó có nghĩa là chúng tôi có thể tạo một lớp quyết định nhanh chóng những trường hợp ngoại lệ nào được cứu:
class SevereMatcher
def self.===(exception)
exception.message =~ /severe/
end
end
begin
raise RuntimeError, "Something severe happened"
rescue SevereMatcher
# rescues all exceptions with the word "severe" in
# the message, regardless of class.
end
Một khi bạn biết thủ thuật 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 rằng:bạn có thể không cần tạo một trình đối sánh ngoại lệ động. Nhưng đây là một ví dụ thực sự thú vị về cách một chi tiết triển khai có vẻ tầm thường như sử dụng ===
thay vì kind_of?
làm cho Ruby trở nên linh hoạt và thú vị hơn nhiều.