Hiểu về Method Missing trong Ruby
Trong bài này chúng ta sẽ tìm hiểu Method Missing trong Ruby, đây là một khái niệm muốn nói đến một phương thức không tồn tại trong Ruby, nên hiểu nó sẽ giúp ích cho bạn rất nhiều trong quá trình làm việc với Ruby.
1. Method missing là gì ?
Chắc hẳn với các lập trình viên chúng ta ai cũng có thể đôi lần dù cố tình hay vô tình gọi đến một method không tồn tại. Điều này thì chắc chắn sẽ gây ra thông báo lỗi mặc định của Ruby rồi.
Thế nhưng Ruby đã xử lý như thế nào với những method không tồn tại này thì trong bài này mình sẽ giới thiệu cho các bạn một phương thức mà Ruby đã gọi tới là chính là method missing.
Đúng như tên gọi của nó method_missing, thì chúng ta cũng có thể hiểu được nôm na chức năng của phương thức này là gì. Nó sẽ được gọi tới khi mà một method được gọi không tồn tại.
Bài viết này được đăng tại [free tuts .net]
2. Method lookup
Để có một cái nhìn rõ hơn về method missing thì trước tiên chúng ta nên tìm hiểu xem khi nào thì method này được gọi tới.
class Animal def hello puts "Hello animal" end end class Dog < Animal end a = Dog.new puts a.hello # Hello animal
Theo như ví dụ ở trên thì chúng ta có một đối tượng được tạo ra từ class Dog được kế thừa từ class Animal. Chúng ta gọi đến một phương thức có tên là hello, thế nhưng rõ ràng trong class Dog** không hề tồn tại do đó nó đã tiếp tục gọi đến superclass của nó đó là Animal.
Và cứ như thế nếu như Animal không tồn tại phương thức hello thì nó sẽ lần lượt gọi tới các superclass khác để tìm kiếm phương thức này. Nếu như không tìm thấy thì method cuối cùng được gọi tới chính là method_missing. Đây là một private method được xây dựng sẵn của class BasicObject.
Mà theo như Ancestors chain của một class thì
Class.ancestors => [Class, Comparable, Object, Kernel, BasicObject]
Nếu như bạn không biết Ancestors chain là gì thì mình sẽ giải thích đơn giản như sau
- Tất cả các class mà nó kế thừa.
- Tất cả các module được mix trong nó.
Do class được kế thừa từ tổ tiên của nó là BasicObject nên nó sẽ sử dụng được method_missing.
Nếu như bạn hỏi tại sao mà mình biết nó thuộc class BasicObject thì mở terminal lên và gõ ngay lệnh
BasicObject.private_methods.sort
để xác thực nhé :D
Trong cây phân lớp class thì BasicObject là tổ tiên cuối cùng của mọi class khác.
3. No method error
Chúng ta có một ví dụ đơn giản
class Dog def talk puts "Dog go go" end end dog = Dog.new puts dog.talk
Kết quả
Dog go go
Kết quả vẫn hoạt động bình thường thế nhưng hãy thử gọi lại một phương thức không tồn tại.
puts dog.eat # Kết quả Traceback (most recent call last): method_missing.rb:8:in `<main>': undefined method `eat' for #<Dog:0x000055632c3cc550> (NoMethodError)
Vậy là con dog của chúng ta không có chức năng ăn rồi, chúng ta sẽ nhận được ngay được thông báo lỗi của chương trình NoMethodError, và cũng thấy Ruby log luôn lỗi tại file nào và dòng nào luôn theo như log được hiển thị.
Thế nhưng bây giờ thay vì muốn nhận được một Exception error thì chúng ta muốn nhận được một message rõ ràng hơn thay vì nhận được một message mặc định của Ruby. Thì đây là lúc chúng ta sử dụng tới method_missing rồi đây.
4. Cú pháp và cách sử dụng
Để khai báo một method missing trong Ruby thì ta sử dụng cú pháp sau.
def method_missing method_name, *args, &block # code end
Cú pháp cũng giống như các phương thức khác và trong method này sẽ nhận vào các tham số như
- method_name: Tên của phương thức được gọi tới mà không tồn tại/ không được định nghĩa
- *arg: Đây là một mảng các tham số chứa các giá trị của các tham số được truyền vào phương thức không tồn tại
- &block: Tham số này không bắt buộc, nó thể hiện phương thức đang được gọi có đi kèm một block ở tận cùng.
Lưu ý: Kiểu dữ liệu mà method_name trả về sẽ là một Symbol nhé
Thử viết lại một message khác mà chúng ta muốn thay vì là sử dụng Exception Error của Ruby mặc định. Để làm được việc này chúng ta chỉ cần overwrite lại phương thức method_missing của class BasicObject.
class Dog def talk puts "Dog go go" end def method_missing method_name, *arg if method_name.to_s == 'eat' puts "Dog don't need eat" else super end end end dog = Dog.new puts dog.eat
Kết quả khi chạy chương trình
Dog don't need eat
Kết quả đã ra đúng như mong muốn. Giờ tiếp tục thử với một method không tồn tại khác xem sao
puts dog.run # kết quả method_missing.rb:10:in `method_missing': undefined method `run' for #<Dog:0x0000563740d04210> (NoMethodError)
Kết quả lại trả về NoMethodError như ban đầu. Đơn giản vì trên đoạn code trên chúng ta chỉ kiểm tra đối với nếu method được gọi tới có tên là eat còn nếu không sẽ nhảy vào else và gọi tới super. Từ khóa này sẽ gọi lại phương thức method_missing của BasicObject.
2. Kết luận
Qua bài bài này chúng ta đã cùng đi tìm hiểu xong cách sử dụng một method missing trong Ruby, từ đó cũng hiểu luôn khi nào thì phương thức này được gọi tới.
Khi mà các bạn làm việc với Ruby on Rails (một framework của Ruby) thì đã có một vài các phương thức mà họ sử dụng method_missing để định nghĩa các hàm có sẵn có thể gọi, nếu không nó cũng sẽ gọi tới keyword super để gọi phương thức method_missing trong BasicObject giống như cách mà chúng ta đã làm ở trên.