Module methods in Ruby
Here is my code:
module Star
def Star.line
puts '*' * 20
end
end
module Dollar
def Star.line
puts '$' * 20
end
end
module At
def line
puts '@' * 20
end
end
include At
Dollar::line # => "@@@@@@@@@@@@@@@@@@@@"
Star::line # => "$$$$$$$$$$$$$$$$$$$$"
Dollar::line # => "@@@@@@@@@@@@@@@@@@@@"
line # => "@@@@@@@@@@@@@@@@@@@@"
Can someone explain how I get this result? I don't understand the flow of looking for a method here.
source to share
This is how I see it:
Dollar::line
This module does not have a method to call At::line
because you included this module.
Star::line
It uses the latest definition from the module Dollar
(it comes after the original definition Star
, so it is overridden).
Dollar::line
The third call is the same as the first.
line
And the last one is At::line
because you did include.
source to share
First, you need to understand that Ruby looks for constants that are somewhat similar to methods. It starts by looking for a constant in the current lexical scope. If he doesn't find a constant, it goes up one level and looks there, etc. If it can't find the constant elsewhere, it ends up looking for the top level, so you can access modules like Kernel
, from anywhere in your code.
module Star
end
Star.object_id # 20
module Dollar
Star.object_id # 20. No Star in current scope, so gets the top-level star
end
module At
module Star
end
Star.object_id # 10. There is now a Star in this scope, so we don't get the top-level one
end
The next thing to understand is that methods defined at the top level in Ruby are instantiated methods Object
. Since everything in Ruby is an instance Object
, such methods can always be called.
Finally, let's look at what it does include
: it takes instance methods from the module and makes them instances in the current scope. So, if you are include
something at the top level, all these methods are added to Object
!
So your code is essentially equivalent to this:
module Star
def self.line
puts '*' * 20
end
# this overwrites the previous definition
def self.line
puts '$' * 20
end
end
# because of the way constants are looked up, the def ends up in Star
module Dollar
end
module At
def line
puts '@' * 20
end
end
# the include does this, so now every object (including Dollar) can call line
def line
puts '@' * 20
end
# except Star already has its own line method, so the one from Object won't be called for it
Star.line # "$$$$$$$$$$$$$$$$$$$$"
source to share