何时使用`method_missing`

在下面的代码中,方法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 ? 使用安全吗?

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

method_missing的优点是您可以以独特的方式响应各种事物。

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

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

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

总结:何时使用? 什么时候会让你的生活更轻松而不会让别人的生活变得复杂。


这是一个浮现在脑海中的例子。 它来自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 finders。

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

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

这是我的最爱

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

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

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

不, 我期待猴子在我门口修补警察……

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

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

我最近在Null对象中使用了method_missing

  • 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 

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

我还发现了一篇来自(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)