Where Are Methods Defined at the Ruby Top Level

Where are methods defined at the ruby top level?

First of all, this behavior and the underlying reasoning have always existed; it's nothing new to 1.9. The technical reason it happens is because main is special and treated differently than any other object. There's no fancy explanation available: it behaves that way because it was designed that way.

Okay, but why? What's the reasoning for main to be magical? Because Ruby's designer Yukihiro Matsumoto thinks it makes the language better to have this behavior:

Is so, why are top-level methods not made singleton-methods on this object,
instead of being pulled in as instance methods on the Object class
itself
(and hence into all other classes i.e. more namespace pollution than is
usually intended). This would still allow top-level methods to call other
top-level methods. And if the top-level object were referred to by
some
constant like Main, then those methods could be called from anywhere
with
Main.method(...).


Do you really wish to type
"Main.print" everywhere?

Further on in the discussion, he explains that it behaves this way because he feels the "assumption is natural."

EDIT:

In response to your comment, your question is aimed at why main's eigenclass seems to report hello as a private instance method. The catch is that none of the top-level functions are actually added to main, but directly to Object. When working with eigenclasses, the instance_methods family of functions always behave as if the eigenclass is still the original class. That is, methods defined in the class are treated as being defined directly in the eigenclass. For example:

class Object
private
def foo
"foo"
end
end

self.send :foo # => "foo"
Object.private_instance_methods(false).include? :foo # => true
self.meta.private_instance_methods(false).include? :foo # => true

class Bar
private
def bar
"bar"
end
end

bar = Bar.new
bar.send :bar # => "bar"
Bar.private_instance_methods(false).include? :bar # => true
bar.meta.private_instance_methods(false).include? :bar # => true

We can add a method directly to main's eigenclass, though. Compare your original example with this:

def self.hello; "hello world"; end

Object.instance_methods.include? :hello # => false
self.meta.instance_methods.include? :hello # => true

Okay, but what if we really want to know that a given function is defined on the eigenclass, not the original class?

def foo; "foo"; end  #Remember, this defines it in Object, not on main
def self.bar; "bar"; end #This is defined on main, not Object

foo # => "foo"
bar # => "bar"

self.singleton_methods.include? :foo # => false
self.singleton_methods.include? :bar # => true

Where do methods defined at the top-level exist?

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.

What are the rules for `self` in a top-level method?

All methods defined in the context of main object, will become [private] methods on Object. And since everything is an Object, this method will be available everywhere. And this is also why self is changing based on where you call it from: those are all different objects, but they also all inherit this method.

private_methods.include?(:who_am_i) # => true
foo # => "main"
A.private_methods.include?(:who_am_i) # => true
A.foo # => "A"
A.new.private_methods.include?(:who_am_i) # => true
A.new.foo # => "#<A:0x00007feaad034e00>"

To which object are top-level methods assigned in Ruby?

There is top-level object in Ruby -- main

def method_name(args)
# body
end
self
# => main

self.methods.grep(/method_name/)
# => [:method_name]

main is an instance of Object. Any methods defined in main become instance methods of Object

Object.private_instance_methods.grep(/method_name/)
# => [:method_name]

This makes them available everywhere (because all classes are descendants of Object), meaning that we can call the method without a receiver inside classes

For example

def foo
puts "foo"
end

class X
def y
foo
end
end

# will print foo
X.new.y

# will raise private method `foo' called for #<X:0x000055e406a0f060> (NoMethodError)
# to reproduce don't use irb, just usual file
X.new.foo

Read more

Why method created at top-level is mentioned in Object.private_methods?

But why zzz method name is returned in array when call Object.private_methods?

self at the toplevel is an object called 'main', and 'main' is an instance of class Object:

p self
p self.class

--output:--
main
Object

The private_instance_methods() of a class, e.g. Object, are the private_methods() of the instances of the class, e.g. 'main'.

And why Object and self return different instance methods names?

