Reputation: 433
I am really confused why the methods in modules B and C are mixed in to the class in module A when I use include C
. Does include somehow recursively mix in with all classes below the namespace where it is called?
module A
class AClass
def a_method_
end
end
end
module B
extend self
def b_method_
end
end
module C
extend self
include B
def c_method_
puts A::AClass.new.methods.grep /_method_/
end
end
C.c_method_
puts "----"
include C
c_method_
With the result that before the include, the AClass instance correctly only has method a_method_, but after the include it has access to the other two methods as well.
a_method_
----
a_method_
c_method_
b_method_
Can someone please help explain this?
Upvotes: 1
Views: 73
Reputation: 22325
You might find this illuminating:
puts Object.instance_methods.grep /_method_/
# c_method_
# b_method_
Your methods have been added to everything, not just AClass
! And to really demonstrate the weirdness, try changing that last bit to:
class D
include C
puts Object.instance_methods.grep /_method_/
end
No more output! You've stumbled upon one of the weird, intentional behaviors of Ruby's main
(the implicit receiver when nothing else is specified). The basic motivation is that if you define a method in main, you want to be able to call it anywhere:
def foo
end
# now all of these work:
class A
foo
end
module B
foo
end
A.new.instance_eval { foo }
A.singleton_class.class_eval { foo }
This isn't about scope resolution either; if you change that to foo = :bar
you'll get NameError
s. This is for stuff like puts
and gets
where you generally don't care who the receiver is when you call them. But this behavior doesn't jive at all with Ruby's object model, so matz hacked it in: every method defined in main
gets added to Object
as an instance method. Since everything is an object, the rest works as expected.
Upvotes: 1
Reputation: 434665
If you change the tail end of your code to this:
C.c_method_
puts self.inspect
puts "----"
include C
c_method_
you should help you see what's going on. That will give you this output:
a_method_
main
----
a_method_
c_method_
b_method_
So what is this main
business? Chuck has a nice summary over in this answer:
Everything in Ruby occurs in the context of some object. The object at the top level is called "main". It's basically an instance of Object with the special property that any methods defined there are added as instance methods of Object (so they're available everywhere).
That means that saying this at the top level:
include C
is the same as saying:
Object.send(:include, C)
Adding things to Object
pollutes everything.
Upvotes: 1