Viblo CTF
0

Ruby Exceptions

Trong một chương trình execution và exception luôn đi cùng với nhau, nghĩa là một chương trình khi thực thi có thể gặp một số trường hợp xấu. ví dụ gỉa sử chúng ta có một đoạn code mở một file mà nếu file đó không tồn tại thì khi chạy chương trình đó sẽ xảy ra lỗi dẫn đến chương trình sẽ dừng lại.

Để xử lý trường hợp này thì theo phương pháp truyền thống chúng ta làm là sử dụng trả về code trong trường hợp xấu, nghĩa là trong ví dụ trên khi mở file không tồn tại thì sẽ trả về một số gía trị lỗi xác định để báo rằng nó đã trượt. Do đó dẫn đến chúng ta phải quản lý tất cả các lỗi, Nếu một đoạn code gọi đến các hàm mở file, đọc file sau đó đóng file thì chúng ta sẽ phải xử lý lỗi cho mỗi lần sẽ trả về một gía trị xác định, Nhưng làm thế nào để nhận biết các lỗi trả về các gía trị tương ứng với việc gọi chính nó. => Exceptions sẽ giải quyết vấn đề này.

Exceptions cho phép chúng ta đóng gói tất cả các thông tin về lỗi thành một đối tượng gọi là đối tượng exception.

Exception Class

Các gói chứa thông tin về exception là một đối tượng của lớp Exception hay một trong các lớp con của lớp Exception.

Các subclass của lớp Exception được xây dựng sẵn:

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

Ruby cho phép kế thừa các exception.

Để phát sinh một exception => chúng ta sẽ sử dụng lớp Exception được xây dựng sẵn hoặc có thể tạo ra lớp riêng.

Trường hợp tạo ra lớp riêng thì lớp đó phải là lớp con của lớp Exception hoặc là lớp con của các lớp con của nó. Nếu không sẽ không raise được exception.

Handling Exceptions

Ruby cung cấp một số cơ chế tốt để xử lý exceptions. Use rescue clauses: Để nói cho ruby biết kiểu exception mà chúng ta muốn xử lý Syntax :

begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
else
# Other exceptions
ensure
# Always will be executed
end

Khối code phát sinh exception sẽ được đặt trong khối begin..rescue...end.

rescue sẽ định nghĩa kiểu exception mà chúng ta mong muốn ( các kiểu exception đã được xây dựng sẵn). Trong trường hợp mà có thể phát sinh các exception ngoài mong muốn ở trên chúng ta sử dụng else.

ensure đảm bảo khối code sẽ được thực thi.

ví dụ:

➜  ~ irb
2.2.3 :001 > values = [1, "a", "b", 2.1, nil, "20", "avr", {name: "mai"}, Object]
 => [1, "a", "b", 2.1, nil, "20", "avr", {:name=>"mai"}, Object]
2.2.3 :002 > while values.length > 0
2.2.3 :003?>   a = values.pop
2.2.3 :004?>   b = values.pop
2.2.3 :005?>   begin
2.2.3 :006 >       a  + b
2.2.3 :007?>   rescue
2.2.3 :008?>      puts "Could not add variables a (#{a.class}) and b (#{b.class})"
2.2.3 :009?>   else
2.2.3 :010 >     puts "a + b = #{a+b}"
2.2.3 :011?>   end
2.2.3 :012?> end
Could not add variables a (Class) and b (Hash)
a + b = avr20
Could not add variables a (NilClass) and b (Float)
a + b = ba
Could not add variables a (Fixnum) and b (NilClass)
 => nil
2.2.3 :013 >

Use retry statement: Dùng để thực thi khối code giữa begin...rescue.

Ví dụ:

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
   fname = "existant_file"
   retry
end

Use raise statement: Phát sinh một exception bất cứ khi nào nó được gọi.

Cách sử dụng:

raise
#phát sinh exception hiện thời

raise "error message"
#phát sinh ra một exception với message "error message" của lớp RuntimeError( Exception mặc đinh).

raise ExceptionType,  "Error Message"
#phát sinh ra một exception với message "error message" của lớp được tạo (ExceptionType).

raise ExceptionType, "Error Message" conition
#giống như cách trên nhưng ta có thể thêm vào điều kiện để phát sinh một exception.

Use ensure statement: Để đảm bảo khối code bắt đầu được thực thi bất chấp nó có bắt gặp một exception hay raise ra một lỗi hay không.

Ví dụ:

➜  ~ irb
2.2.3 :001 >   begin
2.2.3 :002 >     1 / 0
2.2.3 :003?>   rescue ZeroDivisionError => e
2.2.3 :004?>   puts e.message
2.2.3 :005?>   ensure
2.2.3 :006 >     puts "Ensuring execution"
2.2.3 :007?>   end
divided by 0
Ensuring execution
 => nil
2.2.3 :008 >

Use else statement: Nó chỉ thực thi khi không có exception nào được phát sinh bởi rescuse. Ví dụ:

➜  ~ irb
2.2.3 :001 > a = 10
 => 10
2.2.3 :002 > b = 20
 => 20
2.2.3 :003 > begin
2.2.3 :004 >     a + b
2.2.3 :005?>   rescue
2.2.3 :006?>   puts "Could not add variables a (#{a.class}) and b (#{b.class})"
2.2.3 :007?>   else
2.2.3 :008 >     puts "a + b = #{a+b}"
2.2.3 :009?>   end
a + b = 30
 => nil
2.2.3 :010 >

Use Catch and Throw: Giúp chúng ta thoát ra khỏi một khối code mà không cần phải phát sinh exception. Nghĩa là gỉa sử có khối code A được lặp lại 5 lần, lần 1 chạy ok, lần 2 phát sinh lỗi thì chương trình sẽ bị ngắt và trả về nil, hoặc một cái gì khác nếu chúng ta có định nghĩa trong throw(không phát sinh exception)

Ví dụ:

2.2.3 :001 > arr = [2,4,6,10,100]
 => [2, 4, 6, 10, 100]
2.2.3 :002 > even_numbers = catch (:even_numbers) do
2.2.3 :003 >       result = []
2.2.3 :004?>     arr.each do |num|
2.2.3 :005 >           throw :even_numbers if num % 2 != 0
2.2.3 :006?>         result << num
2.2.3 :007?>       end
2.2.3 :008?>     result
2.2.3 :009?>   end
 => [2, 4, 6, 10, 100]
2.2.3 :010 >
2.2.3 :011 >   arr = [2,10,1,20,99]
 => [2, 10, 1, 20, 99]
2.2.3 :012 > even_numbers = catch (:even_numbers) do
2.2.3 :013 >       result = []
2.2.3 :014?>     arr.each do |num|
2.2.3 :015 >           throw :even_numbers if num % 2 != 0
2.2.3 :016?>         result << num
2.2.3 :017?>       end
2.2.3 :018?>     result
2.2.3 :019?>   end
 => nil
2.2.3 :020 >

=> ta thấy trong trường hợp thứ 2 mảng có số lẻ do đó nó sẽ thực thi throw và ngay lập tức trả về nil, nếu không muốn trả về nil ta có thể định nghĩa nó trả về mảng rỗng sử dụng throw( :even_numbers, []).

Tùy vào trường hợp cụ thể mà chúng ta có cách xử lý exception khác nhau.Ở trên là những gì cơ bản và tổng quát nhất vể Ruby Exception.

Tài liệu tham khảo:

All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.