9

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

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返回 true。

SaysHello但是,当您想同时使用and时,这并不能很好地工作SaysGoodbye

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、模块和super交互也是非常有用的。

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

4

2 回答 2

5

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

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

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

关于超级(更新)

当你调用superRuby 时,它会在继承链上继续寻找所调用方法的下一个定义,如果找到,它会调用它并返回响应。当您调用时,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”是一个字符串。

现在有意义吗?

于 2011-06-16T02:33:49.757 回答
2

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

于 2013-12-10T17:53:28.720 回答