当前的Ruby方法是通过super调用的吗?

在运行时的方法中,有没有办法知道该方法是否已通过子类中的super调用? 例如

 module SuperDetector def via_super? # what goes here? end end class Foo include SuperDetector def bar via_super? ? 'super!' : 'nothing special' end end class Fu  "nothing special" Fu.new.bar # => "super!" 

我怎么能写via_super? ,或者,如有必要, via_super?(:bar)

我的另一个 , @ mudasobwa和@sawa 的答案以及递归支持之间的最终组合:

 module SuperDetector def self.included(clazz) unless clazz.instance_methods.include?(:via_super?) clazz.send(:define_method, :via_super?) do first_caller_location = caller_locations.first calling_method = first_caller_location.base_label same_origin = ->(other_location) do first_caller_location.lineno == other_location.lineno and first_caller_location.absolute_path == other_location.absolute_path end location_changed = false same_name_stack = caller_locations.take_while do |location| should_take = location.base_label == calling_method and !location_changed location_changed = !same_origin.call(location) should_take end self.kind_of?(clazz) and !same_origin.call(same_name_stack.last) end end end end 

唯一不起作用的情况(AFAIK)是如果你在基类中有间接递归,但我没有想法如何处理它与解析代码之间的任何事情。

可能有更好的方法,但一般的想法是Object#instance_of? 仅限于当前类,而不是层次结构:

 module SuperDetector def self.included(clazz) clazz.send(:define_method, :via_super?) do !self.instance_of?(clazz) end end end class Foo include SuperDetector def bar via_super? ? 'super!' : 'nothing special' end end class Fu < Foo def bar super end end Foo.new.bar # => "nothing special" Fu.new.bar # => "super!" 

但是,请注意,这不需要在孩子中显式super 。 如果孩子没有这样的方法并且使用了父母的方法,那么via_super? 仍然会返回true 。 除了检查堆栈跟踪或代码本身之外,我认为没有办法只捕获super大小写。

一个优秀的@ndn方法的附录:

 module SuperDetector def self.included(clazz) clazz.send(:define_method, :via_super?) do self.ancestors[1..-1].include?(clazz) && caller.take(2).map { |m| m[/(?<=`).*?(?=')/] }.reduce(&:==) # or, as by @ndn: caller_locations.take(2).map(&:label).reduce(&:==) end unless clazz.instance_methods.include? :via_super? end end class Foo include SuperDetector def bar via_super? ? 'super!' : 'nothing special' end end class Fu < Foo def bar super end end puts Foo.new.bar # => "nothing special" puts Fu.new.bar # => "super!" 

这里我们使用Kernel#caller来确保所Kernel#caller方法的名称与超类中的名称匹配。 这种方法可能需要一些额外的调整,以防不是直接后代( caller(2)应该更改为更复杂的分析),但你可能得到了重点。

UPD感谢include SuperDetector对其他答案的评论,更新时unless defined为在FooFu include SuperDetector时使其工作。

UPD2使用祖先检查超级而不是直接比较。

这是一个更简单(几乎是微不足道)的方法,但你必须传递当前类和方法名称:(我还将方法名称从via_super?更改为called_via?

 module CallDetector def called_via?(klass, sym) klass == method(sym).owner end end 

用法示例:

 class A include CallDetector def foo called_via?(A, :foo) ? 'nothing special' : 'super!' end end class B < A def foo super end end class C < A end A.new.foo # => "nothing special" B.new.foo # => "super!" C.new.foo # => "nothing special" 

编辑改进,遵循Stefan的建议。

 module SuperDetector def via_super? m0, m1 = caller_locations[0].base_label, caller_locations[1]&.base_label m0 == m1 and (method(m0).owner rescue nil) == (method(m1).owner rescue nil) end end