Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I find a method's nesting in Ruby?

In Ruby, constant lookup is affected by nesting and methods retain their nesting.

For example, if I have these modules:

module A
  X = 1
end

module B
  X = 2
end

module Foo
end

I can define a method Foo.a that has a nesting of [A, Foo]:

module Foo
  module ::A
    Module.nesting #=> [A, Foo]
    def Foo.a
      X
    end
  end
end

Foo.a #=> 1

And a method Foo.b that has a nesting of [Foo, B]:

module B
  module ::Foo
    Module.nesting #=> [Foo, B]
    def Foo.b
      X
    end
  end
end

Foo.b #=> 2

The difference becomes apparent if I define Foo::X:

Foo::X = 3

Foo.a #=> 1  <- still resolves to A::X
Foo.b #=> 3  <- now resolves to Foo::X

But how do I determine the nesting of a given method?

like image 739
Stefan Avatar asked Dec 01 '16 09:12

Stefan


People also ask

Can you put a method inside a method in Ruby?

In short: no, Ruby does not support nested methods.

How do you call a method object in Ruby?

We call (or invoke) the method by typing its name and passing in arguments. You'll notice that there's a (words) after say in the method definition. This is what's called a parameter. Parameters are used when you have data outside of a method definition's scope, but you need access to it within the method definition.

How do you return a value from a method in Ruby?

- in ruby, there are 2 types of return from the method: explicit return (using the return keyword) and implicit return; - exception from all rules about return value — assignment methods; - a return value can be directly assigned to variables.


2 Answers

You are thinking about it the wrong way. There is no such thing as "nesting of methods". Constants are nested somewhere. Nesting has path resolution connected to names of modules and classes. Methods are contained within a module/class (be it "normal" or singleton).


Where a method is placed is semantical. There is a concept, similar to self, which determines where a method will be defined, called default definee. There is no keyword for it, but it's roughly equivalent to:

kind_of?(Module) ? name : self.class.name

Where a constant is placed/searched for is purely syntactical. When you refer to X, it does not care the least bit if you placed it a method or not:

DEEP_MIND = Object.new

module Foo
  X = 42
end

module Foo
  module Bar
    def DEEP_MIND.talk
      p X
    end
  end
end

DEEP_MIND.talk # => 42

module Foo::Bar
  def DEEP_MIND.talk
    p X
  end
end

DEEP_MIND.talk # => uninitialized constant

All it cares about is what the "current nesting" on the line of the code where you tried to reference it.


Now, if you actually wanted to find "the current nesting inside the body of the method", then you need some way to pretend you are actually there.

Sadly, I don't think there is any other way other than the one showed in @Eric's answer. Using block with instance_eval/instance_exec will give you the nesting of where the block was defined.

like image 22
ndnenkov Avatar answered Oct 19 '22 04:10

ndnenkov


This would work:

Foo.method(:a).to_proc.binding.eval('Module.nesting')
#=> [A, Foo]

Foo.method(:b).to_proc.binding.eval('Module.nesting')
#=> [Foo, B]

Tested with Ruby 2.2.1 and 2.3.1. It doesn't work with Ruby 2.1.5.

like image 195
Eric Duminil Avatar answered Oct 19 '22 05:10

Eric Duminil