Computer >> Máy Tính >  >> Lập trình >> Ruby

Giải cứu thủ thuật tao nhã để biết được ngoại lệ nào cần bắt

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.