That can't be because self/main is not a Class, and therefore self/main does not respond to instance_methods():

p self.instance_methods

--output:--
1.rb:3:in `<main>': undefined method `instance_methods' for main:Object (NoMethodError)

As for this:

p Object.private_methods(false)

--output:--
[:inherited, :initialize]

Object, like all classes, is an instance of Class, so the private_instance_methods() of Class:

p Class.private_instance_methods(false)

--output:--
[:inherited, :initialize]

are the private_methods() of the instances of Class, e.g. Object:

p Object.private_methods(false)

--output:--
[:inherited, :initialize]

And this:

Temp.private_methods() # [:inherited, :initialize, :included, :extended,...., :zzz,..., :singleton_method_added, :singleton_method_removed, :singleton_method_undefined, :method_missing]  
#`zzz` is in list, but there is no `:hello`

zzz() is a method defined in Object, which means that ALL objects inherit that method. Temp is a class, but Temp is also an object(it is an instance of Class):

puts Temp.class

--output:--
Class

So the Temp object inherits the method zzz() from Object. In fact, ALL objects can call zzz() because all objects inherit from Object, but because zzz is a private instance method in Object, you cannot specify a receiver:

def zzz
puts 'zzz'
end

class Temp
puts self #=>Temp
zzz #When you don't specify a receiver for a method call
#ruby uses self, so that line is equivalent to
#self.zzz, which is equivalent to Temp.zzz

def hello
puts 'Hello!'

puts self #=>Temp_instance
zzz #Same as self.zz, which is equivalent to Temp_instance.zz
end
end

--output:--
Temp
zzz
Hello!
#<Temp:0x00000101136238>
zzz

The method hello() can only be called by instances of the class Temp, so Temp can call zzz(), but Temp cannot call hello():

class Temp
def hello
puts 'Hello!'
end
end

Temp.new.hello
Temp.hello

--output:--
Hello!
1.rb:8:in `<main>': undefined method `hello' for Temp:Class (NoMethodError)

How to list all the methods defined in top self Object in Ruby?

The closest I could find is to call private_methods on the main object, with false as argument

Returns the list of private methods accessible to obj. If the all
parameter is set to false, only those methods in the receiver will be
listed.

def foo
"foo"
end

def bar
"bar"
end

def baz
"baz"
end

p private_methods(false)
# [:include, :using, :public, :private, :define_method, :DelegateClass, :foo, :bar, :baz]

If you omit the argument, you also get all the private methods defined in Kernel or BasicObject.

In order to refine the list further, you could select the methods defined for Object:

p private_methods(false).select{|m| method(m).owner == Object}
#=> [:DelegateClass, :foo, :bar, :baz]

Only :DelegateClass is left, because it is defined in the top-level scope, just like :foo, :bar and :baz.

Why are global methods allowed to be defined outside of a class in Ruby?

When you define a function in Ruby at the global scope in this way, it technically becomes a private method of the Object class, which is the base class that everything inherits from in Ruby. Everything in Ruby is an object, so it is indeed true that you have defined a method.

def say_goodnight(name)
result = "Goodnight, " + name
return result
end

Object.private_methods.include? :say_goodnight
=> true

Because it is defined as a method with private visibility on Object, it can only be called inside objects of the class on which it's defined or subclasses. So why does it appear to be available globally?

Basically, the Ruby program itself defines an instance of Object called main, which serves as the top-level scope where your method was defined. So if you think of your program as running inside main (which is an Object) its private methods are available for use.

# In irb:
self
=> main
self.class
=> Object
self.private_methods.include? :say_goodnight
=> true

Addendum:
This answer which further explains how main is defined and implemented.

Update for Ruby >= 2.3

Noted in the comment thread, later versions of Ruby would define the method Object#say_goodnight in this example with public visibility rather than private. This behavior appears to have changed between Ruby 2.2.x and 2.3.x, but does not affect method exposure.



Related Topics



Leave a reply



Submit