何时使用`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)