hector
hector

Reputation: 967

Where do methods defined at the top-level exist?

I am learning some metaprogramming and I was stuck trying to find a method. Let's say I have the following class:

class MyClass
  def self.my_method
  end
  def your_method
  end
end

With the following code I can search for each method in the object space:

type = Class
name = /^my_method$/
result = ObjectSpace.each_object(type).select do |o|
  o.instance_methods(false).grep(name).size > 0 || o.methods.grep(name).size > 0
end
p result

And it finds it showing the following output:

[MyClass]

As the searcher code also searches for instance methods, it shows the same output when looking for your_method.

Even with if I add a singleton method to an object:

mc = MyClass.new
def mc.our_method
end

Just changing the searcher like this:

type = Object
name = /^our_method$/
result = ObjectSpace.each_object(type).select do |o|
  o.methods.grep(name).size > 0
end
p result

It also finds it:

[#<MyClass:0x8f86760>]

The question is, how do I find a method defined in the top level object? This method:

def hidden
end

Besides, which is the current class when defining a method like this?

Upvotes: 4

Views: 81

Answers (2)

Cary Swoveland
Cary Swoveland

Reputation: 110725

If you had this:

def my_method() end

class A
  def self.my_method() end
end

class B < A
  def my_method() end
end

class C
  def my_method() end
end

and wanted to find methods named 'my_method' that you've created, you could do this:

ObjectSpace.each_object(Class).select do |o|
  o.instance_methods(false).include?(:my_method)
end
  #=> [C, B]

ObjectSpace.each_object(Class).select do |o|
  o.methods(false).include?(:my_method)
end
  #=> [A]

ObjectSpace.each_object(Class).select do |o|
  o.private_instance_methods(false).include?(:my_method)
end
  #=> [Object]

Upvotes: 0

Andrew Marshall
Andrew Marshall

Reputation: 96994

Which is the current class when defining a method like this?

We can easily figure out what object we’re in by inspecting self in this top level scope:

self        #=> main
self.class  #=> Object

So we’re not in a Class, but an instance of Object which is dubbed “main”.


How do I find a method defined in the top level object?

This is where it gets interesting. The top-level scope object in Ruby behaves specially, but it’s relatively easy to discover where a method here defined lives:

def foo; :bar; end
method(:foo).owner     #=> Object
Object.new.foo         #=> NoMethodError: private method `foo' called
Object.new.send(:foo)  #=> :bar

So methods defined at the top-level are made (private*) instance methods of Object. The reason your ”searcher” cannot find it is because these methods are private, and neither methods nor instance_methods include private methods, instead you need private_methods and private_instance_methods:

Object.instance_methods.include?(:foo)          #=> false
Object.private_instance_methods.include?(:foo)  #=> true

* Note that Pry (at least v0.10.1) alters this to make methods defined at top-level in its REPL public.

Upvotes: 5

Related Questions