12/08/2018, 14:46

Cơ bản về Exception trong Ruby

Exception là một khái niệm không lạ trong Ruby, có thể bạn đã từng sử dụng nó nhưng có chắc bạn biết chính xác Exception là gì hay tại sao nó lại hữu dụng. Nếu chưa, hy vọng bạn sẽ tìm thấy câu trả lời sau khi đọc bài viết này. Exception là gì? Exception dịch sang tiếng việt là ngoại lệ, là ...

Exception là một khái niệm không lạ trong Ruby, có thể bạn đã từng sử dụng nó nhưng có chắc bạn biết chính xác Exception là gì hay tại sao nó lại hữu dụng. Nếu chưa, hy vọng bạn sẽ tìm thấy câu trả lời sau khi đọc bài viết này.

Exception là gì?

Exception dịch sang tiếng việt là ngoại lệ, là cách giải quyết khi gặp một sự kiện không mong muốn trong Ruby. Nếu bạn đã từng gõ sai một lỗi nào đó khi lập trình, như kiểu quên dấu chấm phẩy hay gõ sai chính tả, chương trình sẽ lập tức trả về cho bạn một thông báo như SyntaxError hay NoMethodError, với những bạn mới lập trình Ruby như mình thì thông báo này quá quen thuộc, đó chính là một Exception. Đây là một ví dụ. Trong đoạn code bên dưới, chúng ta cố gắng chia cho 0. Điều đó là không thể, vì vậy Ruby sẽ trả về exception gọi là ZeroDivisionError. Chương trình kết thúc và in ra thông báo lỗi

1 / 0
# Program crashes and outputs: "ZeroDivisionError: divided by 0"

Chương trình kết thúc như vậy khiến chúng ta rất bực mình, vì vậy chúng ta sẽ muốn ngăn không cho nó kết thúc như vậy và phản hồi một cách thông minh hơn. Nó gọi là "rescuing", "handling" hay "catching" exception. Nó có ý nghĩa tương đương nhau, hiểu đơn giản là xử lý lỗi đó. Đây là cách chúng ta làm trong Ruby

begin
  # Any exceptions in here... 
  1/0
rescue
  # ...will cause this code to run
  puts "Got an exception, but I'm responding intelligently!"
  do_something_intelligent()
end
# This program does not crash.
# Outputs: "Got an exception, but I'm responding intelligently!"

Exception vẫn xảy ra, nhưng chương trình không bị hỏng vì nó đã được “rescued”. Thay vì thoát ra, Ruby sẽ chạy đoạn code trong rescue, in ra thông báo lỗi. Điều này khá tốt, nhưng vẫn có một hạn chế khá lớn. nó chỉ cho bạn biết một cái gì đó bị lỗi, nhưng bạn lại không biết chính xác là cái gì gây ra lỗi. Tất cả thông tin về lỗi đó được lưu trong đối tượng exception.

Đối tượng exception

Đối tượng exception cũng giống các đối tượng khác trong Ruby. Nó chứa tất cả dữ liệu về exception mà bạn vừa rescue. Để lấy được đối tượng exception, bạn sẽ phải sử dụng cú pháp rescue hơi khác một chút

# Rescues all errors
rescue => e

# Rescues only ZeroDivisionError 
rescue ZeroDivisionError => e

Đối tượng Exception chứa những thông tin khá hữu ích, như là đối với ZeroDivisionError

begin
  1/0
rescue ZeroDivisionError => e
  puts "Exception Class: #{e.class.name}"
  puts "Exception Message: #{e.message}"
  puts "Exception Backtrace: #{e.backtrace}"
end

# Outputs:
# Exception Class: ZeroDivisionError
# Exception Message: divided by 0
# Exception Backtrace: ...backtrace as an array...

Raise exception

Chúng ta mới nói đến rescue một exception, ngoài ra bạn có thể tạo một exception bằng cách gọi đến phương thức raise. Khi bạn raise exception, bạn có thể chọn loại exception để sử dụng và thiết lập thông báo lỗi. Đây là một ví dụ

begin
  raise ArgumentError.new("You messed up!")
rescue ArgumentError => e  
  puts e.message
end

# Outputs: You messed up! 

Như bạn thấy, chúng ta vừa tạo một đối tượng mới ArgumentError với dòng thông báo "You messed up!". Trong Ruby, raise có thể được gọi bằng nhiều cách

# Đây là cách hay được sử dụng nhất
raise RuntimeError.new("You messed up!")

# ...cách này cũng cho kết quả tương tự
raise RuntimeError, "You messed up!"

Custom exception

Exception trong Ruby khá nhiều, nhưng nó không thể bao hàm được hết các trường hợp. Điều gì sẽ xảy ra khi bạn xây dựng một hệ thống cho người dùng và muốn raise exception khi người dùng cố gắng truy cập vào một trang không được cấp quyền? Không có exception nào trong Ruby phù hợp, cách tốt nhất là tự tạo một loại exception mới. Để làm được việc này, bạn chỉ cần viết một class mới kế thừa StandardError

class PermissionDeniedError < StandardError

end

raise PermissionDeniedError.new()

Class này cũng giống như những class khác trong ruby, có nghĩa bạn có thể thêm phương thức và dữ liệu như bình thường. Hãy thử thêm một attribute gọi là “action”

class PermissionDeniedError < StandardError
  attr_reader :action

  def initialize(message, action)
    super(message)
    @action = action
  end
end

# Và giờ khi một người dùng cố gắng xóa một cái gì họ không có quyền xóa, bạn có thể gọi thế này
raise PermissionDeniedError.new("Permission Denied", :delete)

Phân cấp Exception

Đây là hầu hết các exception trong Ruby, được xếp theo thứ tự phân cấp

Exception
 NoMemoryError
 ScriptError
   LoadError
   NotImplementedError
   SyntaxError
 SignalException
   Interrupt
 StandardError
   ArgumentError
   IOError
     EOFError
   IndexError
   LocalJumpError
   NameError
     NoMethodError
   RangeError
     FloatDomainError
   RegexpError
   RuntimeError
   SecurityError
   SystemCallError
   SystemStackError
   ThreadError
   TypeError
   ZeroDivisionError
 SystemExit

Tất nhiên bạn không cần nhớ hết tất cả, bài viết liệt kê ra chỉ để cho bạn thấy lý do vì sao việc phân cấp exception lại quan trọng. Đó là khi rescue một class cũng sẽ rescue các class con của nó.
Ví dụ như khi bạn rescue StandardError, bạn không chỉ rescue exception với class StandardError mà bạn còn rescue class con của nó như ArgumentError, IOError,...

Bài viết chỉ giải thích và liệt kê một số cách dùng đơn giản của exception. Mong rằng nó giúp ích cho bạn, cảm ơn đã theo dõi! Nguồn tham khảo: http://blog.honeybadger.io/a-beginner-s-guide-to-exceptions-in-ruby/

0