Search code examples
rubymetaprogrammingsubclasssuperruby-2.3

Is the current Ruby method called via super?


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_super?
    # what goes here?
  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!"

How could I write via_super?, or, if necessary, via_super?(:bar)?


Solution

  • The ultimate mix between my other, @mudasobwa's and @sawa's answers plus recursion support:

    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
    

    The only case that wont work (AFAIK) is if you have indirect recursion in the base class, but I don't have ideas how to handle it with anything short of parsing the code.