0

I would like to get a tree of the enclosing methods for this method. Here is my code (with hopefully descriptive comments):

enclosing_method(var1,var2) do
    enclosing_method(var3,var4) do
        get_tree # This method should return an array of it's enclosing methods
        # e.g. [get_tree, enclosing_method, enclosing_method, main]
    end
end

How can I do this? If this is unclear please let me know, I'm having trouble wording my question. Also, my title is bad as well. If anyone can think of a better title please suggest it in comments.

EDIT:

I learned from @WandMaker's answer comment that nested methods are not possible. So, I am semi changing the question. What about:

class Myclass
    @@all_instances
    def initialize
        @parents = get_tree # method that will return
        # all of the containing instances / anythings
        @content = yield
        @@all_instances << self
    end
    attr_reader :content
    attr_reader :parents
end
Myclass.new do
    Myclass.new do
        # bonus points if you make this possible!
        # I don't really need this to work but
        # it's a kewl thing so please try
        get_tree # => [Myclass, Myclass, <main>]
    end
end

What I am looking for is what the method get_tree would have to be to have the following outputs:

> Myclass.all_instances[0].parents # => [<main>]
> Myclass.all_instances[1].parents # => [Myclass, <main>]

Lemme know in comments if this is confusing or in any way nonsensical. I will fix it. I promise. Thanks to all you geniuses in advance for figuring this out. I am eternally grateful.

thesecretmaster
  • 1,950
  • 1
  • 27
  • 39

1 Answers1

1

You actually can nest method blocks within each other like you mentioned in your question. (Simple example: [1, 2, 3].each{|x| x.tap{|y| puts y}}) Finding out programmatically what that nesting is is a bit trickier, but doable.

If you're trying to build a DSL similar to Erector, there are actually a few different ways you could approach the problem. Perhaps the simplest is to push some value onto a global stack when the method call begins, then reference that stack when you want to see what the current nesting is. For example:

@call_stack = []
def some_method
  @call_stack.push("some_method")
  yield
ensure
  @call_stack.pop
end
def some_method2
  @call_stack.push("some_method2")
  yield
ensure
  @call_stack.pop
end

Usage:

some_method do
  some_method2 do
    @call_stack #=> ["some_method", "some_method2"]
   end
end

Another possibility is to use instance_exec to change the calling context inside the block, and instantiate a new object use as the calling context for each nested level. That's a bit more complicated though, so I won't cover it here.

Finally, just for completeness I should mention that if you only want this for debugging purposes, there's also the option of using caller_locations to get a full stack trace. That's pretty simple to use, but doesn't sound like what you want in this case.

Ajedi32
  • 45,670
  • 22
  • 127
  • 172