8

在下面的代码中,methodroar没有在 class 中定义Lion,但仍然可以使用 using 来调用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?使用安全吗?

4

5 回答 5

9

只要您以预期的方式使用它并且不要被冲昏头脑,使用它是完全安全的。毕竟,不是你能做的每一件事都值得去做。

的好处method_missing是你可以以独特的方式应对各种事情。

缺点是你不宣传你的能力。其他期望你respond_to?做某事的对象不会得到确认,并且可能会以你不想要的方式对待你的自定义对象。

对于构建领域特定语言并在组件之间提供非常松散的粘合,这种事情是无价的。

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

于 2012-12-07T09:11:27.017 回答
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连接的命令。如果是这样,我们将其委托给底层连接。如果没有,转发给super。

另一个著名的method_missing应用示例是 ActiveRecord 查找器。

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

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

于 2012-12-07T09:07:54.057 回答
4
于 2012-12-07T09:11:51.323 回答
4

首先,坚持 Sergio Tulentsev 的总结。

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


我最近method_missingNull Object中使用了。

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

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


没有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.

于 2012-12-07T15:47:38.000 回答
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)
于 2012-12-07T09:32:50.807 回答