Search code examples
rubymetaprogramming

Ruby, How I can obtain the whole namespace definition path for a method inside the same method?


How I can obtain the whole namespace definition path for a method inside the same method?

Using this example to make easy the question:

module Parent
    module Child
        def self.get_complete_namespace
            this_path = ???
            puts this_path.to_s
        end
    end
end

Parent::Child.get_complete_namespace
#=> "Parent::Child.get_complete_namespace"

So to puts or print out this string "Parent::Child.get_complete_namespace", what is the "this_path" variable value using self super o something like that?


Solution

  • I don't know of any existing functions for this, though you could pretty easily create one using __callee__ (or __method__)

    module Parent
      module Child
        def self.get_complete_namespace
          m = method(__callee__)
          "#{m.receiver.name}.#{m.name}"
        end
      end
    end
    
    Parent::Child.get_complete_namespace
    #=> "Parent::Child.get_complete_namespace"
    

    Some usages notes:

    • __callee__ and __method__ will usually return the same symbol. If the method is aliased, then __callee__ returns the method name that was invoked, while __method__ will return the method name as it was defined.
    • For a class/module method, you need to use m.receiver. For an instance method, you need to use m.owner. See Method.

    Class example:

    module Parent
      class Child
        def self.get_complete_namespace
          m = method(__callee__)
          "#{m.receiver.name}.#{m.name}"
        end
    
        def get_complete_namespace
          m = method(__callee__)
          "#{m.owner.name}.#{m.name}"
        end
    
        alias_method :complete, :get_complete_namespace
      end
    end
    
    Parent::Child.get_complete_namespace
    #=> "Parent::Child.get_complete_namespace"
    
    Parent::Child.new.get_complete_namespace
    #=> "Parent::Child.get_complete_namespace"
    
    Parent::Child.new.complete
    #=> "Parent::Child.complete"
    

    Edit - one-liner version:

    def self.get_complete_namespace
      method(__callee__).instance_eval { "#{receiver.name}.#{name}" }
    end