Is the current Ruby method called via super?

后端 未结 5 1598
北海茫月
北海茫月 2021-01-05 19:15

Within a method at runtime, is there a way to know if that method has been called via super in a subclass? E.g.

module SuperDetector
  def via_s         


        
5条回答
  •  忘掉有多难
    2021-01-05 20:02

    An addendum to an excellent @ndn approach:

    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!"
    

    Here we use Kernel#caller to make sure that the name of the method called matches the name in super class. This approach likely requires some additional tuning in case of not direct descendant (caller(2) should be changed to more sophisticated analysis,) but you probably get the point.

    UPD thanks to @Stefan’s comment to the other answer, updated with unless defined to make it to work when both Foo and Fu include SuperDetector.

    UPD2 using ancestors to check for super instead of straight comparison.

提交回复
热议问题