【问题标题】:How to compose modules containing method_missing in ruby如何在 ruby​​ 中编写包含 method_missing 的模块
【发布时间】:2011-09-16 00:39:38
【问题描述】:

我有几个扩展方法缺失的模块:

module SaysHello
    def respond_to?(method)
        super.respond_to?(method) || !!(method.to_s =~ /^hello/)
    end
    def method_missing(method, *args, &block)
        if (method.to_s =~ /^hello/)
            puts "Hello, #{method}"
        else
            super.method_missing(method, *args, &block)
        end
    end
end

module SaysGoodbye
    def respond_to?(method)
        super.respond_to?(method) || !!(method.to_s =~ /^goodbye/)
    end
    def method_missing(method, *args, &block)
        if (method.to_s =~ /^goodbye/)
            puts "Goodbye, #{method}"
        else
            super.method_missing(method, *args, &block)
        end
    end
end

class ObjectA
    include SaysHello
end

class ObjectB
    include SaysGoodbye
end

这一切都很好,例如ObjectA.new.hello_there 输出"Hello, hello_there"。同样,ObjectB.new.goodbye_xxx 输出 "Goodbye, xxx"respond_to? 也可以,例如ObjectA.new.respond_to? :hello_there return true。

但是,当您想同时使用 SaysHelloSaysGoodbye 时,这并不好用:

class ObjectC
    include SaysHello
    include SaysGoodbye
end

虽然ObjectC.new.goodbye_aaa 工作正常,但ObjectC.new.hello_a 行为却很奇怪:

> ObjectC.new.hello_aaa
Hello, hello_aaa
NoMethodError: private method `method_missing' called for nil:NilClass
    from test.rb:22:in `method_missing' (line 22 was the super.method_missing line in the SaysGoodbye module)

它正确输出,然后抛出错误。 respond_to? 也不正确,ObjectC.new.respond_to? :hello_a 返回 false。

最后,添加这个类:

class ObjectD
    include SaysHello
    include SaysGoodbye

    def respond_to?(method)
        super.respond_to?(method) || !!(method.to_s =~ /^lol/)
    end


    def method_missing(method, *args, &block)
        if (method.to_s =~ /^lol/)
            puts "Haha, #{method}"
        else
            super.method_missing(method, *args, &block)
        end
    end
end

行为也很奇怪。 ObjectD.new.lol_zzz 有效,但是 ObjectD.new.hello_aand ObjectD.new.goodbye_t 在输出正确的字符串后都会抛出名称异常。 respond_to? 也无法使用 hello 和 goodbye 方法。

有没有办法让这一切正常工作?解释method_missing、Modules 和super 是如何交互的也是非常有用的。

编辑:coreyward 解决了这个问题,如果我在我定义的所有方法中使用 super 而不是 super.&lt;method-name&gt;(args...),程序可以正常工作。我不明白为什么会这样,所以我在What does super.<method-name> do in ruby?问了另一个问题@

【问题讨论】:

  • 包含的模块被添加到继承链中;他们不会覆盖或替换方法。所以如果每个method_missing调用super,最终它们都会被调用。请参阅下面的答案。

标签: ruby method-missing


【解决方案1】:

http://www.perfectline.ee/blog/activerecord-method-missing-with-multiple-inheritance

本文详细解释了它的工作原理:每个新模块都不会覆盖或替换方法 - 相反,它的方法被插入到继承链中。这就是为什么从每个method_missing调用super最终会调用所有method_missing。

类在继承链中保持最低,最后添加的模块与该类相邻。

所以:

class Foo
  include A
  include B
end

导致内核 -> A -> B -> Foo

【讨论】:

    【解决方案2】:

    当你重新定义一个方法时,你就重新定义了一个方法;期间。

    当您使用 method_missing 方法定义包含第二个模块时,您所做的是覆盖先前定义的 method_missing。您可以在重新定义它之前通过对其进行别名来保留它,但您可能需要注意这一点。

    另外,我不知道你为什么打电话给super.method_missing。一旦你的 method_missing 定义没有技巧,你应该让 Ruby 知道它可以继续在链上寻找处理调用的方法,只需调用 super (无需传递参数或指定方法名称) .

    关于 Super(更新)

    当您调用super 时,Ruby 继续沿着继承链查找所调用方法的下一个定义,如果找到,它会调用它并返回响应。当你调用super.method_missing 时,你调用method_missing 方法来响应super()

    举这个(有点傻的)例子:

    class Sauce
      def flavor
        "Teriyaki"
      end
    end
    
    # yes, noodles inherit from sauce:
    #   warmth, texture, flavor, and more! ;)
    class Noodle < Sauce
      def flavor
        sauce_flavor = super
        "Noodles with #{sauce_flavor} sauce"
      end
    end
    
    dinner = Noodle.new
    puts dinner.flavor     #=> "Noodles with Teriyaki sauce"
    

    你可以看到 super 和其他方法一样,只是在幕后发挥了一些作用。如果您在这里调用super.class,您将看到String,因为“Teriyaki”是一个字符串。

    现在有意义吗?

    【讨论】:

    • 但是导入第二个模块并没有覆盖第一个模块的method_missing,否则ObjectC.new.hello_a不会输出Hello, hello_a(加上一个NameError)。使用 super 而不是 super.method_missing(...) 实际上解决了问题,所有的method_missing调用和respond_to?呼叫现在可以正常工作。我不明白为什么。
    • @nanothief:我已经更新了这个问题(希望)更多地解释super 在 Ruby 中的工作原理。我想它会为你解决问题。
    • 谢谢,这就是我在使用 super 时遇到的问题。我假设 super.method 是在父类上调用方法,而不是在调用超类上的当前方法的结果上调用方法。
    • 也许应该澄清“覆盖”的意思是“插入到继承链中”,“超级”的意思是“调用更高的继承链”。这就是为什么从每个method_missing调用super最终调用所有method_missing。
    猜你喜欢
    • 2013-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-30
    • 2016-02-24
    • 1970-01-01
    • 2015-02-10
    相关资源
    最近更新 更多