Ruby does not have any entry point to the code it is executing. Compare that to other languages like C, C++, Java, etc., which has a main()
method in some way. And this main()
is the entry point to the code. But in Ruby, we can open a file with a .rb
extension and write our code at the top-level like:
foo = 'Great Scott!'
puts foo # => Great Scott!
... and Ruby will execute the file for us. You'll get introduced to this top-level scope as global scope throughout most tutorials. Even though the intention is correct, the terminology as global scope may be slightly confusing. I feel that it's better if we just stick to calling it the top-level instead of the global scope. Here's why.
When you write any code in the top-level, Ruby will quietly wrap the Object
class around it. That is:
# ... some code ...
... loosely becomes:
class Object
# ... some code ...
end
This means that whatever you do in the top-level, you're essentially just monkey-patching the Object
class. The historical/philosophical reason behind this decision might be that Matz wanted everything to be an object in Ruby. Which is why you can see that even things like classes, modules, functions, etc., are all objects. And so it would make sense that any code lying at the top-level should also belong to an object in some way, and thus the Object
class.
There are implications to this decision. Continue reading the below mentioned concepts affected due to this decision to connect the dots.
Method definition
When you define methods in the top-level:
def foo
:foo
end
... then they will be available on every object:
42.foo # => :foo
3.14.foo # => :foo
'Ka-Chow!'.foo # => :foo
true.foo # => :foo
[].foo # => :foo
class FooBar; end
FooBar.new.foo # => :foo
The reason is because when we defined the foo
method in the top-level, the effect was of monkey-patching the Object
class. And since every other class in Ruby is a descendant of the Object
class, it was available on every object.
Module inclusion
When you have a module:
module Foo
def bar
:bar
end
end
... and you include
it in the top-level:
include Foo
... then the methods defined inside of the module will be available on every object:
42.bar # => :bar
3.14.bar # => :bar
'SHAZAM!'.bar # => :bar
true.bar # => :bar
[].bar # => :bar
class FooBar; end
FooBar.new.bar # => :bar
This is also because of the same reason that when we include
-ed the Foo
module in the top-level, the effect was of include
-ing it inside of the Object
class. This allowed every object to have access to the bar
method defined inside of the Foo
module.
Constant definition
When you define any sort of constants in the top-level:
FOO = 42
class Bar; end
module Baz; end
... they get scoped by the Object
class:
FOO == Object::FOO # => true
Bar == Object::Bar # => true
Baz == Object::Baz # => true
This is again because of the same reason that defining constants in the top-level is the same thing as re-opening the Object
class and defining those inside of it since Ruby will quietly wrap everything in the top-level by the Object
class.
Ruby is weird, and that's why we love it!