Reraise (cùng một ngoại lệ) sau khi bắt một ngoại lệ trong Ruby


84

Tôi đang cố gắng cải thiện kỹ năng Ruby của mình bằng cách bắt các ngoại lệ. Tôi muốn biết liệu việc sắp xếp lại cùng một loại ngoại lệ có phổ biến hay không khi bạn có một số cuộc gọi phương thức. Vì vậy, đoạn mã sau sẽ có ý nghĩa? Có thể đánh giá lại cùng một loại ngoại lệ hay tôi không nên bắt nó trên phương thức process?

class Logo
  def process
    begin
      @processed_logo = LogoProcessor::create_image(self.src)
    rescue CustomException
      raise CustomException
    end
  end
end

module LogoProcessor
  def self.create_image
    raise CustomException if some_condition
  end
end

Câu trả lời:


168

Đôi khi chúng ta chỉ muốn biết một lỗi đã xảy ra mà không cần phải thực sự xử lý lỗi đó.

Thường xảy ra trường hợp người chịu trách nhiệm xử lý lỗi là người dùng của đối tượng: người gọi. Điều gì sẽ xảy ra nếu chúng ta quan tâm đến lỗi, nhưng không muốn chịu trách nhiệm đó? Chúng tôi giải cứu lỗi, làm bất cứ điều gì chúng tôi cần làm và sau đó truyền tín hiệu lên ngăn xếp như thể không có gì xảy ra.

Ví dụ, nếu chúng tôi muốn ghi lại thông báo lỗi và sau đó để người gọi xử lý nó thì sao?

begin
  this_will_fail!
rescue Failure => error
  log.error error.message
  raise
end

Gọi raisemà không có bất kỳ đối số nào sẽ phát sinh lỗi cuối cùng. Trong trường hợp của chúng tôi, chúng tôi đang nuôi dưỡng lại error.

Trong ví dụ bạn đã trình bày trong câu hỏi của mình, việc nêu lại lỗi đơn giản là không cần thiết. Bạn chỉ có thể để nó tự nhiên lan truyền lên ngăn xếp. Sự khác biệt duy nhất trong ví dụ của bạn là bạn đang tạo một đối tượng lỗi mới và nâng nó lên thay vì nâng lại đối tượng cuối cùng.


Hấp dẫn. Câu hỏi của tôi là, nếu tôi không bắt được lỗi trong định nghĩa của process, thì tôi sẽ cần phải bắt lỗi khi tôi gọi phương thức process, ví dụ:, begin @logo.process; rescue...nhưng sau đó tôi sẽ không bắt được một ngoại lệ do chính process khởi chạy, nhưng của một cái gì đó đã được gọi từ trong quá trình. Làm vậy có đúng không?
Hommer Smith

2
Điều này sẽ làm mất stacktraceđi ngoại lệ ban đầu, bạn có thể muốn bao gồm ngoại lệ causecó sẵn trong ruby> 2.1
bjhaid

4
@bjhaid Gọi raisetheo cách của câu trả lời này hoàn toàn giữ nguyên ngoại lệ ban đầu, bao gồm cả backtrace. causekhông áp dụng cho trường hợp này. Thay vào đó, nó được điền tự động khi một rescuekhối tạo ra một ngoại lệ mới.
rep

@HommerSmith: Điều gì sẽ xảy ra nếu dòng trước khi nâng (log.error trong trường hợp này, nhưng nó có thể là bất cứ điều gì) không thành công? Tôi đang nghĩ đến việc "đảm bảo" nó, nhưng bên trong đảm bảo, tôi sẽ cần sử dụng tham chiếu đến lỗi làm đối số của "tăng". Bạn nghĩ gì về điều này?
jgomo3

1
@ RafałCieślak Mỗi khi có lỗi, nó được gán cho $!biến toàn cục. Việc gọi raisemà không có đối số làm tăng lỗi có trong đó $!, tăng lỗi cuối cùng một cách hiệu quả. Tuy nhiên, raise errorsẽ làm tăng lỗi có trong errorbiến cục bộ, có thể có hoặc không cùng một đối tượng chứa trong $!. Trong ví dụ của tôi, $!cũng giống như error. Tuy nhiên, bạn cũng có thể làm điều này:error = Exception.new; raise error
Matheus Moreira.

3

Điều này sẽ làm tăng cùng loại lỗi với lỗi ban đầu, nhưng bạn có thể tùy chỉnh thông báo.

rescue StandardError => e
  raise e.class, "Message: #{e.message}"

Tôi khuyên bạn nên bắt StandardError vì điều này bao gồm tất cả các loại chức năng cấp thấp hơn và có thể làm treo chương trình của bạn.
Paul Whitehead

1
Tôi tin rằng đây là một "ngoại lệ" đối với quy tắc, vì chúng tôi đang ngay lập tức nâng lại Ngoại lệ.
FreePender
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.