何时使用`method_missing`

时间:2012-12-07 09:04:05

标签: ruby metaprogramming

在下面的代码中,方法roar未在类Lion中定义,但仍可使用method_missing进行调用。

class Lion
  def method_missing(name, *args)
    puts "Lion will #{name}: #{args[0]}"
  end
end

lion = Lion.new
lion.roar("ROAR!!!") # => Lion will roar: ROAR!!!

在哪些情况下我应该如何使用此method_missing?使用安全吗?

5 个答案:

答案 0 :(得分:9)

完全安全使用,只要您以预期的方式使用它并且不会被带走。毕竟,并非你所能做的一切都值得做。

method_missing的优点是你可以用独特的方式回应各种事物。

缺点是你没有宣传你的能力。期望您respond_to?某些内容的其他对象无法获得确认,并且可能会以您不想要的方式处理您的自定义对象。

为了构建领域特定语言并在组件之间提供非常松散的粘合剂,这种东西是非常宝贵的。

一个很好的例子是Ruby OpenStruct类。

答案 1 :(得分:8)

摘要:何时使用?什么时候会让你的生活更轻松不会使他人的生活变得复杂。


这是一个浮现在脑海中的例子。它来自redis_failover gem。

# Dispatches redis operations to master/slaves.
def method_missing(method, *args, &block)
  if redis_operation?(method)
    dispatch(method, *args, &block)
  else
    super
  end
end

这里我们检查调用的方法是否实际上是redis连接的命令。如果是这样,我们将其委托给底层连接。如果没有,请转发到超级。

method_missing应用程序的另一个着名示例是ActiveRecord查找程序。

User.find_by_email_and_age('me@example.com', 20)

当然,没有一种方法find_by_email_and_age。相反,method_missing会破坏名称,分析部分并使用适当的参数调用find

答案 2 :(得分:4)

这是我的最爱

class Hash
  def method_missing(sym,*args)
    fetch(sym){fetch(sym.to_s){super}}
  end
end

这让我可以像访问属性那样访问哈希值。在使用JSON数据时,这非常方便。

因此,例如,我可以写tweets.collect{|each|each['text']}而不是写tweets.collect(&:text),而不是写tweets.first['author']。或者,而不是tweets.first.author我可以写{{1}}感觉更自然。实际上,它为您提供了对哈希值的Javascript样式访问。

NB,我期待猴子在我家门口修补警察......

答案 3 :(得分:4)

首先,请坚持Sergio Tulentsev的总结。

除此之外,我认为看一些例子是了解method_missing的正确与错误情况的最佳方式;所以这是另一个简单的例子:


我最近在Null Object中使用了method_missing

  • Null对象是订单模型的替代品。

  • 订单存储不同货币的不同价格。


没有method_missing,它看起来像这样:

class NullOrder
  def price_euro
    0.0
  end

  def price_usd
    0.0
  end

  # ...
  # repeat for all other currencies
end

使用method_missing,我可以将其缩短为:

class NullOrder
  def method_missing(m, *args, &block)  
    m.to_s =~ /price_/ ? 0.0 : super
  end
end

当我向NullOrder添加新的price_xxx属性时,不必(记得)更新Order的附加好处。

答案 4 :(得分:3)

我还发现了(Paolo Perrotta)的一篇博文,其中展示了何时使用method_missing:

class InformationDesk
  def emergency
    # Call emergency...
    "emergency() called"
  end

  def flights
    # Provide flight information...
    "flights() called"
  end

  # ...even more methods
end

检查午餐时间是否有人询问过服务。

class DoNotDisturb
  def initialize
    @desk = InformationDesk.new
  end

  def method_missing(name, *args)
    unless name.to_s == "emergency"
      hour = Time.now.hour
      raise "Out for lunch" if hour >= 12 && hour < 14
    end

    @desk.send(name, *args)
  end
end

# At 12:30...
DoNotDisturb.new.emergency # => "emergency() called"
DoNotDisturb.new.flights # ~> -:37:in `method_missing': Out for lunch (RuntimeError)