12/08/2018, 13:37

Exception & StandardError trong Ruby

Trong công việc, đôi khi bạn phải bắt những lỗi như Exception & StandardError, hiểu rõ hơn về chúng sẽ giúp bạn quản lý công việc tốt hơn. Bình thường ta hay viết def some_method ##some code ... rescue Exception => e e.message end Exception đơn giản là 1 class ...

Trong công việc, đôi khi bạn phải bắt những lỗi như Exception & StandardError, hiểu rõ hơn về chúng sẽ giúp bạn quản lý công việc tốt hơn.

Bình thường ta hay viết

def some_method
    ##some code ...
rescue Exception => e
    e.message
end

Exception đơn giản là 1 class (http://ruby-doc.org/core-2.2.0/Exception.html) dùng để bắt những lỗi được raise hoặc fail trong 1 block begin end, Exception có những loại sau:

    NoMemoryError
    ScriptError
        LoadError
        NotImplementedError
        SyntaxError
    SecurityError
    SignalException
        Interrupt
    StandardError -- default for rescue
        ArgumentError
            UncaughtThrowError
        EncodingError
        FiberError
        IOError
            EOFError
        IndexError
            KeyError
            StopIteration
        LocalJumpError
        NameError
            NoMethodError
        RangeError
            FloatDomainError
        RegexpError
        RuntimeError -- default for raise
        SystemCallError
            Errno::*
        ThreadError
        TypeError
        ZeroDivisionError
    SystemExit
    SystemStackError
    fatal – impossible to rescue

Như trong list trên StandardError cũng chỉ là một dạng Exception.

Exception bao gồm cả những lỗi như thiếu bộ nhớ (NoMemoryError), break khi đang thực hiện thao tác (SignalException::Interrupt), ví dụ ta dùng tổ hợp phím CTRL_C ...

StandardError bao gồm những lỗi phổ biến như chia cho 0 (ZeroDivisionError), không có hoặc gọi tên hàm sai (NameError::NoMethodError)...

Muốn raise 1 Exception với 1 message theo ý mình ta chỉ việc dùng (ví dụ với ZeroDivisionError):

raise ZeroDivisionError, "some error ..."

Muốn định nghĩa 1 dạng Error, ta chỉ việc thêm 1 class kế thừa (có thể là StandardError), sau đó raise như bình thường :

class SomeError < StandardError
end

def test_error agr
   raise SomeError, "some error ..." if agr.nil?
end

def test_another_error agr
    agr / 0
rescue ZeroDivisionError
    raise SomeError, "some error ..."
end

Muốn xem full backtrace gọi method e.backtrace, hàm trả về dạng mảng các dòng báo lỗi.

Muốn thay đổi backtrace của error ta dùng method set_backtrace:

e.set_backtrace(["Hello", "World"])
e.backtrace
Hello
World

Method e.inspect để xem error đầy đủ.

Khi viết API, ta thường hay phải raise các error khi gặp các trường hợp đặc biệt và đưa ra endpoint những error_code dựa trên những dạng error mà chúng taraise ra.

Grape là 1 trong những gem giúp ta làm API khá, phổ biến.

Khi dùng Grape làm API ta thường raise những class lỗi tự định nghĩa bằng cách kế thừa class Grape::Exceptions::ValidationErrors rồi dùng rescue_from để catch lại.

Đối với những lỗi phát sinh trong quá trình sử lý ta cũng có thể dùng rescue_from để catch lại, đơn giản nhất là rescue_from :all do |e|, tuy nhiên chỉ có tác dụng đối với StandardError và không đối với Exception.

Grape::Exceptions::ValidationErrors có base là StandardError do vậy rescue_from sẽ bắt được hết error tự định nghĩa do kế thừa nó.

Trong Grape::Exceptions::ValidationErrors ta có message, headers hoạt động như các attributes để ta có thể gán thông tin tiện cho việc xử lý lúc catch lại.

def validate_param! attr_name, params
    raise Grape::Exceptions::Validation,
        params: [@scope.full_name(attr_name)],
        headers: Settings.api_validation.bad_digit_params unless params[attr_name].try(:length).try :<=, @option
end

...

rescue_from Grape::Exceptions::ValidationErrors do |e|
    missing_params = bad_format_params = bad_digit_params = []
    e.each do |_param, errors|
        if errors[:headers] == Settings.api_validation.bad_digit_params
            bad_digit_params += errors[:params]
        end
    end
end

Cảm ơn và hi vọng bài viết giúp ích trong công việc của bạn.

0