Ruby Metaprogramming - Method Missing
What is method_missing? method_missing là một phương thức mà ruby cho phép bạn truy cập vào bên trong một đối tượng và xử lý trong trường hợp bạn gọi tới một phương thức không tồn tại. Nó đơn giản giống như một đoạn xỷ lý Begin/Rescue, nhưng là cho lời gọi hàm. Nó cho phép bạn cơ hội cuối cùng ...
What is method_missing?
method_missing là một phương thức mà ruby cho phép bạn truy cập vào bên trong một đối tượng và xử lý trong trường hợp bạn gọi tới một phương thức không tồn tại. Nó đơn giản giống như một đoạn xỷ lý Begin/Rescue, nhưng là cho lời gọi hàm. Nó cho phép bạn cơ hội cuối cùng được xử lý khi phương thức gọi tới không tồn tại trước khi exception được trả về.
What does it look like?
method_mising nhận 3 tham số:
- Tham số đầu tiên là tên của phương thức mà bạn đang cố gọi tới
- Tham số thứ hai là những đối số (*args) được truyền khi gọi tới phương thức đó
- Tham số thứ ba là một block (&block) những tham số của phương thức Tham số thứ hai và ba có thể rỗng nếu phương thức gọi tới không tryền tham số
def method_missing(m, *args, &block) end
Using method_missing
Có thể đơn giản nghĩ tới đơn giản trường hợp sử dụng method_missing khi delegating lời gọi tới một phương thức của đối tượng khác. Bạn có thể viết một presenter đơn giản và tự định nghĩa phương thức delegation sử dụng method_missing. Đa số chúng ta có thể sẽ sử dụng SimpleDelegator để làm việc này, nhưng trong ví dụ dưới đây, chúng ta sẽ tự định nghĩa một presenter.
Creating a Presenter
Using Delegation and Method Missing
Chúng ta sẽ tạo một Presenter class. Mục đích của class là nhận một đối tượng trong hàm tạo, và bất kỳ phương thức không được thi hành bởi Presenter sẽ được delegated tới đối tượng mà nó nhận trong hàm tạo.
class Presenter attr_accessor :object def initialize(object) self.object = object end # If a method we call is missing, pass the call onto # the object we delegate to. def method_missing(m, *args, &block) puts "Delegating #{m}" object.send(m, *args, &block) end end
Chúng ta sẽ sử dụng lại Presenter này để tạo ra UserPresenter, đặc biệt được sử dụng cho chính các thực thể của class User.
class UserPresenter < Presenter # We just want to display the first letter of the last name def last_name "#{object.last_name[0]}." end def full_name "#{first_name} #{last_name}" end end # A mini User object to work with User = Struct.new(:first_name, :last_name, :age) user = User.new("Leigh", "Halliday", 30) user_presenter = UserPresenter.new(user) puts user_presenter.full_name puts user_presenter.age
Và dưới đây là output của các lời gọi trên.
Delegating first_name Leigh H. Delegating age 30
Re-Creating the Alias method
Để minh họa method_missing được sử dụng theo cách khác, chúng ta sẽ sử dụng alias method, gọi là mimic. Cái mà chúng ta sẽ tạo là một mimic phương thức, cái mà được gọi ở một cấp độ khác của class. Việc mà chúng ta làm là tạo một bảng tìm kiếm các phương thức qua định danh khác nhau. Khi chúng ta gọi một phương thức mà không tồn tại, đối tượng sẽ cố gắng tìm một định danh alias cho phương thức đó trước khi trả về thất bại nếu không tìm thấy.
class Mimic @@mimic_lookup = {} def self.mimic(to, from) @@mimic_lookup[to] = from end def method_missing(m, *args, &block) if @@mimic_lookup.include?(m.to_sym) self.send(@@mimic_lookup[m.to_sym], *args, &block) else raise ArgumentError.new("Method `#{m}` doesn't exist.") end end def respond_to?(method_name, include_private = false) @@mimic_lookup.include?(method_name.to_sym) || super end end class Alpaca < Mimic mimic :saludar, :greet def greet puts "Hey there" end end buddy = Alpaca.new p buddy.respond_to?(:saludar) buddy.saludar buddy.welcome
Chúng ta thấy có thể gọi thành công tới greet khi sử dụng alias saludar (tiếng Tây Ban Nha cho greet), và sau đó trả về chính xác lỗi khi chúng ta gọi tới phương thức welcome
true Hey there alias.rb:13:in `method_missing': Method `welcome` doesn't exist. (ArgumentError) from alias.rb:31:in ` '
Một điều quan trong không nhắc tới trong bài viết trên về method_missing, đó là sẽ tốt hơn nếu luôn override lại phương thức respond_to? khi bạn làm việc với method_missing
REFS
Ruby Metaprogramming - Method Missing