【问题标题】:When to use `method_missing`何时使用`method_missing`
【发布时间】:2012-12-07 09:04:05
【问题描述】:

在下面的代码中,方法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应该在哪些情况下以及如何使用?使用起来安全吗?

【问题讨论】:

  • Ghost 方法,我猜通常是元编程工具。注意效率。 Ghost方法很慢。
  • 我认为您的意思是 class Lion 而不是 Class Lion
  • @EricWalker:谢谢,输入错误。
  • @EricWalker -- 真的吗,伙计?
  • @DylanRichards 是的,必须使用小写的class。在 Ruby 中,class 是类似 ifdef 的关键字,而 Class 是类的名称。 Class Lion 将产生 NameError: uninitialized constant Lion

标签: ruby metaprogramming


【解决方案1】:

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

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

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

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

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

【讨论】:

  • +1 提及method_missing 的孪生兄弟responds_to?
  • 实际上,method_missing 的孪生兄弟是respond_to_missing? 你应该总是一起覆盖它们。
  • @JörgWMittag 从未听说过respond_to_missing 那是一颗宝石!?
  • @akuhn:它在标准库中:Object#respond_to_missing?
  • @SergioTulentsev 谢谢!必须是 1.9 方法,在我在 OSX 上安装的 1.8.7 中不存在。
【解决方案2】:

总结:什么时候使用?什么时候它会让你的生活更轻松并且不会让别人的生活复杂化。


这是一个让我想起的例子。来自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

【讨论】:

    【解决方案3】:

    这是我的最爱

    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,感觉更自然。实际上,它为您提供了对哈希值的 Javascript 样式访问。

     

    注意,我期待猴子随时在我家门口修补警察......

    【讨论】:

    • 您还可以编写一个发出过程的方法,例如tweets.collect(&attr('text')),即:def attr(a); lambda { |e| e[a] }; end
    • 你是对的,但是我不能写tweets.first.text(更新了我的帖子)。
    • 与其自己动手,不如使用 OpenStruct?自己创建一个“裸”对象是很棘手的。如果您有 hash 这样的键,它将与内部方法冲突。
    • @tadman JSON.parse(file) 不创建 OpenStruct 而是创建 Hash 实例,因此我的猴子补丁。
    【解决方案4】:

    首先,坚持 Sergio Tulentsev 的总结。

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


    我最近在Null Object 中使用了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 的额外好处。

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-27
        • 2023-04-03
        • 1970-01-01
        • 2011-04-04
        • 2017-04-18
        • 1970-01-01
        相关资源
        最近更新 更多