+1

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 đượ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ố:

  1. Tham số đầu tiên là tên của phương thức mà bạn đang cố gọi tới
  2. Tham số thứ hai là những đối số (*args) được truyền khi gọi tới phương thức đó
  3. 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


All Rights Reserved

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