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

Raise bất kỳ Object nào làm Ruby Exception: Kỹ thuật & Best Practice

Cú pháp `raise` của Ruby cho vài option chỉ định loại error muốn raise. Code dưới cho 3 cách raise `RuntimeError`.


raise "hello"
raise RuntimeError, "hello"
raise RuntimeError.new("hello")
# ...tất cả cho "RuntimeError: hello"

Đẹp, nhưng raise non-exception thì sao? Raise số? Ruby không cho. Nhận error:


raise 1 # TypeError: exception class/object expected

Thông báo này có thể khiến bạn tin `raise` expect exception class/object. Nhưng đó là sai!

Phương thức exception

Khi làm `raise foo`, method `raise` không expect `foo` là exception object. Nó expect sẽ nhận exception object khi gọi `foo.exception`.

Điều nhớ: có thể pass BẤT KỲ GÌ cho `raise`, miễn là nó có method `exception` trả về exception.

Vì vậy, nếu muốn, có thể monkeypatch number class cho phép raise số. Trông như:


class Fixnum
  def exception
    RuntimeError.new("I'm number: #{ self }")
  end
end
raise 42
# ...kết quả raise_number.rb:7:in `
': I'm number: 42 (RuntimeError)

Đây neat party trick, nhưng có thực tế useful? Ứng dụng chính tôi thấy: tách logic build exception khỏi logic quyết định raise exception. Edge case, nhưng xem ví dụ.

Ví dụ có thể thực tế

Giả sử muốn đọc dòng data từ IO nào đó. Network IO, file, không quan trọng. Chỉ muốn đọc data validate.

Nếu data đọc không valid, raise exception. Nhưng exception cần tailored theo input. Network connection cần debug info khác local file. Làm được bằng custom exception method cho mỗi input class. Pseudo-ruby:


# Ba class đại diện IO khác nhau với exception khác nhau
class NetworkConnection
  ...
  def exception
    NetworkConnectionError.new(url: url, ...)
  end
end

class LocalFile
  ...
  def exception
    FileError.new(path: path, ...)
  end
end

class UnixPipe
  ...
  def exception
    PipeError.new(...)
  end
end

def read_all(*items)
  items.each do |item|
    if item.readline != "foo"
      # Raise item, gây ra exception class phù hợp được dùng
      raise item
    end
  end
end

read_all(NetworkConnection.new(url: "example.com"), LocalFile.new("/something"), UnixPipe.new)