Ruby Metaprogramming - Method Missing
This post hasn't been updated for 7 years
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
All Rights Reserved