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
What Are the Differences Between Rbenv, Rvm, and Chruby
Rails: Hasmanythroughassociationnotfounderror
Error Installing Homebrew - Brew Command Not Found
Why Does Ruby Open-Uri's Open Return a Stringio in My Unit Test, But a Fileio in My Controller
How to Calculate the Offset, in Hours, of a Given Timezone from Utc in Ruby
Generate and Publish Ruby Based Rest APIs Documentation
Ruby Mixins: Extend and Include
How to Select Option in Drop Down Using Capybara
Calling Method in Parent Class from Subclass Methods in Ruby
Is There a Way in Ruby/Rails to Execute Code That Is in a String
Puppet/Facter "Could Not Retrieve Fact Fqdn": How to Fix or Circumvent
Adding Attributes to a Ruby Object Dynamically
Error Loading Rubygems Plugin ,Openssl.Bundle (Loaderror)
Rspec Allow/Expect VS Just Expect/And_Return